This commit is contained in:
ocroguennec committed 2026-08-22 18:40:36 +02:00
1 parent dc05e633c6
commit c9ca27b7be
1 file changed
+27 -6
+27 -6
View File
@@ -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é // 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 // 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; const dateChangee = nouvelle_date_cible !== ancienne_date_cible;
let nouveau_duree_mois = ancien_duree_mois; let nouveau_duree_mois = ancien_duree_mois;
if (dateChangee && inv.date_premiere_echeance) { if (dateChangee) {
nouveau_duree_mois = monthsDiff(inv.date_premiere_echeance, nouvelle_date_cible) + 1; 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 // 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 body = RevisionSchema.parse(req.body);
const invId = Number(req.params.id); const invId = Number(req.params.id);
const inv = db.prepare(` 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 JOIN investisseurs inv ON inv.id = i.investisseur_id
WHERE i.id = ? AND inv.user_id = ? WHERE i.id = ? AND inv.user_id = ?
`).get(invId, req.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 nouveau_taux = body.nouveau_taux ?? ancien_taux;
const nouvelle_date_cible = body.nouvelle_date_cible ?? ancienne_date_cible; 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; const dateChangee = nouvelle_date_cible !== ancienne_date_cible;
let nouveau_duree_mois = ancien_duree_mois; let nouveau_duree_mois = ancien_duree_mois;
if (dateChangee && inv.date_premiere_echeance) { if (dateChangee) {
nouveau_duree_mois = monthsDiff(inv.date_premiere_echeance, nouvelle_date_cible) + 1; 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 // Statut : on rejoue le même raisonnement que POST/DELETE combinés — d'abord retrouver le