diff --git a/backend/src/db/index.js b/backend/src/db/index.js index 3a2c945..e08c186 100644 --- a/backend/src/db/index.js +++ b/backend/src/db/index.js @@ -2263,11 +2263,24 @@ console.log('[DB] Migrations 2FA OK'); WHERE i.is_principal = 1 AND NOT EXISTS (SELECT 1 FROM comptes c WHERE c.investisseur_id = i.id) `).all(); - for (const inv of principalsWithoutCompte) { - db.prepare( - 'INSERT INTO comptes (user_id, nom, type, investisseur_id) VALUES (?,?,?,?)' - ).run(inv.user_id, `Compte courant — ${inv.nom}`, 'compte_courant', inv.investisseur_id); - console.log(`[DB] backfill: compte courant créé pour l'investisseur principal #${inv.investisseur_id} (user #${inv.user_id})`); + if (principalsWithoutCompte.length > 0) { + // Rattache directement l'enveloppe "Compte courant non rémunéré" et marque le compte comme + // compte de règlement par défaut (28/09/26 (suite), fusion Type/Enveloppe demandée par + // Olivier). Ce bloc de backfill s'exécute AVANT la création de enveloppes_referentiel plus + // bas dans ce fichier — garde d'existence de table pour ne jamais casser un premier + // démarrage sur base neuve (où principalsWithoutCompte est de toute façon vide). + const enveloppesTableExiste = db.prepare( + "SELECT COUNT(*) AS n FROM sqlite_master WHERE type='table' AND name='enveloppes_referentiel'" + ).get().n > 0; + const enveloppeCompteCourantNonRemunere = enveloppesTableExiste + ? db.prepare("SELECT id FROM enveloppes_referentiel WHERE nom = 'Compte courant non rémunéré'").get() + : null; + for (const inv of principalsWithoutCompte) { + db.prepare( + 'INSERT INTO comptes (user_id, nom, type, enveloppe_id, investisseur_id, compte_reglement_par_defaut) VALUES (?,?,?,?,?,1)' + ).run(inv.user_id, `Compte courant — ${inv.nom}`, 'compte_courant', enveloppeCompteCourantNonRemunere?.id ?? null, inv.investisseur_id); + console.log(`[DB] backfill: compte courant créé pour l'investisseur principal #${inv.investisseur_id} (user #${inv.user_id})`); + } } } @@ -4134,13 +4147,13 @@ db.prepare("DELETE FROM supports_referentiel WHERE nom = 'Gestion pilotée / man // ── Migration : enveloppe_id sur comptes (Phase 3a, 28/09/26, demande Olivier) ──────────── // Rattache un compte au référentiel enveloppes_referentiel — nullable, non-destructif, sur le // même principe que comptes.institution_id (Phase 2, cf. plan de déploiement Institutions -// financières) : le champ `type` existant (compte_courant/pea_pme) reste en base tel quel et -// continue de piloter la logique de remboursement/frais déjà en place (de nombreux écrans -// investissement/remboursement font comptes.find(c => c.type === 'compte_courant') pour -// proposer un compte de règlement par défaut) — enveloppe_id est un enrichissement PUREMENT -// descriptif, ajouté à côté du champ type, jamais en remplacement, pour ne rien casser de cette -// logique existante. Restreint aux enveloppes "compte financier" (eligible_compte = 1) côté -// sélecteur frontend uniquement — pas de contrainte CHECK en base, pour rester simple. +// financières). À l'origine ajouté PUREMENT à côté du champ `type` existant (compte_courant/ +// pea_pme), qui pilotait alors seul la logique de sélection d'un compte de règlement par défaut +// sur les remboursements/frais. Depuis la migration compte_reglement_par_defaut plus bas dans ce +// fichier (28/09/26 (suite), fusion Type/Enveloppe demandée par Olivier), c'est ce nouveau +// booléen dédié qui pilote cette logique — `type` reste en base mais n'est plus lu nulle part. +// Restreint aux enveloppes "compte financier" (eligible_compte = 1) côté sélecteur frontend +// uniquement — pas de contrainte CHECK en base, pour rester simple. { const cols = db.prepare('PRAGMA table_info(comptes)').all().map(c => c.name); if (!cols.includes('enveloppe_id')) { @@ -4204,4 +4217,73 @@ db.exec(` db.exec('CREATE INDEX IF NOT EXISTS idx_emprunts_user ON emprunts(user_id)'); db.exec('CREATE INDEX IF NOT EXISTS idx_emprunts_inv ON emprunts(investisseur_id)'); +// ── Migration : compte_reglement_par_defaut sur comptes (28/09/26 (suite), demande Olivier — +// fusion Type/Enveloppe) ────────────────────────────────────────────────────────────────── +// Après la Phase 3a, Olivier a demandé d'aller plus loin : éliminer la redondance entre le +// champ `type` (compte_courant/pea_pme, 2 valeurs codées en dur) et le rattachement au +// référentiel `enveloppe_id` (cf. migration ci-dessus). Exploration du code réel (28/09/26) : +// `type` n'a en réalité qu'UN SEUL consommateur métier dans toute l'application — la sélection +// d'un compte de règlement par défaut pour un investisseur (comptes.find(c => c.type === +// 'compte_courant'), 7 emplacements frontend + 1 requête backend de rattrapage, tous avec un +// repli sur le premier compte sinon). Toutes les autres occurrences de la chaîne +// "compte_courant"/"pea_pme" dans le code appartiennent à des champs sans rapport +// (methode_remboursement/mode_reglement sur remboursements/frais/investissements, +// mode_detention côté Private Equity) et ne sont pas concernées par ce changement. +// Remplacement choisi (validé par Olivier via AskUserQuestion, 28/09/26) : un booléen dédié +// plutôt qu'une dérivation depuis le nom de l'enveloppe — découplé du choix d'enveloppe, donc +// valable même si un jour un compte de règlement par défaut n'est pas un "compte courant" au +// sens strict (ex. un livret ou un compte rémunéré fintech). `type` reste en base, non-destructif +// comme toujours dans ce chantier, mais n'est plus lu par aucune logique ni affiché/modifiable +// dans l'UI (Paramètres > Mes comptes courants) à partir de cette migration. +{ + const cols = db.prepare('PRAGMA table_info(comptes)').all().map(c => c.name); + if (!cols.includes('compte_reglement_par_defaut')) { + db.exec('ALTER TABLE comptes ADD COLUMN compte_reglement_par_defaut INTEGER NOT NULL DEFAULT 0'); + // Backfill exécuté une seule fois, dans cette même garde de colonne : reproduit exactement + // le comportement actuel (comptes.find(c => c.type === 'compte_courant')) pour chaque + // investisseur ayant déjà un compte de type compte_courant — le premier par id, comme le + // faisait `.find()` côté frontend. Une édition manuelle ultérieure n'est ensuite jamais + // réécrasée par ce bloc (qui ne s'exécute qu'une fois, à la création de la colonne). + const investisseurIds = db.prepare( + 'SELECT DISTINCT investisseur_id FROM comptes WHERE investisseur_id IS NOT NULL' + ).all().map(r => r.investisseur_id); + const findDefault = db.prepare( + "SELECT id FROM comptes WHERE investisseur_id = ? AND type = 'compte_courant' ORDER BY id LIMIT 1" + ); + const setDefault = db.prepare('UPDATE comptes SET compte_reglement_par_defaut = 1 WHERE id = ?'); + db.transaction(() => { + for (const invId of investisseurIds) { + const row = findDefault.get(invId); + if (row) setDefault.run(row.id); + } + })(); + } +} + +// ── Backfill : rattachement rétroactif de l'enveloppe "Compte courant non rémunéré" sur +// les comptes existants (28/09/26 (suite), demande Olivier) ───────────────────────────── +// La fusion Type/Enveloppe ci-dessus câble l'auto-rattachement enveloppe_id + +// compte_reglement_par_defaut UNIQUEMENT sur les 4 points de création d'un compte courant +// (inscription, invitation, ajout de membre, seed admin) — jamais en rattrapage sur les +// comptes déjà existants avant ce chantier, puisque la règle "compte courant → enveloppe +// Compte courant non rémunéré" n'existait pas encore au moment où enveloppe_id a été ajouté +// (Phase 3a, purement additif, sans valeur par défaut). Olivier a signalé ce trou après coup +// (« est-ce que les utilisateurs existants ont migré le compte courant vers la bonne +// enveloppe ? ») — corrigé ici par un backfill toujours exécuté, gardé par +// enveloppe_id IS NULL : ne touche donc jamais un compte déjà rattaché, que ce soit +// manuellement (comme les comptes d'Olivier) ou par une exécution précédente de ce même +// bloc. Ne s'applique qu'aux comptes type='compte_courant' — un PEA-PME reste sans +// enveloppe automatique, cohérent avec le fait que seuls les comptes courants sont +// auto-créés/auto-rattachés par les 4 points de création listés ci-dessus. +{ + const enveloppeCompteCourantNonRemunere = db.prepare( + "SELECT id FROM enveloppes_referentiel WHERE nom = 'Compte courant non rémunéré'" + ).get(); + if (enveloppeCompteCourantNonRemunere) { + db.prepare( + "UPDATE comptes SET enveloppe_id = ? WHERE type = 'compte_courant' AND enveloppe_id IS NULL" + ).run(enveloppeCompteCourantNonRemunere.id); + } +} + export default db; diff --git a/backend/src/routes/auth.js b/backend/src/routes/auth.js index cfba5a5..9a35fb2 100644 --- a/backend/src/routes/auth.js +++ b/backend/src/routes/auth.js @@ -55,10 +55,15 @@ router.post('/register', async (req, res, next) => { `INSERT INTO investisseurs (user_id, nom, prenom, type, type_fiscal, is_principal) VALUES (?, ?, ?, 'famille', 'PP', 1)` ).run(userId, fullName, prenom); - // Auto-créer un compte courant pour le profil principal + // Auto-créer un compte courant pour le profil principal — rattaché directement à + // l'enveloppe "Compte courant non rémunéré" et marqué compte de règlement par défaut + // (28/09/26 (suite), demande Olivier). + const enveloppeCompteCourantNonRemunere = db.prepare( + "SELECT id FROM enveloppes_referentiel WHERE nom = 'Compte courant non rémunéré'" + ).get(); db.prepare( - 'INSERT INTO comptes (user_id, nom, type, investisseur_id) VALUES (?,?,?,?)' - ).run(userId, `Compte courant — ${fullName}`, 'compte_courant', invResult.lastInsertRowid); + 'INSERT INTO comptes (user_id, nom, type, enveloppe_id, investisseur_id, compte_reglement_par_defaut) VALUES (?,?,?,?,?,1)' + ).run(userId, `Compte courant — ${fullName}`, 'compte_courant', enveloppeCompteCourantNonRemunere?.id ?? null, invResult.lastInsertRowid); // Accorde l'accès au workspace socle (crowdlending) à tout nouveau compte // — cf. project_workspaces_transformation.md. diff --git a/backend/src/routes/comptes.js b/backend/src/routes/comptes.js index fc1bebc..cfc87a2 100644 --- a/backend/src/routes/comptes.js +++ b/backend/src/routes/comptes.js @@ -7,21 +7,33 @@ const router = Router(); const Schema = z.object({ nom: z.string().min(1), - type: z.enum(['compte_courant', 'pea_pme']).default('compte_courant'), investisseur_id: z.number().int().positive().nullable().optional(), banque: z.string().nullable().optional(), institution_id: z.number().int().positive().nullable().optional(), // Phase 3a (28/09/26) — rattachement au référentiel enveloppes_referentiel, purement - // descriptif et non-destructif (cf. commentaire de migration dans db/index.js) : n'a aucune - // incidence sur `type`, qui reste le champ utilisé par la logique de remboursement/frais - // existante. Non contraint à eligible_compte=1 côté Zod — le filtrage se fait côté frontend. + // descriptif et non-destructif (cf. commentaire de migration dans db/index.js). Non contraint + // à eligible_compte=1 côté Zod — le filtrage se fait côté frontend. enveloppe_id: z.number().int().positive().nullable().optional(), exoneration_fiscale: z.enum(['aucune', 'pfnl_5ans']).default('aucune'), + // Fusion Type/Enveloppe (28/09/26 (suite), demande Olivier) — remplace l'ancien champ `type` + // (compte_courant/pea_pme) comme unique source de vérité pour la sélection d'un compte de + // règlement par défaut sur les remboursements/frais (cf. migration compte_reglement_par_defaut + // dans db/index.js). Au plus un compte par investisseur ; l'exclusivité est appliquée + // ci-dessous via clearOtherDefaults(), pas via une contrainte SQL (investisseur_id nullable). + compte_reglement_par_defaut: z.boolean().optional().default(false), }); +function clearOtherDefaults(userId, investisseurId, excludeId) { + if (!investisseurId) return; + db.prepare( + 'UPDATE comptes SET compte_reglement_par_defaut = 0 WHERE investisseur_id = ? AND user_id = ? AND id <> ?' + ).run(investisseurId, userId, excludeId ?? -1); +} + router.get('/', (req, res) => { const rows = db.prepare(` - SELECT c.id, c.nom, c.type, c.banque, c.institution_id, c.enveloppe_id, c.exoneration_fiscale, c.investisseur_id, c.created_at, + SELECT c.id, c.nom, c.banque, c.institution_id, c.enveloppe_id, c.exoneration_fiscale, + c.investisseur_id, c.compte_reglement_par_defaut, c.created_at, inv.nom AS investisseur_nom, inv.prenom AS investisseur_prenom, inv.type AS investisseur_type, inv.type_fiscal AS investisseur_type_fiscal, ir.nom AS institution_nom, ir.domiciliation AS institution_domiciliation, @@ -40,20 +52,29 @@ router.get('/', (req, res) => { router.post('/', (req, res, next) => { try { const body = Schema.parse(req.body); - const r = db.prepare( - 'INSERT INTO comptes (user_id, nom, type, banque, institution_id, enveloppe_id, investisseur_id, exoneration_fiscale) VALUES (?,?,?,?,?,?,?,?)' - ).run(req.user.id, body.nom, body.type, body.banque || null, body.institution_id ?? null, body.enveloppe_id ?? null, body.investisseur_id ?? null, body.exoneration_fiscale ?? 'aucune'); - res.status(201).json({ id: r.lastInsertRowid, ...body }); + const defaut = body.compte_reglement_par_defaut ? 1 : 0; + const id = db.transaction(() => { + if (defaut) clearOtherDefaults(req.user.id, body.investisseur_id); + const r = db.prepare( + 'INSERT INTO comptes (user_id, nom, banque, institution_id, enveloppe_id, investisseur_id, exoneration_fiscale, compte_reglement_par_defaut) VALUES (?,?,?,?,?,?,?,?)' + ).run(req.user.id, body.nom, body.banque || null, body.institution_id ?? null, body.enveloppe_id ?? null, body.investisseur_id ?? null, body.exoneration_fiscale ?? 'aucune', defaut); + return r.lastInsertRowid; + })(); + res.status(201).json({ id, ...body }); } catch (e) { next(e); } }); router.put('/:id', (req, res, next) => { try { const body = Schema.parse(req.body); - const r = db.prepare( - `UPDATE comptes SET nom=?, type=?, banque=?, institution_id=?, enveloppe_id=?, investisseur_id=?, exoneration_fiscale=?, updated_at=datetime('now') WHERE id=? AND user_id=?` - ).run(body.nom, body.type, body.banque || null, body.institution_id ?? null, body.enveloppe_id ?? null, body.investisseur_id ?? null, body.exoneration_fiscale ?? 'aucune', req.params.id, req.user.id); - if (r.changes === 0) throw new HttpError(404, 'Not found'); + const defaut = body.compte_reglement_par_defaut ? 1 : 0; + const changes = db.transaction(() => { + if (defaut) clearOtherDefaults(req.user.id, body.investisseur_id, Number(req.params.id)); + return db.prepare( + `UPDATE comptes SET nom=?, banque=?, institution_id=?, enveloppe_id=?, investisseur_id=?, exoneration_fiscale=?, compte_reglement_par_defaut=?, updated_at=datetime('now') WHERE id=? AND user_id=?` + ).run(body.nom, body.banque || null, body.institution_id ?? null, body.enveloppe_id ?? null, body.investisseur_id ?? null, body.exoneration_fiscale ?? 'aucune', defaut, req.params.id, req.user.id).changes; + })(); + if (changes === 0) throw new HttpError(404, 'Not found'); res.json({ id: Number(req.params.id), ...body }); } catch (e) { next(e); } }); diff --git a/backend/src/routes/investissements.js b/backend/src/routes/investissements.js index 40ee6ef..c062847 100644 --- a/backend/src/routes/investissements.js +++ b/backend/src/routes/investissements.js @@ -225,7 +225,7 @@ router.get('/comptes-par-investisseur/:investisseur_id', (req, res, next) => { .get(invId, req.user.id); if (!inv) throw new HttpError(403, 'Investisseur non autorisé'); const rows = db.prepare( - 'SELECT id, nom, type, banque FROM comptes WHERE investisseur_id = ? AND user_id = ? ORDER BY type, nom' + 'SELECT id, nom, banque, compte_reglement_par_defaut FROM comptes WHERE investisseur_id = ? AND user_id = ? ORDER BY compte_reglement_par_defaut DESC, nom' ).all(invId, req.user.id); res.json(rows); } catch (e) { next(e); } diff --git a/backend/src/routes/investisseurs.js b/backend/src/routes/investisseurs.js index c8ff0b9..84b2040 100644 --- a/backend/src/routes/investisseurs.js +++ b/backend/src/routes/investisseurs.js @@ -45,9 +45,15 @@ router.post('/', (req, res, next) => { // Auto-créer un compte courant pour ce nouveau profil // body.nom contient déjà le nom complet (ex. "Marine CROGUENNEC") const label = `Compte courant — ${body.nom}`; + // Rattache directement l'enveloppe "Compte courant non rémunéré" et marque ce compte comme + // compte de règlement par défaut — plus besoin de le faire à la main ensuite dans + // Paramètres > Mes comptes courants (28/09/26 (suite), demande Olivier). + const enveloppeCompteCourantNonRemunere = db.prepare( + "SELECT id FROM enveloppes_referentiel WHERE nom = 'Compte courant non rémunéré'" + ).get(); db.prepare( - 'INSERT INTO comptes (user_id, nom, type, investisseur_id) VALUES (?,?,?,?)' - ).run(req.user.id, label, 'compte_courant', invId); + 'INSERT INTO comptes (user_id, nom, type, enveloppe_id, investisseur_id, compte_reglement_par_defaut) VALUES (?,?,?,?,?,1)' + ).run(req.user.id, label, 'compte_courant', enveloppeCompteCourantNonRemunere?.id ?? null, invId); res.status(201).json({ id: invId, ...body }); } catch (e) { next(e); } diff --git a/backend/src/routes/invitations.js b/backend/src/routes/invitations.js index 2e345c3..62f5b76 100644 --- a/backend/src/routes/invitations.js +++ b/backend/src/routes/invitations.js @@ -194,9 +194,14 @@ router.post('/:token/register', async (req, res, next) => { const invResult = db.prepare( `INSERT INTO investisseurs (user_id, nom, prenom, type, type_fiscal, is_principal) VALUES (?, ?, ?, 'famille', 'PP', 1)` ).run(pendingUser.id, fullName, prenom); + // Rattache directement l'enveloppe "Compte courant non rémunéré" et marque ce compte comme + // compte de règlement par défaut (28/09/26 (suite), demande Olivier). + const enveloppeCompteCourantNonRemunere = db.prepare( + "SELECT id FROM enveloppes_referentiel WHERE nom = 'Compte courant non rémunéré'" + ).get(); db.prepare( - 'INSERT INTO comptes (user_id, nom, type, investisseur_id) VALUES (?,?,?,?)' - ).run(pendingUser.id, `Compte courant — ${fullName}`, 'compte_courant', invResult.lastInsertRowid); + 'INSERT INTO comptes (user_id, nom, type, enveloppe_id, investisseur_id, compte_reglement_par_defaut) VALUES (?,?,?,?,?,1)' + ).run(pendingUser.id, `Compte courant — ${fullName}`, 'compte_courant', enveloppeCompteCourantNonRemunere?.id ?? null, invResult.lastInsertRowid); // Accorde l'accès au workspace socle (crowdlending) à tout nouveau compte // — cf. project_workspaces_transformation.md. diff --git a/backend/src/routes/remboursements.js b/backend/src/routes/remboursements.js index 5cbebd2..348dc48 100644 --- a/backend/src/routes/remboursements.js +++ b/backend/src/routes/remboursements.js @@ -612,7 +612,7 @@ router.post('/backfill-comptes', (req, res, next) => { // Fallback : premier compte_courant du détenteur if (!compteId) { const compte = db.prepare( - "SELECT id FROM comptes WHERE investisseur_id=? AND user_id=? AND type='compte_courant' ORDER BY id LIMIT 1" + "SELECT id FROM comptes WHERE investisseur_id=? AND user_id=? AND compte_reglement_par_defaut=1 ORDER BY id LIMIT 1" ).get(row.investisseur_id, req.user.id); compteId = compte?.id ?? null; } diff --git a/frontend/src/components/InvestissementFormModal.jsx b/frontend/src/components/InvestissementFormModal.jsx index 9c90b8c..d8e172a 100644 --- a/frontend/src/components/InvestissementFormModal.jsx +++ b/frontend/src/components/InvestissementFormModal.jsx @@ -168,7 +168,7 @@ export default function InvestissementFormModal({ const handleMethodeChange = (methode) => { const defaultCompte = methode === 'compte_courant' - ? (comptesInvestisseur.find(c => c.type === 'compte_courant') ?? comptesInvestisseur[0]) + ? (comptesInvestisseur.find(c => c.compte_reglement_par_defaut) ?? comptesInvestisseur[0]) : null; onFormPatch({ methode_remboursement: methode, nom_compte_courant: defaultCompte?.nom || '', compte_id: String(defaultCompte?.id || '') }); }; diff --git a/frontend/src/components/RembFormModal.jsx b/frontend/src/components/RembFormModal.jsx index 6709d80..a99a712 100644 --- a/frontend/src/components/RembFormModal.jsx +++ b/frontend/src/components/RembFormModal.jsx @@ -123,7 +123,7 @@ export default function RembFormModal({ const modalTitle = title ?? (editingId ? 'Modifier le remboursement' : (locked ? 'Saisir le remboursement' : 'Nouveau remboursement')); const handleMethodeChange = (m) => { - const def = m === 'compte_courant' ? (comptesInvestisseur?.find(c => c.type === 'compte_courant') ?? comptesInvestisseur?.[0]) : null; + const def = m === 'compte_courant' ? (comptesInvestisseur?.find(c => c.compte_reglement_par_defaut) ?? comptesInvestisseur?.[0]) : null; onFormPatch({ methode_remboursement: m, compte_id: def ? String(def.id) : '' }); }; @@ -133,7 +133,7 @@ export default function RembFormModal({ // 'compte_courant' — même pattern que handleFraisReglementChange côté investissement. const handleFraisModeChange = (mode) => { onFieldChange('frais_mode_reglement', mode); - const def = mode === 'compte_courant' ? (comptesInvestisseur?.find(c => c.type === 'compte_courant') ?? comptesInvestisseur?.[0]) : null; + const def = mode === 'compte_courant' ? (comptesInvestisseur?.find(c => c.compte_reglement_par_defaut) ?? comptesInvestisseur?.[0]) : null; onFormPatch({ frais_compte_id: def ? String(def.id) : '' }); }; diff --git a/frontend/src/pages/InvestissementDetail.jsx b/frontend/src/pages/InvestissementDetail.jsx index 50f659d..f0d38e6 100644 --- a/frontend/src/pages/InvestissementDetail.jsx +++ b/frontend/src/pages/InvestissementDetail.jsx @@ -127,7 +127,7 @@ export default function InvestissementDetail() { .then(comptes => { setComptesInvestisseur(comptes); if (form.methode_remboursement === 'compte_courant' && !form.compte_id) { - const def = comptes.find(c => c.type === 'compte_courant') ?? comptes[0]; + const def = comptes.find(c => c.compte_reglement_par_defaut) ?? comptes[0]; if (def) setForm(f => ({ ...f, compte_id: String(def.id), nom_compte_courant: def.nom })); } }) @@ -148,7 +148,7 @@ export default function InvestissementDetail() { .then(comptes => { setComptesRembInvestisseur(comptes); if (rembForm.methode_remboursement === 'compte_courant' && !rembForm.compte_id) { - const def = comptes.find(c => c.type === 'compte_courant') ?? comptes[0]; + const def = comptes.find(c => c.compte_reglement_par_defaut) ?? comptes[0]; if (def) setRembForm(f => ({ ...f, compte_id: String(def.id) })); } }) diff --git a/frontend/src/pages/Investissements.jsx b/frontend/src/pages/Investissements.jsx index cc1f16b..f6cddce 100644 --- a/frontend/src/pages/Investissements.jsx +++ b/frontend/src/pages/Investissements.jsx @@ -342,7 +342,7 @@ export default function Investissements() { .then(comptes => { setComptesInvestisseur(comptes); if (form.methode_remboursement === 'compte_courant' && !form.compte_id) { - const def = comptes.find(c => c.type === 'compte_courant') ?? comptes[0]; + const def = comptes.find(c => c.compte_reglement_par_defaut) ?? comptes[0]; if (def) setForm(f => ({ ...f, compte_id: String(def.id), nom_compte_courant: def.nom })); } }) diff --git a/frontend/src/pages/Remboursements.jsx b/frontend/src/pages/Remboursements.jsx index 5e7232c..b186952 100644 --- a/frontend/src/pages/Remboursements.jsx +++ b/frontend/src/pages/Remboursements.jsx @@ -727,7 +727,7 @@ export default function Remboursements() { if (next.methode_remboursement === 'compte_courant') { // Priorité : compte lié à l'investissement, sinon premier compte_courant const inherited = inv.compte_id ? comptes.find(c => c.id === inv.compte_id) : null; - const def = inherited ?? comptes.find(c => c.type === 'compte_courant') ?? comptes[0]; + const def = inherited ?? comptes.find(c => c.compte_reglement_par_defaut) ?? comptes[0]; if (def) setForm(f => ({ ...f, compte_id: String(def.id) })); } }) diff --git a/frontend/src/pages/settings/ComptesSection.jsx b/frontend/src/pages/settings/ComptesSection.jsx index d5daa5c..e24afa0 100644 --- a/frontend/src/pages/settings/ComptesSection.jsx +++ b/frontend/src/pages/settings/ComptesSection.jsx @@ -9,12 +9,7 @@ const EXONERATION_LABELS = { aucune: 'Aucune', pfnl_5ans: 'PFnl 5 ans', }; -const TYPE_COMPTE_LABELS = { - compte_courant: 'Compte courant', - pea_pme: 'PEA-PME', -}; - -const EMPTY_COMPTE = { nom: '', type: 'compte_courant', investisseur_id: null, institution_id: null, banque: '', enveloppe_id: null, exoneration_fiscale: 'aucune' }; +const EMPTY_COMPTE = { nom: '', investisseur_id: null, institution_id: null, banque: '', enveloppe_id: null, exoneration_fiscale: 'aucune', compte_reglement_par_defaut: false }; // Référentiel institutions financières (Phase 2, 26/09/26) — logos servis au même endroit // que ceux des plateformes (/api/logos/:filename), cf. institutionsReferentiel.js. @@ -45,31 +40,22 @@ function CompteFormFields({ state, setter, investisseurs, institutions, envelopp onChange={e => setter({ ...state, nom: e.target.value })} placeholder="ex. Compte courant BNP" /> -
-
- - -
-
- - -
+
+ +
{isAutre && (
@@ -91,7 +77,7 @@ function CompteFormFields({ state, setter, investisseurs, institutions, envelopp ))}

- Rattachement au référentiel (Admin > Enveloppes & supports) — informatif, sans effet sur le champ Type ci-dessus. + Rattachement au référentiel (Admin > Enveloppes & supports).

@@ -104,6 +90,18 @@ function CompteFormFields({ state, setter, investisseurs, institutions, envelopp ))}
+
+ +

+ Utilisé pour pré-remplir automatiquement le compte de règlement sur les remboursements + et frais de ce détenteur. Un seul compte par défaut par détenteur (28/09/26 — remplace + l'ancien champ Type). +

+