Correction bug

This commit is contained in:
ocroguennec committed 2026-08-22 17:59:17 +02:00
1 parent 8e1b82d161
commit dc05e633c6
2 files changed
+64 -4

No files matched your search

+62 -3
View File
@@ -125,7 +125,8 @@ export function monthsDiff(isoA, isoB) {
export function adjustSimulForActuals(db, investissementId) {
const inv = db.prepare(`
SELECT id, montant_investi, taux_interet, duree_mois, type_remb, freq_interets,
date_premiere_echeance, date_debut_simul, date_souscription, echeance_fin_de_mois
date_premiere_echeance, date_debut_simul, date_souscription, echeance_fin_de_mois,
date_cible
FROM investissements WHERE id = ?
`).get(investissementId);
@@ -433,7 +434,8 @@ function adjustFirstPartialPeriod(db, investissementId) {
export function generateSimulWithReinvestissements(db, investissementId) {
const inv = db.prepare(`
SELECT id, montant_investi, taux_interet, duree_mois, type_remb, freq_interets,
date_premiere_echeance, date_debut_simul, date_souscription, echeance_fin_de_mois
date_premiere_echeance, date_debut_simul, date_souscription, echeance_fin_de_mois,
date_cible
FROM investissements WHERE id = ?
`).get(investissementId);
@@ -452,6 +454,31 @@ export function generateSimulWithReinvestissements(db, investissementId) {
// taux_interet peut légitimement valoir 0 (cf. commentaire dans adjustSimulForActuals)
if (inv.taux_interet == null || !inv.duree_mois) return;
// Cas particulier "differe" (versement unique), avec réinvestissements : une seule échéance
// au total, ancrée sur date_cible (la date d'effet de la révision la plus récente, jamais
// recalculée à partir d'un décompte de mois écoulés — cf. commentaire équivalent, et le
// bug qu'il corrige, dans generateSimul() plus bas).
if ((inv.type_remb || 'in_fine') === 'differe') {
const finDeMoisDiffere = !!inv.echeance_fin_de_mois;
const startDate = inv.date_cible || inv.date_premiere_echeance || inv.date_souscription;
if (!startDate) return;
const date = finDeMoisDiffere ? lastDayOfMonth(startDate) : startDate;
const capital = round2(
inv.montant_investi +
reinvests.filter(r => r.date_reinvestissement <= date).reduce((s, r) => s + r.montant, 0)
);
const interets = round2(capital * (inv.taux_interet / 100 / 12) * inv.duree_mois);
db.transaction(() => {
db.prepare('DELETE FROM simul_remboursements WHERE investissement_id=?').run(investissementId);
db.prepare(`
INSERT INTO simul_remboursements
(investissement_id, numero_echeance, date_prevue, capital_prevu, interets_prevus, total_prevu)
VALUES (?,?,?,?,?,?)
`).run(investissementId, 1, date, capital, interets, round2(capital + interets));
})();
return;
}
const finDeMois = !!inv.echeance_fin_de_mois;
const type = inv.type_remb || 'in_fine';
const freq = inv.freq_interets || 'mensuel';
@@ -609,12 +636,44 @@ export function generateSimulWithReinvestissements(db, investissementId) {
*/
export function generateSimul(db, inv) {
const { id, montant_investi, taux_interet, duree_mois, type_remb, freq_interets,
date_premiere_echeance, date_debut_simul, date_souscription, echeance_fin_de_mois } = inv;
date_premiere_echeance, date_debut_simul, date_souscription, echeance_fin_de_mois,
date_cible } = inv;
// taux_interet peut légitimement valoir 0 (cf. commentaire dans adjustSimulForActuals)
if (taux_interet == null || !duree_mois) return;
const finDeMois = !!echeance_fin_de_mois;
// Cas particulier "differe" (versement unique) : il n'existe jamais qu'une seule échéance
// au total, donc le mécanisme de restructuration ci-dessous (numérotation par mois écoulés
// depuis date_premiere_echeance, conservation d'échéances déjà honorées) n'a pas de sens —
// il a été conçu pour les prêts à échéances multiples (in_fine/amortissable) où l'on
// reprend le calendrier en cours de route. Appliqué tel quel à un prêt différé après une
// révision (ex. prolongation), il produisait un numero_echeance et une date aberrants :
// numero_echeance = mois écoulés + 1 (ex. n°10) au lieu de 1, et une date recalculée à
// partir de l'ancienne cadence (date_premiere_echeance + mois écoulés) au lieu de la
// nouvelle date cible — bug signalé par Olivier le 22/08/26 sur le dossier "Le Dolce
// Notte". On régénère donc toujours une unique échéance, ancrée sur date_cible (la date
// d'effet de la révision la plus récente, ou la date de première échéance d'origine si
// aucune révision n'a encore eu lieu).
if ((type_remb || 'in_fine') === 'differe') {
const startDate = date_cible || date_premiere_echeance || date_souscription;
if (!startDate) return;
const [echeance] = buildSchedule({
montant: montant_investi, taux: taux_interet, duree: duree_mois,
type: 'differe', freq: freq_interets || 'in_fine', startDate, finDeMois,
});
db.transaction(() => {
db.prepare('DELETE FROM simul_remboursements WHERE investissement_id=?').run(id);
db.prepare(`
INSERT INTO simul_remboursements
(investissement_id, numero_echeance, date_prevue, capital_prevu, interets_prevus, total_prevu)
VALUES (?,?,?,?,?,?)
`).run(id, 1, echeance.date, echeance.capital, echeance.interets, echeance.total);
})();
return;
}
const isRestructuration = !!date_debut_simul;
// En mode restructuration, on a besoin de connaître les échéances déjà honorées