← Retour aux travaux
2025 · Plateforme paroissiale · Odoo 19 + Vue + Flask + Nuxt + Expo

English Chapel Antananarivo

Une plateforme à cinq surfaces que nous avons bâtie pour notre propre paroisse — un board administratif Odoo 19 avec la localisation comptable malgache qu'il a fallu écrire pour le faire tourner, un site Vue 3 public, un cockpit Flask + Nuxt 3 pour l'équipe de louange, et une application Expo pour la communauté. Cinq piles, une communauté, faites avec gratitude.

Rôle
Architecture · implémentation · QA — les cinq surfaces
Durée
Depuis 2025, en cours
Équipe
Nous trois
VISUEL DE PROJET · PLACEHOLDER
FIG. 01
Contexte

L'English Chapel Antananarivo est notre paroisse — l'église anglophone où nous trois allons le dimanche. C'est aussi, discrètement, la chose la plus complète que l'atelier a livrée cette année : un board Odoo 19 qui tient l'administration, un site Vue 3 qui présente la chapelle, un cockpit Nuxt et une API Flask qui planifient les services et consolident les slides, et une application Expo qui met les annonces et les paroles dans la poche de chacun. Cinq surfaces. Une communauté. La même famille les a écrites ; la même famille s'en sert.

Il a fallu écrire une localisation comptable malgache avant de pouvoir livrer le board. Odoo 19 n'avait pas de l10n_mg en base, donc nous en avons écrit un — le PCG, les taxes (TVA, IRSA, retenues à la source, TVA à l'importation), les positions fiscales, un modèle d'exercice fiscal au niveau société, et un post-init hook qui active automatiquement l'ensemble pour toute société dont le pays est Madagascar. l10n_mg_report ajoute les documents statutaires — Bilan, Compte de Résultat, Tableau des Flux de Trésorerie (direct et indirect), Variation des Capitaux Propres — en PDF (QWeb) et en XLSX (via le moteur OCA report_xlsx), avec un cron qui rafraîchit les valeurs en cache. l10n_mg_tax_form livre le Bordereau de Versement de l'Impôt Synthétique (HETRA TAMBATRA), rempli automatiquement depuis le Bilan. La chapelle en avait besoin. Toute autre entité enregistrée à Madagascar en aura besoin aussi.

Les parties intéressantes vivent là où les surfaces se rencontrent. eca_offering enregistre chaque don sur l'une des cinq méthodes de paiement malgaches — Espèces, Virement, Mvola, Airtel Money, Orange Money — et poste une écriture comptable qui débite le compte correspondant (531100 / 512100 / 530002 / 530003 / 530004) et crédite 756100, Libéralités perçues. Les références suivent OFF/YYYY/NNNN depuis une `ir.sequence`. Une fois postée, l'offrande est verrouillée en écriture sauf pour les notes et l'état — annuler produit une contrepartie, pas une suppression discrète. Le trio mobile money, c'est la moitié des habitudes de don du pays qu'une localisation générique aurait manquée.

L'application mobile et le cockpit louange ferment la boucle. Le module annonces expose un contrôleur JSON-RPC en bearer-auth — parce que le `/web/dataset/call_kw` standard d'Odoo 19 est session-only et refuse les API keys d'office — branché sur un utilisateur `ECA Bot` dont le login est `bot@english-chapel-antananarivo.org` et dont la clé API est portée par l'app mobile. L'API Flask louange détient les chants et, pour un service donné, ouvre chaque PowerPoint, copie les slides dans un deck consolidé — layouts, fonds, textes placeholder, tailles de police, alignements — et renvoie une URL au cockpit Nuxt. Le dimanche matin, l'écran diaporama de l'app mobile projette le deck obtenu. Rien n'exige que quelqu'un soit dans la même pièce qu'un ordinateur portable.

Périmètre

Ce que nous avons bâti.

eca01

Module Odoo 19 racine : menu de tête, groupes de sécurité, flux de demande d'accès pour les nouveaux membres du board.

eca_offering02

Gestion des offrandes — cinq méthodes de paiement malgaches, immuable une fois postée, écriture comptable automatique sur 756100 avec références OFF/YYYY/NNNN.

eca_announcement03

