Échéancier¶
Extrait du domain map API, qui était chargé dans chaque session agent. La rule garde l'index module → contexte ; le détail vit ici, à côté du code.
Un echeancier par bien (obligation/loan), configure apres funding success.
Logique de calcul :
- Interets calcules jour par jour (30 jours/mois comptable), le capital restant evolue a chaque evenement
- Mensualite = interets + remboursement capital
- Sequestre d'interets : montant pre-collecte deduit des mensualites. Peut reduire amountDue a zero (echeance sans paiement du)
- amountDue = max(0, mensualite + penalites - sequestre restant). remainingToBePaid = amountDue - deja paye
Evenements qui recomputent l'echeancier a partir de leur date (en preservant les echeances passees) :
- capitalLoaned : nouvelle tranche versee (augmente capital restant)
- capitalRepaymentPlanned / capitalRepaymentUnplanned : remboursement (diminue capital restant)
- prorogation : prolongation de duree (doit etre > duree actuelle)
- interestRateChange, vatRateChange, decreaseInterestSequestre
Subtilites :
- Prorogation contractuelle prevue (contractSpecifications.durationInMonths.prorogation = duree totale apres prorogation, strictement > nominale) mais pas encore appliquee a l'echeancier : simulation sans ecriture DB via GET /administration/espace-financement/project/:projectId/echeancier/prorogation-simulation (espace-financement-ext) et GET /administration/property/:projectId/payment-schedule/prorogation-simulation (BO) ; 400 prorogation-not-planned si absente, 400 prorogation-duration-should-be-greater-than-nominal-duration si prorogation <= nominal. Si deja appliquee, renvoie l'echeancier courant
- GET .../echeancier/start-and-end-date (espace-financement-ext) expose endDateWithProrogation : date du dernier capitalRepaymentPlanned apres simulation de prorogation (null si prorogation absente / simulation impossible)
- Penalites de retard : poussees par gestion-des-defauts (webhook POST /administration/gestion-defauts/echeancier/penalties), stockees en jsonb penalty {total, forInvestors, forBricks, discount?} sur l'echeance. Seule la part forInvestors entre dans amountDue ; forBricks n'est pas encaisse via l'echeancier — reclame en une fois au solde de tout compte (somme des forBricks sur le dernier virement). Echeance / echeancier introuvable : skip du projet (TX rollback) + Slack, le batch continue — un id fantome ne doit pas bloquer la synchro des autres projets (HTTP 201)
- Le meme webhook porte le statut projet (delay | default | null) : il ecrit financialStatus (etat courant, recalcule a la regularisation) et leve hasBeenInDelayOrDefaultOnce, jamais efface a la regularisation — c'est ce flag qui maintient l'acces au suivi detaille apres retour a la normale, financialStatus distinguant deja retard et defaut. Historique anterieur irrecuperable (financialStatus ecrase sur place) : backfill via export gestion-des-defauts (scripts/property/backfill-has-been-in-delay-or-default-once.script.ts)
- L'echeancier PDP (project-financing-request-echeancier) expose penalty (total + les deux parts) quand total > 0 — affiche dans l'Espace Financement avec le detail du calcul
- L'echeancier PDP expose mensualite (theorique) et amountDue (du post-sequestre), et derive en vue mensualiteStatus: 'coveredByInterestSequestre' (amountDue = 0 && mensualite > 0 && interestSequestreRemaining > 0) — jamais persiste, le noPayment en base reste surcharge. La jauge "echeances restantes" compte les couvertes dans le total mais pas dans le restant. Detection BO / notaire / PDF via deriveMensualiteDisplayStatus (@bricks-common/api-communication-bricksoffice, le contrat admin reste brut) ; la vue PDP derive le meme predicat sequestre cote serveur — changer les deux ensemble
- Edit BO du contract budget (totalFundEscrow/totalConstructionBudget, action admin property.edit-contract-budget) : sequestre d'interets gele des qu'un echeancier existe (pas de recompute, choix conservateur) ; sinon baisse interdite apres le premier virement PDP, hausse conditionnee au solde Lemonway. Budget travaux indicatif, toujours editable
- Remboursement anticipe (capitalRepaymentUnplanned) interdit si echeances passees impayees
- Le remboursement anticipe ne peut pas depasser le capital restant a rembourser
- Frais de gestion mensuels transferes separement des revenus investisseurs (SPV -> compte Bricks)
- Frais de gestion executes seulement si la mensualite n'est pas impayee (inclut noPayment quand le sequestre couvre tout)
- TVA sur frais de gestion depend du pays du SPV, et peut changer via evenement vatRateChange
- Cloture anticipee : les frais de gestion du dernier mois contractuel sont proratas au jour pres (pas factures pour le mois complet)
- Switch BO du paymentMode (prélèvement ↔ virement) : PUT .../echeancier/payment-mode. Le dayOfMonth est conservé. Passage virement → prélèvement : mandat SDD Lemonway obligatoire (liste via proxy GET /administration/spv/:spvId/sdd-mandates). paymentMode ne pilote que la programmation des SDD ; le cron echeancier-bank-debit-status-update synchro toute ligne qui a déjà un prélèvement Lemonway, y compris après un switch en wire.
- Sync prelevements SEPA (echeancier-bank-debit-status-update cron, quotidien) : mois recap (20 N → 8 N+1) + prelevements scheduled + rescan chargeback des prelevements executed sur businessRules.echeancier.bankDebitChargebackResyncWindowDays (meme echeance deja paid) — le webhook Lemonway chargeback ne met pas a jour l'echeancier SPV. Un refund sur echeance paid hors BANK_DEBIT_PAYOUT_HOLD_BUSINESS_DAYS (7j ouvrés français Paris, meme hold que le script 02a) poste une alerte dediee dans #suivi-projet-echeances (hors recap mensuel). Un refund encore dans le hold reste dans le recap quotidien.