Défaut réel/reproductible (pas stale, byte-repro vert) : finlib/builder.py:89
faisait [roles_resolved[k] for k in sorted(roles_resolved)], itérant les 4 SLOTS
logiques de spec["roles"] — 2 slots (conseiller_wag ET validation_dossier) pointent
le même rôle ventes-conseiller → 4 entrées pour 3 rôles distincts. Preuve bug (pas
per-slot voulu) : la sortie drope la clé de slot (cle), donc le doublon ne porte
aucune info distinctive ; sémantique documentée README.md:54 « rôles RBAC utilisés »
+ ci/README.md:1100 « ensemble distinct » ; les 3 modules frères produisent tous un
ensemble distinct (confotur 3/3, commissions 4/4, workflow_vente 7/7) — financement
seul outlier 4/3.
Fix (aligné idiome commissions) : ensemble distinct trié par role_id via
sorted({rr["role_id"] for rr in roles_resolved.values()}). Artefact régénéré → 3/3
distinct. 0 gate ajouté (#5 — champ lu par AUCUN gate financement ni consommateur
externe ; convention distinctness déjà tenue par les frères). 0 chiffre inventé (#6).
run_ci 33 PASS · 0 FAIL · 0 SKIP (byte-repro vert avec nouvel artefact, 35 tests verts).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Financement Bancaire · parcours hypothécaire immobilier RD
Sprint 4 · CRM natif ERPNext · roadmap L52 (workflow vente end-to-end · volet
financement). Matérialise la
DIRECTIVE_FINANCEMENT_BANCAIRE_COMPLET_20260803.md
(Michel, 2026-08-03) : un module complet de financement bancaire pour un vrai
parcours hypothécaire dominicain, à la place du module « trop simpliste »
antérieur. Livrable Phase 1 (P0 · MVP) : le contrat de données + le cœur
métier du gate check + la structure des 7 sections + la bannière critique.
Ce worker n'écrit jamais sur le VPS (contrainte #8). Il produit le contrat et les artefacts en-repo ; le backend
/api/hypotheque/*et le frontendrenderHypothequeréels sont appliqués côté serveur (agents ERPNext Backend / Frontend Console). La Phase 2 (vrais formulaires PDF des 6 banques) attend les démarches relationship-manager de Michel — hors périmètre worker.
Le cœur du livrable · le gate check 4 conditions
L'exigence non-négociable de Michel : aucun document n'est transmis à la banque
tant que les 4 conditions ne sont pas remplies. La logique vit dans
finlib/gate.py — fonctions pures, testées, sans I/O :
| # | Condition (key) |
Règle vérifiée |
|---|---|---|
| 1 | apport_initial_complet |
Σ paiements ≥ prix × taux — 20 % résident RD · 30 % étranger (amendement Michel · aussi exigence des banques RD · Ley 189-11). |
| 2 | documents_exiges |
Tous les documents is_required de la banque choisie au statut « Validé WAG » ou « Envoyé banque ». |
| 3 | autorisations_signees |
Toutes les autorisations signées (signature_date non nulle). |
| 4 | validation_wag |
Un conseiller WAG référent a validé (wag_validated_by). |
from finlib import gate
cfg = gate.GateConfig.from_spec(spec)
ok, reasons = gate.can_submit_dossier(dossier, cfg) # (False, ["Apport initial incomplet : …", …])
status = gate.gate_status(dossier, cfg) # forme de l'endpoint gate-status (barres 0-100 %)
Si une condition manque, can_submit est False et reasons liste chaque
blocage (ordre canonique). Le backend POST /…/submit doit renvoyer 403 + ces
raisons ; le frontend grise le bouton « Envoyer à la banque » et affiche une
barre de progression par condition. Tant que l'apport initial est incomplet,
toutes les sections aval sont grisées (section apport_initial = seule
section gating, en position 1).
Ce qui est généré (out/, commité — hand-off direct)
| Fichier | Contenu |
|---|---|
banques.json |
Profils des banques partenaires (type, taux/LTV/durée, spécialités, devises). |
sections.json |
Structure accordéon (apport → info achat → choix banque → formulaires → exigences → autorisations → envoi). |
documents.json |
Catalogue des documents exigés par catégorie (client / immobilier / étrangers). |
autorisations.json |
Autorisations à signer (OTO Sign) + base légale. |
gate_spec.json |
Les 4 conditions + bannière critique + endpoints + rôle validation résolu + workflow suivi banque. |
gate_status_example.json |
Exemple calculé (dossier P07 volontairement bloqué) — auditable, jamais fabriqué. |
MANIFEST.json |
Traçabilité (sources, comptes dérivés, rôles RBAC utilisés). |
Comptes courants (source de vérité = MANIFEST.json) : 6 banques,
7 sections, 15 documents (requis 9 résident · 13 étranger),
4 autorisations, 4 conditions de gate.
Anti-invention (#6)
- Aucun taux d'apport codé en dur : 20/30 viennent du contrat
(
apport.residence_types), sourcés de la DIRECTIVE. Changer le contrat change le gate, sans toucher au code. - Aucun taux/LTV bancaire fabriqué : les fourchettes non données par la
DIRECTIVE restent
null(a_confirmer· démarche relationship manager). Seules les valeurs que la DIRECTIVE énonce (ex. Scotiabank LTV 70 %, BHD León 30 ans) sont encodées. - Aucun nom de rôle Frappe en dur : les rôles (
ventes-conseillerpour la validation WAG, etc.) sont résolus depuisrbac_50_roles.jsonviafinlib/rbac.py(source unique · zéro duplication · #5).
Validation · schéma + 12 invariants
validate re-génère le bundle en mémoire et vérifie le schéma de sortie
(financement.schema.json) plus 12 invariants
métier (unicité des ids ; 0 < résident < étranger ≤ 100 ; fourchettes banque
min ≤ max ; rôles résolus dans RBAC ; sections contiguës + section gating en
tête ; gate = 4 conditions alignées sur la bannière ; documents étrangers
strictement additionnels ; statuts recevables ⊆ statuts ; exemple de gate
recalculé cohérent ; comptes du manifeste cohérents). Un invariant qui casse
refuse le build (anti-régression).
Utilisation
cd 05_deliverables_mvp/crm/financement_bancaire
python3 financement_bancaire_gen.py validate # schéma + 12 invariants
python3 financement_bancaire_gen.py build # écrit les 7 artefacts out/
python3 -m unittest discover -s tests -v # suite (gate + générateur)
Sortie déterministe (tri stable, aucun horodatage) → l'artefact commité est
reproductible bit-à-bit et vérifié par ci/check_artifacts.sh.
Endpoints backend visés (Phase 1 · à implémenter côté serveur)
Contrat exposé par gate_spec.json :
GET /api/hypotheque/banques· profils banques (⇐banques.json)GET /api/hypotheque/dossier/{id}/gate-status· état des 4 conditions (forme degate_status_example.json)POST /api/hypotheque/dossier/{id}/submit· 403 + raisons tant que le gate n'est pas vertGET /api/hypotheque/dossier/{id}/journal· historique horodaté
Périmètre
- ✅ Phase 1 (ce module) : contrat, gate check, sections, bannière, exemple calculé.
- ⏳ Phase 2 (hors worker) : vrais formulaires PDF officiels des 6 banques
(démarche relationship-manager Michel) + implémentation runtime des endpoints /
du
renderHypothequecôté VPS.