Tableau d'annonces avec un contrôleur REST bearer-auth dédié à l'app mobile. Appuyé sur un utilisateur `ECA Bot` dont la clé API garde les appels entrants.

eca_pv04

Procès-verbaux (CA, AG, AGE) — contenu rich-text suivi via mail.thread.

l10n_mg05

Localisation comptable Madagascar : PCG, taxes, positions fiscales, modèle d'exercice. Auto-activée via `_l10n_mg_post_init_hook` pour toute société basée à Madagascar.

l10n_mg_report06

Rapports statutaires — Bilan, CDR, TFT direct / indirect, Variation Capitaux Propres — en PDF (QWeb) et XLSX (OCA report_xlsx), rafraîchis par cron.

l10n_mg_tax_form07

Impôt Synthétique (HETRA TAMBATRA) — Bordereau de Versement rempli automatiquement depuis le Bilan, avec champs NIF / STAT sur res.company.

eca_website08

SPA Vue 3 + Vite — site public de la chapelle. Accueil, Communauté, Leadership, App mobile, À propos, Confidentialité. Bilingue EN / FR via vue-i18n ; store Pinia ; composant ChapelMap pour l'itinéraire.

worship_team_backend09

API REST Flask + SQLAlchemy — chants, services, utilisateurs, uploads. Auth JWT (HS256). Génère les PowerPoint consolidés via python-pptx. Factory de stockage ; distribution d'APK par S3 présigné.

worship_team_frontend10

Cockpit Nuxt 3 pour l'équipe louange — services, chants, utilisateurs. Store Pinia ; bilingue ; composition de service par drag-and-drop (vuedraggable).

eca_companion11

App mobile Expo / React Native — annonces, services, chants, écran diaporama live pour les dimanches. Parle à la fois à l'API Flask louange et au contrôleur annonces Odoo.

infra (Terraform)12

Ressources AWS derrière la plateforme — buckets S3 pour la distribution d'APK, deux utilisateurs IAM (reader + publisher) — une clé reader qui fuit ne donne pas l'écriture.

Approche

À quoi ressemble le travail, en 4 pièces.

01

La comptabilité malgache, de bout en bout

