# Financement Bancaire · parcours hypothécaire immobilier RD

**Sprint 5 · 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.
