diff --git a/backend/src/routes/investissements.js b/backend/src/routes/investissements.js index 8a7825a..67407f5 100644 --- a/backend/src/routes/investissements.js +++ b/backend/src/routes/investissements.js @@ -673,11 +673,25 @@ router.post('/:id/revisions', (req, res, next) => { // Recalcule duree_mois si la date cible change réellement, pour que l'échéancier régénéré // atteigne effectivement cette nouvelle date (cf. convention date_cible = date_premiere_echeance - // + (duree_mois - 1) mois, utilisée côté frontend pour tous les types de prêt). + // + (duree_mois - 1) mois, utilisée côté frontend pour les prêts à échéances multiples). + // + // Cas particulier "differe" (versement unique) : duree_mois y représente le nombre total de + // mois entre date_souscription et l'unique échéance (cf. buildSchedule(), qui l'utilise + // directement dans interets = montant * taux/100/12 * duree — même convention qu'à la + // création du prêt), PAS un décompte d'échéances depuis date_premiere_echeance. Réutiliser + // la formule "+1 depuis date_premiere_echeance" ici sous-évalue lourdement la durée réelle + // (et donc les intérêts) — bug signalé par Olivier le 22/08/26 sur "Le Troubadour" : une + // prolongation de 12%→17% jusqu'au 30/09/2026 donnait duree_mois=8 (mois écoulés entre + // date_premiere_echeance et la nouvelle date cible, +1) au lieu de 25 (mois écoulés depuis + // la souscription), d'où des intérêts largement sous-estimés (28,33€ au lieu de ~88,54€). const dateChangee = nouvelle_date_cible !== ancienne_date_cible; let nouveau_duree_mois = ancien_duree_mois; - if (dateChangee && inv.date_premiere_echeance) { - nouveau_duree_mois = monthsDiff(inv.date_premiere_echeance, nouvelle_date_cible) + 1; + if (dateChangee) { + if (inv.type_remb === 'differe' && inv.date_souscription) { + nouveau_duree_mois = monthsDiff(inv.date_souscription, nouvelle_date_cible); + } else if (inv.date_premiere_echeance) { + nouveau_duree_mois = monthsDiff(inv.date_premiere_echeance, nouvelle_date_cible) + 1; + } } // Statut "Prolongation" : uniquement quand la date cible change réellement ET que le @@ -782,7 +796,7 @@ router.put('/:id/revisions/:rid', (req, res, next) => { const body = RevisionSchema.parse(req.body); const invId = Number(req.params.id); const inv = db.prepare(` - SELECT i.id, i.statut, i.date_premiere_echeance FROM investissements i + SELECT i.id, i.statut, i.date_premiere_echeance, i.type_remb, i.date_souscription FROM investissements i JOIN investisseurs inv ON inv.id = i.investisseur_id WHERE i.id = ? AND inv.user_id = ? `).get(invId, req.user.id); @@ -802,10 +816,17 @@ router.put('/:id/revisions/:rid', (req, res, next) => { const nouveau_taux = body.nouveau_taux ?? ancien_taux; const nouvelle_date_cible = body.nouvelle_date_cible ?? ancienne_date_cible; + // Cf. commentaire équivalent dans POST /:id/revisions : un prêt "differe" (versement + // unique) recalcule duree_mois depuis date_souscription (sans +1), jamais depuis + // date_premiere_echeance comme les prêts à échéances multiples. const dateChangee = nouvelle_date_cible !== ancienne_date_cible; let nouveau_duree_mois = ancien_duree_mois; - if (dateChangee && inv.date_premiere_echeance) { - nouveau_duree_mois = monthsDiff(inv.date_premiere_echeance, nouvelle_date_cible) + 1; + if (dateChangee) { + if (inv.type_remb === 'differe' && inv.date_souscription) { + nouveau_duree_mois = monthsDiff(inv.date_souscription, nouvelle_date_cible); + } else if (inv.date_premiere_echeance) { + nouveau_duree_mois = monthsDiff(inv.date_premiere_echeance, nouvelle_date_cible) + 1; + } } // Statut : on rejoue le même raisonnement que POST/DELETE combinés — d'abord retrouver le