Odoo 19 n'a pas livré de localisation Madagascar, donc nous l'avons écrite. PCG, taxes (TVA, IRSA, retenues, TVA à l'importation), modèle d'exercice au niveau société, rapports statutaires (Bilan, CDR, TFT, Variation Capitaux Propres) en PDF et XLSX, Impôt Synthétique rempli depuis le Bilan. Auto-activation via `_l10n_mg_post_init_hook` pour toute société avec pays = MG. La chapelle en avait besoin ; toute autre entité enregistrée à Madagascar aussi.

02

Mobile money là où le pays paie vraiment

eca_offering enregistre chaque don sur l'une des cinq méthodes — Espèces, Virement, Mvola, Airtel Money, Orange Money — et poste une écriture qui débite le compte correspondant et crédite 756100, Libéralités perçues. Les offrandes postées sont verrouillées en écriture sauf notes et état. L'annulation produit une contrepartie, pas une suppression silencieuse. Le trio mobile money, c'est la moitié des habitudes de don du pays qu'une localisation générique aurait manquée.

03

Cinq surfaces, une communauté

Le board Odoo, c'est où la chapelle vit administrativement. Le site la présente aux visiteurs. Le cockpit louange planifie les services. L'API Flask détient les chants et consolide les slides du dimanche. L'app mobile lit les annonces et affiche le diaporama. Les formes diffèrent ; les coutures sont choisies. Le contrôleur annonces parle REST en bearer-auth parce que le call_kw d'Odoo 19 est session-only et refuse les API keys — donc nous avons bâti le contrat dont l'app mobile a vraiment besoin.

04

Pour notre propre monde, à notre rythme

ECA est notre paroisse. Pas de deadline client, pas de scope creep commercial — juste le travail dont la chapelle a vraiment besoin, livré au rythme qu'elle peut absorber. La plateforme existe parce que trois ingénieurs vont à la même église et aiment l'idée d'y être utiles. Les standards sont ceux de l'atelier, appliqués sans négociation.

Choix techniques

Les solutions dont nous sommes le plus fiers.

01

Hook d'auto-activation l10n_mg

`_l10n_mg_post_init_hook` tourne à l'installation et parcourt chaque `res.company` dont `country_id` est Madagascar, chargeant le plan de comptes, les taxes, les positions fiscales et les comptes par défaut. Une société MG créée plus tard hérite de la localisation via le même hook, sans étape manuelle. Les rapports statutaires ont un cron quotidien (`data/ir_cron.xml`) qui rafraîchit les valeurs en cache pour Bilan, CDR et TFT.

02

Mapping offrande → écriture comptable

eca_offering définit une table `_DEBIT_ACCOUNT_CODES` — `{cash: 531100, bank: 512100, mvola: 530002, airtel: 530003, orange: 530004}` — et à la confirmation poste un `account.move` qui débite le compte cash / banque / mobile money correspondant et crédite 756100 (Libéralités perçues). La référence suit `OFF/YYYY/NNNN` depuis une `ir.sequence`. `write()` est surchargé pour refuser autre chose que notes et état une fois l'offrande postée ; l'annulation passe par un `unlink()` prudent.

03

REST bearer-auth pour l'app mobile

Le `/web/dataset/call_kw` standard d'Odoo 19 utilise `auth='user'` et refuse les API keys d'office. `eca_announcement.controllers.AnnouncementController` livre donc des endpoints `@http.route(..., auth='bearer', methods=['POST'], csrf=False)` — `/api/announcements` et `/api/announcements/<id>` — branchés sur un utilisateur `ECA Bot`. `./eca/manage.sh apikey` génère la clé sans passer par l'UI Odoo. L'app mobile la porte dans un header Bearer ; rien d'autre n'a besoin de connaître son existence.

04

Consolidation PPT multi-présentations

`Service.generate_consolidated_ppt()` ouvre chaque PowerPoint de chant avec python-pptx et copie les slides dans un deck neuf — layouts choisis (`title and body` pour le contenu, `blank` sinon), fonds de slide (pour que le fond noir d'un chant voyage), textes des placeholders par index, text-boxes flottants avec word-wrap et vertical-anchor préservés, tailles et couleurs de police et bold, alignements. Les chants sans PPT stocké sont générés depuis les paroles à la demande. Le fichier de sortie s'appelle `service_YYYYMMDD_<hash>.pptx` et l'ancien consolidé est supprimé à la régénération.

05

Deux utilisateurs IAM pour l'APK

L'app mobile est livrée sous forme d'APK dans S3. Le backend Flask signe des URLs GetObject courtes pour les utilisateurs (`APK_URL_TTL` par défaut à 3600s) avec un utilisateur `worship-releases-reader`. Le script de release (`./scripts/release-apk.sh`) uploade les nouveaux builds avec un utilisateur séparé `worship-releases-publisher`. Une clé reader qui fuit ne peut pas pousser un APK malveillant — elle ne peut lire que ce qui est déjà là.

06

Interface bilingue sur chaque surface

Le site, le cockpit et l'app mobile portent tous une paire EN / FR. Le site utilise vue-i18n avec `tm` + `rt` pour les tableaux traduits ; le cockpit utilise Nuxt + @nuxt/i18n ; l'app mobile utilise i18n-js. Les chaînes vivent à un seul endroit par app, jamais en dur dans les composants. La chapelle est bilingue ; la plateforme refuse de l'être moins.

07

Rapports statutaires en PDF et XLSX

Chaque rapport Madagascar (Bilan, CDR, TFT direct / indirect, Variation Capitaux Propres) est implémenté deux fois — une en template PDF QWeb, une en classe XLSX qui étend l'`AbstractReportXlsx` du module OCA `report_xlsx`. Les deux vues partagent le même payload calculé ; quel que soit le format que le comptable préfère, les chiffres correspondent.

Résultats

Quelques chiffres, en formes brutes.

5
Surfaces — Odoo, site, API, cockpit, mobile
8
Modules Odoo personnalisés, l10n_mg incluse
5
Méthodes de paiement — dont Mvola / Airtel / Orange
Pour nous
Fait avec gratitude à Antananarivo

Stack
Odoo 19PythonFlaskPostgreSQLVue 3Nuxt 3ExpoReact Nativepython-pptxAWS S3TerraformDocker

Un projet qui mérite ce niveau de soin ?

Démarrer une conversation