Ajout du type Intérêt plateforme pour les remboursements

This commit is contained in:
ocroguennec committed 2026-08-16 16:49:20 +02:00
1 parent 37f9f8b8a7
commit 1bab97b5b3
9 files changed
+248 -66

No files matched your search

+43 -7
View File
@@ -8,7 +8,7 @@ import { adjustSimulForActuals, generateSimulWithReinvestissements } from '../ut
const router = Router();
const Schema = z.object({
type: z.enum(['normal', 'bonus_parrainage', 'bonus_plateforme']).default('normal'),
type: z.enum(['normal', 'bonus_parrainage', 'bonus_plateforme', 'interets_plateforme']).default('normal'),
investissement_id: z.number().int().positive().nullable().optional(),
bonus_plateforme_id: z.number().int().positive().nullable().optional(),
bonus_investisseur_id: z.number().int().positive().nullable().optional(),
@@ -44,13 +44,20 @@ function computeChamps(body, isTaxIndicatif = false) {
return { interets_nets, net_recu };
}
function getTaxIndicatif(investissementId) {
if (!investissementId) return false;
function getTaxIndicatif(body) {
// Intérêts plateforme : pas d'investissement, la fiscalité applicable est celle
// de la plateforme associée (bonus_plateforme_id) — pas de fiscalite_override possible.
if (body.type === 'interets_plateforme') {
if (!body.bonus_plateforme_id) return false;
const p = db.prepare('SELECT fiscalite FROM plateformes WHERE id = ?').get(body.bonus_plateforme_id);
return p ? p.fiscalite !== 'flat_tax' : false;
}
if (!body.investissement_id) return false;
const row = db.prepare(`
SELECT p.fiscalite, i.fiscalite_override
FROM investissements i JOIN plateformes p ON p.id = i.plateforme_id
WHERE i.id = ?
`).get(investissementId);
`).get(body.investissement_id);
if (!row) return false;
return row.fiscalite !== 'flat_tax' || row.fiscalite_override === 'exonere';
}
@@ -167,6 +174,7 @@ router.get('/', (req, res) => {
WHEN 'normal' THEN b.inv_nom_projet
WHEN 'bonus_parrainage' THEN '— Bonus Parrainage' || CASE WHEN NULLIF(b.notes, '') IS NOT NULL THEN ' — ' || b.notes ELSE '' END
WHEN 'bonus_plateforme' THEN '— Bonus Plateforme' || CASE WHEN NULLIF(b.notes, '') IS NOT NULL THEN ' — ' || b.notes ELSE '' END
WHEN 'interets_plateforme' THEN '— Intérêts plateforme' || CASE WHEN NULLIF(b.notes, '') IS NOT NULL THEN ' — ' || b.notes ELSE '' END
ELSE b.inv_nom_projet
END AS nom_projet,
p.nom AS plateforme_nom,
@@ -188,7 +196,7 @@ router.get('/', (req, res) => {
router.post('/', (req, res, next) => {
try {
const body = Schema.parse(req.body);
const { interets_nets, net_recu } = computeChamps(body, getTaxIndicatif(body.investissement_id));
const { interets_nets, net_recu } = computeChamps(body, getTaxIndicatif(body));
if (body.type === 'normal') {
assertOwnedInvestissement(body.investissement_id, req.user.id);
@@ -229,9 +237,26 @@ router.post('/', (req, res, next) => {
return res.status(201).json({ id: r.lastInsertRowid, ...body, interets_nets, net_recu });
}
// Bonus (parrainage ou plateforme)
// Bonus (parrainage / plateforme) ou Intérêts plateforme
const bonusInvestisseurId = body.bonus_investisseur_id ?? req.investisseur.id;
assertOwnedInvestisseur(bonusInvestisseurId, req.user.id);
if (body.type === 'interets_plateforme') {
const r = db.prepare(`
INSERT INTO remboursements
(type, bonus_plateforme_id, bonus_investisseur_id, date_remb,
capital, cashback, interets_bruts_avant_local, taxe_locale, interets_bruts,
prelev_sociaux, prelev_forfaitaire, interets_nets, net_recu, statut, source, notes)
VALUES ('interets_plateforme',?,?,?, 0,0,?,?,?,?,?, ?,?, ?, 'manuel', ?)
`).run(
body.bonus_plateforme_id, bonusInvestisseurId, body.date_remb,
body.interets_bruts_avant_local, body.taxe_locale, body.interets_bruts,
body.prelev_sociaux, body.prelev_forfaitaire,
interets_nets, net_recu, body.statut, body.notes || null,
);
return res.status(201).json({ id: r.lastInsertRowid, ...body, interets_nets, net_recu });
}
const r = db.prepare(`
INSERT INTO remboursements
(type, bonus_plateforme_id, bonus_investisseur_id, date_remb,
@@ -348,7 +373,7 @@ router.put('/:id', (req, res, next) => {
if (!existing) throw new HttpError(404, 'Not found');
const body = Schema.parse(req.body);
const { interets_nets, net_recu } = computeChamps(body, getTaxIndicatif(body.investissement_id));
const { interets_nets, net_recu } = computeChamps(body, getTaxIndicatif(body));
if (body.type === 'normal') {
db.prepare(`
@@ -369,6 +394,17 @@ router.put('/:id', (req, res, next) => {
syncInvestissementStatut(body.investissement_id);
adjustSimulForActuals(db, body.investissement_id);
syncAutoRetrait(id, body.methode_remboursement, net_recu, body.investissement_id, body.date_remb);
} else if (body.type === 'interets_plateforme') {
db.prepare(`
UPDATE remboursements
SET date_remb=?, interets_bruts_avant_local=?, taxe_locale=?, interets_bruts=?,
prelev_sociaux=?, prelev_forfaitaire=?, interets_nets=?, net_recu=?, statut=?, notes=?
WHERE id=?
`).run(
body.date_remb, body.interets_bruts_avant_local, body.taxe_locale, body.interets_bruts,
body.prelev_sociaux, body.prelev_forfaitaire,
interets_nets, net_recu, body.statut, body.notes || null, id,
);
} else {
const bonusInvestisseurId = body.bonus_investisseur_id ?? existing.bonus_investisseur_id;
db.prepare(`