# 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`](../../../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 6 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 frontend > `renderHypotheque` ré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`](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`). | ```python 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-conseiller` pour la validation WAG, etc.) sont résolus depuis [`rbac_50_roles.json`](../../rbac/rbac_50_roles.json) via `finlib/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`](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 ```bash 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 de `gate_status_example.json`) - `POST /api/hypotheque/dossier/{id}/submit` · **403 + raisons** tant que le gate n'est pas vert - `GET /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 `renderHypotheque` côté VPS.