Bug Prêts différés
This commit is contained in:
1 parent
26197fc411
commit
e8e3ca34cc
1 file changed
+46
-26
@@ -264,6 +264,10 @@ router.post('/fix-differe-dates', (req, res, next) => {
|
||||
WHERE i.type_remb = 'differe'
|
||||
AND i.date_souscription IS NOT NULL
|
||||
AND i.duree_mois IS NOT NULL
|
||||
-- Bug corrigé le 26/09/26 (dossier "Le Mitra") : un prêt déjà remboursé/clôturé n'a
|
||||
-- plus lieu d'avoir sa date d'échéance "corrigée" — cf. commentaire plus bas sur
|
||||
-- regenererEcheancier() pour le détail du bug que cette exclusion referme.
|
||||
AND i.statut NOT IN ('rembourse', 'cloture')
|
||||
`).all(req.user.id);
|
||||
|
||||
// Approximation jours/mois (moyenne julienne) — cohérent avec l'ancien seuil fixe
|
||||
@@ -327,20 +331,20 @@ router.post('/fix-differe-dates', (req, res, next) => {
|
||||
`écart > ${seuilMois} mois avec date_souscription + duree_mois`,
|
||||
});
|
||||
|
||||
// Régénère l'échéancier de projection avec la date corrigée — sans ça,
|
||||
// simul_remboursements reste calé sur l'ancienne date aberrante.
|
||||
generateSimul(db, {
|
||||
id: inv.id,
|
||||
montant_investi: inv.montant_investi,
|
||||
taux_interet: inv.taux_interet,
|
||||
duree_mois: inv.duree_mois,
|
||||
type_remb: inv.type_remb,
|
||||
freq_interets: inv.freq_interets,
|
||||
date_premiere_echeance: dateCalculee,
|
||||
date_debut_simul: inv.date_debut_simul,
|
||||
date_souscription: inv.date_souscription,
|
||||
echeance_fin_de_mois: inv.echeance_fin_de_mois ?? 0,
|
||||
});
|
||||
// Régénère l'échéancier de projection avec la date corrigée, PUIS réconcilie contre
|
||||
// les remboursements réels déjà enregistrés. Bug corrigé le 26/09/26 (dossier "Le
|
||||
// Mitra", statut déjà "Remboursé" depuis le 24/07/2025) : un generateSimul() direct
|
||||
// ignore tout remboursement déjà perçu et recrée toujours une échéance naïve à la
|
||||
// date corrigée — y compris pour un prêt intégralement soldé, dont la date corrigée
|
||||
// (date_souscription + duree_mois) tombait dans le futur. L'échéance orpheline
|
||||
// réapparaissait alors comme "à recevoir" dans le Dashboard et sur la fiche du prêt.
|
||||
// regenererEcheancier() applique le même correctif que POST/PUT /revisions (cf.
|
||||
// project_revision_correction) : pour un prêt différé (jamais de restructuration,
|
||||
// donc jamais de date_debut_simul), il rejoue l'historique des remboursements de
|
||||
// capital via replayAdjustSimulForActuals() après régénération, qui supprime toute
|
||||
// échéance future désormais caduque. Filet de sécurité en plus de l'exclusion des
|
||||
// prêts clôturés/remboursés ajoutée ci-dessus (WHERE i.statut NOT IN (...)).
|
||||
regenererEcheancier(inv.id);
|
||||
|
||||
corriges.push({
|
||||
id: inv.id,
|
||||
@@ -376,7 +380,7 @@ router.post('/fix-differe-dates', (req, res, next) => {
|
||||
router.post('/check-echeancier-differe', (req, res, next) => {
|
||||
try {
|
||||
const rows = db.prepare(`
|
||||
SELECT i.id, i.nom_projet, i.date_premiere_echeance, i.date_cible,
|
||||
SELECT i.id, i.nom_projet, i.statut, i.date_premiere_echeance, i.date_cible,
|
||||
i.montant_investi, i.taux_interet, i.duree_mois, i.type_remb, i.freq_interets,
|
||||
i.date_debut_simul, i.date_souscription, i.echeance_fin_de_mois
|
||||
FROM investissements i
|
||||
@@ -393,15 +397,23 @@ router.post('/check-echeancier-differe', (req, res, next) => {
|
||||
WHERE investissement_id = ?
|
||||
ORDER BY numero_echeance
|
||||
`);
|
||||
const hasReinvest = db.prepare('SELECT 1 FROM reinvestissements WHERE investissement_id = ? LIMIT 1');
|
||||
|
||||
const corriges = [];
|
||||
|
||||
for (const inv of rows) {
|
||||
const simulRows = getSimul.all(inv.id);
|
||||
const incoherent =
|
||||
simulRows.length !== 1 ||
|
||||
simulRows[0].date_prevue !== inv.date_premiere_echeance;
|
||||
|
||||
// Bug corrigé le 26/09/26 (dossier "Le Mitra") : un prêt déjà remboursé ou clôturé ne
|
||||
// doit conserver AUCUNE échéance de projection — même une échéance unique dont la date
|
||||
// correspond bien à date_premiere_echeance est une échéance ORPHELINE dans ce cas (le
|
||||
// prêt est soldé), pas un état cohérent. Sans ce garde-fou, cette route n'aurait jamais
|
||||
// détecté l'échéance laissée par le bug de "Corriger les dates des prêts différés"
|
||||
// (ci-dessus) sur un prêt déjà soldé, puisque cette échéance orpheline correspondait
|
||||
// justement (par construction du bug) à date_premiere_echeance.
|
||||
const estClos = inv.statut === 'rembourse' || inv.statut === 'cloture';
|
||||
const incoherent = estClos
|
||||
? simulRows.length > 0
|
||||
: (simulRows.length !== 1 || simulRows[0].date_prevue !== inv.date_premiere_echeance);
|
||||
|
||||
if (!incoherent) continue;
|
||||
|
||||
@@ -409,11 +421,19 @@ router.post('/check-echeancier-differe', (req, res, next) => {
|
||||
? 'aucune échéance'
|
||||
: simulRows.map(s => `${s.date_prevue} (n°${s.numero_echeance})`).join(', ');
|
||||
|
||||
if (hasReinvest.get(inv.id)) {
|
||||
generateSimulWithReinvestissements(db, inv.id);
|
||||
} else {
|
||||
generateSimul(db, inv);
|
||||
}
|
||||
// regenererEcheancier() régénère PUIS réconcilie contre les remboursements réels déjà
|
||||
// enregistrés (replayAdjustSimulForActuals(), cf. project_revision_correction.md) — pour
|
||||
// un prêt soldé, cela supprime l'échéance orpheline ; pour un prêt actif dont
|
||||
// l'échéancier était simplement désynchronisé, cela le reconstruit SANS écraser un
|
||||
// éventuel remboursement anticipé partiel déjà pris en compte, contrairement à l'ancien
|
||||
// appel direct à generateSimul()/generateSimulWithReinvestissements() ci-dessous, qui ne
|
||||
// réconciliait jamais rien.
|
||||
regenererEcheancier(inv.id);
|
||||
|
||||
const nouvelEcheancier = getSimul.all(inv.id);
|
||||
const nouvelleValeur = nouvelEcheancier.length === 0
|
||||
? 'aucune échéance (prêt soldé)'
|
||||
: nouvelEcheancier.map(s => `${s.date_prevue} (n°${s.numero_echeance})`).join(', ');
|
||||
|
||||
recordHistory(inv.id, {
|
||||
type_evenement: 'correction_auto_echeancier',
|
||||
@@ -421,7 +441,7 @@ router.post('/check-echeancier-differe', (req, res, next) => {
|
||||
champ: 'echeancier',
|
||||
label: 'Échéancier de projection',
|
||||
ancienne_valeur: ancienEcheancier,
|
||||
nouvelle_valeur: inv.date_premiere_echeance,
|
||||
nouvelle_valeur: nouvelleValeur,
|
||||
}],
|
||||
notes: "Correction automatique (Nettoyage > Vérifier la cohérence de l'échéancier des prêts différés)",
|
||||
});
|
||||
@@ -430,7 +450,7 @@ router.post('/check-echeancier-differe', (req, res, next) => {
|
||||
id: inv.id,
|
||||
nom_projet: inv.nom_projet,
|
||||
ancien_echeancier: ancienEcheancier,
|
||||
nouvelle_date: inv.date_premiere_echeance,
|
||||
nouvelle_date: nouvelleValeur,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user