d6cc09da0d
- Finding : ligne 3 du README « Sprint 5 · roadmap L52 (workflow vente end-to-end · volet financement) » = citation roadmap-GLOBALE (cite L52 + reprend le texte du deliverable S4), PAS un cadre agent-interne → tranchée fausse par 4 autorités concordantes : · fichier roadmap : ## SPRINT 4 = L48, deliverable L52 ; ## SPRINT 5 = L54 « ONAPI + Compliance + Mobile » (rien à voir) → « Sprint 5 · roadmap L52 » auto-contradictoire · autorité byte-gatée acceptance_spec.json (partition INV5) : crm/financement_bancaire ∈ S4 · roadmap_line 52 · 3 modules frères du bucket S4 tous « Sprint 4 » (workflow_vente/dossier_vente/commissions · L51-52) → financement seul outlier · fiche 03_agents/crm/AGENT.md liste déjà « 4 (roadmap L52) » → le module contredisait sa propre fiche - Provenance surfacée non éditée : DIRECTIVE Michel :140 cadre « [Sprint 5 · Financement] » (son snapshot d'entrée) transcrit à tort dans un slot roadmap-global ; directive laissée INTACTE (input immuable, comme daily_reports) - Balayage exhaustif : exactement 2 surfaces manuscrites (README:3 + gen.py:4) ; aucun spec/artefact/out/gate ne porte de champ sprint → build ne régénère aucun artefact (check_artifacts vert) - 0 gate ajouté (#5 · label sprint = piège à 2 cadres légitimes, gate aveugle = faux positifs sur frames agent-internes) · 0 édition d'autorité (acceptance disait déjà S4) · 0 chiffre inventé (#6) · 0 commande VPS (#8) · CI 33 PASS Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
112 lines
6.1 KiB
Markdown
112 lines
6.1 KiB
Markdown
# 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.
|