This commit is contained in:
ocroguennec committed 2026-09-28 08:24:16 +02:00
1 parent 624d32ff7c
commit bce89d8112
18 files changed
+663 -28

No files matched your search

+118
View File
@@ -4086,4 +4086,122 @@ db.prepare(`
// stade, la suppression est donc sans risque de perte de données utilisateur.
db.prepare("DELETE FROM supports_referentiel WHERE nom = 'Gestion pilotée / mandat'").run();
// ── Migration : eligible_compte sur enveloppes_referentiel (Phase 3a, 28/09/26, demande
// Olivier — chantier "rattacher Mes comptes courants au référentiel") ────────────────────
// Distingue les enveloppes qui ont le profil d'un vrai "compte" (banque/courtier/assureur —
// Livret A, PEA, Assurance-vie, CTO, Compte crypto exchange...) de celles qui n'en ont pas
// (immobilier physique, objets de collection, wallets crypto auto-conservés, placements via
// plateforme déjà couverts par le mécanisme plateformes_referentiel...). Sert à filtrer le
// sélecteur "Enveloppe" de Paramètres > Mes comptes (cf. comptes.enveloppe_id ci-dessous) —
// cadrage validé par Olivier via AskUserQuestion (28/09/26) : sous-ensemble "compte financier",
// pas tout le référentiel. Colonne booléenne plutôt que catégorie existante réutilisée : la
// classification ne suit aucune des 15 catégories actuelles (ex. "Actifs numériques" contient
// à la fois Compte crypto exchange, éligible, et les wallets auto-conservés, non éligibles).
{
const cols = db.prepare('PRAGMA table_info(enveloppes_referentiel)').all().map(c => c.name);
if (!cols.includes('eligible_compte')) {
db.exec("ALTER TABLE enveloppes_referentiel ADD COLUMN eligible_compte INTEGER NOT NULL DEFAULT 0");
// Backfill exécuté une seule fois (dans cette même garde de colonne) : classification
// initiale par nom, jamais retouchée ensuite même si l'admin édite ce flag à la main.
const ELIGIBLE_COMPTE_NOMS = [
// Comptes courants
'Compte courant non rémunéré', 'Compte courant rémunéré', 'Compte rémunéré nouvelle génération (fintech)',
// Épargne réglementée
'Livret A', 'LDDS', 'LEP', 'Livret Jeune', 'CEL', 'PEL',
// Épargne bancaire non réglementée
'Livret bancaire non réglementé', 'Compte à terme (CAT)',
// Assurance-vie et assimilés
'Assurance-vie monosupport (fonds euros)', 'Assurance-vie multisupport',
'Contrat de capitalisation', 'Assurance-vie luxembourgeoise',
// Épargne retraite
'PER individuel', 'PER collectif / obligatoire (PERCOL, article 83)',
// Épargne salariale
'PEE',
// Enveloppes titres / marchés financiers
'PEA', 'PEA-PME', 'Compte-titres ordinaire (CTO)',
// Actifs numériques — seul le compte sur exchange a une institution (l'exchange
// lui-même) ; les wallets auto-conservés/conservés par un tiers restent hors périmètre.
'Compte crypto (exchange)',
// Épargne solidaire
'Livret solidaire',
];
const setEligible = db.prepare('UPDATE enveloppes_referentiel SET eligible_compte = 1 WHERE nom = ?');
db.transaction(() => { for (const nom of ELIGIBLE_COMPTE_NOMS) setEligible.run(nom); })();
}
}
// ── 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.
{
const cols = db.prepare('PRAGMA table_info(comptes)').all().map(c => c.name);
if (!cols.includes('enveloppe_id')) {
db.exec('ALTER TABLE comptes ADD COLUMN enveloppe_id INTEGER REFERENCES enveloppes_referentiel(id) ON DELETE SET NULL');
}
}
// ── Extension du 28/09/26 (Phase 3a) — catégorie "Gestion de trésorerie" + Spiko / Regular
// Finance ──────────────────────────────────────────────────────────────────────────────────
// Identifiés pendant le chantier "Référentiel Enveloppes & Supports d'épargne" (cf. "Extension
// du 29/09/26 (suite) — support Fonds monétaires" dans le plan de déploiement dédié) : Spiko et
// Regular Finance ne sont pas des enveloppes mais des plateformes/institutions donnant accès à
// un fonds monétaire (OPCVM monétaire, y compris tokenisé sur blockchain chez Spiko) — elles ont
// vocation à rejoindre institutions_referentiel, pas enveloppes_referentiel. Toujours exécuté,
// INSERT OR IGNORE sur des clés UNIQUE(nom) — s'ajoute sans jamais dupliquer ni écraser le
// référentiel déjà seedé (Phase 1), même principe que les extensions "EXTRA" du chantier
// enveloppes.
{
db.prepare('INSERT OR IGNORE INTO categories_institutions (nom) VALUES (?)').run('Gestion de trésorerie');
const insInst = db.prepare('INSERT OR IGNORE INTO institutions_referentiel (nom) VALUES (?)');
const getInst = db.prepare('SELECT id FROM institutions_referentiel WHERE nom = ?');
const getCat = db.prepare('SELECT id FROM categories_institutions WHERE nom = ?');
const linkCat = db.prepare('INSERT OR IGNORE INTO referentiel_categories_institutions (institution_id, categorie_id) VALUES (?, ?)');
db.transaction(() => {
for (const nom of ['Spiko', 'Regular Finance']) {
insInst.run(nom);
const inst = getInst.get(nom);
const cat = getCat.get('Gestion de trésorerie');
if (inst && cat) linkCat.run(inst.id, cat.id);
}
})();
}
// ── Chantier "Passif — Emprunts" (Phase 3c, 28/09/26, demande Olivier) ───────────────────────
// Objet séparé de enveloppes_referentiel : un emprunt est structurellement différent d'un actif
// (le capital restant dû DIMINUE avec le temps, porte un taux et un échéancier fixés à la
// souscription) — cf. "Réflexion prospective" du plan de déploiement Enveloppes & Supports (doc
// projet), point 3 ("un passif, pas un actif"). Table dédiée plutôt qu'un champ sur comptes,
// pour la même raison qu'un compte n'est jamais une "enveloppe" : structure et cycle de vie
// différents (un compte se crédite/débite librement, un emprunt s'amortit). Rattachement
// institution optionnel (établissement prêteur) via institutions_referentiel, sur le même
// principe que comptes.institution_id — nullable, non contraignant.
db.exec(`
CREATE TABLE IF NOT EXISTS emprunts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
investisseur_id INTEGER REFERENCES investisseurs(id) ON DELETE SET NULL,
institution_id INTEGER REFERENCES institutions_referentiel(id) ON DELETE SET NULL,
nom TEXT NOT NULL,
montant_emprunte REAL NOT NULL,
capital_restant_du REAL NOT NULL,
taux_nominal REAL,
date_debut TEXT,
duree_mois INTEGER,
mensualite REAL,
notes TEXT,
created_at TEXT NOT NULL DEFAULT (datetime('now')),
updated_at TEXT
)
`);
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)');
export default db;