FixBug: Décalrage de date des prêts différés

This commit is contained in:
2026-07-12 17:58:28 +02:00
parent ec3c7f7cab
commit 794f8684a2
3 changed files with 211 additions and 17 deletions
+82 -1
View File
@@ -3,7 +3,8 @@ import { z } from 'zod';
import db from '../db/index.js';
import { HttpError } from '../middleware/errorHandler.js';
import { requireInvestisseur } from '../middleware/investisseurScope.js';
import { generateSimul } from '../utils/schedule.js';
import { generateSimul, generateSimulWithReinvestissements } from '../utils/schedule.js';
import { checkStatutsRetard } from '../jobs/autoStatut.js';
const router = Router();
@@ -275,6 +276,86 @@ router.post('/fix-differe-dates', (req, res, next) => {
}
}
// Une date corrigée peut désormais être passée (prêt qui bascule en retard) ou au
// contraire future (prêt qui en sort) — on relance immédiatement la vérification de
// statut plutôt que d'attendre le prochain passage du job (minuit / redémarrage serveur),
// sinon le prêt affiche un statut incohérent avec sa date jusqu'au lendemain.
const statutsMisAJour = corriges.length > 0 ? checkStatutsRetard() : 0;
res.json({ updated: corriges.length, detail: corriges, statutsMisAJour });
} catch (err) {
next(err);
}
});
// POST /api/investissements/check-echeancier-differe
// Vérifie, pour chaque prêt différé, que l'échéancier de projection (simul_remboursements)
// contient bien une unique échéance dont la date correspond à date_premiere_echeance
// (== date_cible pour un prêt différé). Régénère l'échéancier sinon — utile après un
// import, une restauration, ou tout chemin ayant modifié les dates sans régénérer la
// simulation associée (cf. bouton "Corriger les dates des prêts différés" ci-dessus,
// qui ne couvre que l'écart date_souscription + durée > 2 ans).
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,
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
JOIN investisseurs inv ON inv.id = i.investisseur_id AND inv.user_id = ?
WHERE i.type_remb = 'differe'
AND i.date_premiere_echeance IS NOT NULL
AND i.taux_interet IS NOT NULL
AND i.duree_mois IS NOT NULL
`).all(req.user.id);
const getSimul = db.prepare(`
SELECT id, numero_echeance, date_prevue
FROM simul_remboursements
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;
if (!incoherent) continue;
const ancienEcheancier = simulRows.length === 0
? '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);
}
recordHistory(inv.id, {
type_evenement: 'correction_auto_echeancier',
changements: [{
champ: 'echeancier',
label: 'Échéancier de projection',
ancienne_valeur: ancienEcheancier,
nouvelle_valeur: inv.date_premiere_echeance,
}],
notes: "Correction automatique (Nettoyage > Vérifier la cohérence de l'échéancier des prêts différés)",
});
corriges.push({
id: inv.id,
nom_projet: inv.nom_projet,
ancien_echeancier: ancienEcheancier,
nouvelle_date: inv.date_premiere_echeance,
});
}
res.json({ updated: corriges.length, detail: corriges });
} catch (err) {
next(err);