maj
This commit is contained in:
1 parent
1e3994f17f
commit
624d32ff7c
17 files changed
+2192
-1
No files matched your search
@@ -3669,4 +3669,421 @@ db.exec(`
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ── Chantier "Référentiel Enveloppes & Supports d'épargne" (28/09/26, demande Olivier) ──────
|
||||
// Catalogue admin des enveloppes fiscales/comptes d'épargne (Livret A, Assurance-vie, PEA,
|
||||
// PER...) et des supports d'investissement qu'on peut y loger (fonds euros, ETF, SCPI...),
|
||||
// avec un lien N-N entre les deux référentiels (quels supports sont disponibles dans quelle
|
||||
// enveloppe) — cadrage validé par Olivier via AskUserQuestion (28/09/26) :
|
||||
// - Deux référentiels reliés (pas un seul catalogue plat) : enveloppes_referentiel et
|
||||
// supports_referentiel, liés par enveloppes_supports_disponibles.
|
||||
// - Catalogue seul pour l'instant, sans rattachement à l'existant (comptes, investissements...)
|
||||
// — prépare le futur module patrimoine, déjà noté comme "Phase 3" dans le chantier
|
||||
// Institutions financières (cf. claude/plan_referentiel_institutions_financieres.md).
|
||||
// - Métadonnées enrichies sur les enveloppes : description, plafond (€) et régime fiscal
|
||||
// résumé, en plus du nom et des catégories (familles).
|
||||
// Une enveloppe est transverse à tous les espaces de travail, comme les institutions
|
||||
// financières (même décision : ce ne sont pas des objets propres à un workspace).
|
||||
db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS enveloppes_referentiel (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
nom TEXT NOT NULL UNIQUE,
|
||||
description TEXT,
|
||||
plafond_eur REAL,
|
||||
regime_fiscal TEXT,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||||
)
|
||||
`);
|
||||
db.exec('CREATE INDEX IF NOT EXISTS idx_envref_nom ON enveloppes_referentiel(nom)');
|
||||
|
||||
// Catégories (familles) des enveloppes — mêmes 10 regroupements que la liste fournie par
|
||||
// Olivier (Comptes courants, Épargne réglementée...). Gérées par l'admin, comme
|
||||
// categories_institutions : table dédiée plutôt que réutiliser categories_inv/categories_institutions,
|
||||
// une famille d'enveloppe d'épargne n'ayant de sens ni pour une plateforme crowdlending/PE ni
|
||||
// pour une institution financière.
|
||||
db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS categories_enveloppes (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
nom TEXT NOT NULL UNIQUE,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||||
)
|
||||
`);
|
||||
db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS referentiel_categories_enveloppes (
|
||||
enveloppe_id INTEGER NOT NULL REFERENCES enveloppes_referentiel(id) ON DELETE CASCADE,
|
||||
categorie_id INTEGER NOT NULL REFERENCES categories_enveloppes(id) ON DELETE CASCADE,
|
||||
PRIMARY KEY (enveloppe_id, categorie_id)
|
||||
)
|
||||
`);
|
||||
|
||||
// Référentiel des supports d'investissement (fonds euros, ETF, SCPI...) — catalogue à plat,
|
||||
// pas de sous-catégories (une dizaine d'entrées seulement, cf. "Types de supports (UC)
|
||||
// disponibles" dans la liste d'Olivier).
|
||||
db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS supports_referentiel (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
nom TEXT NOT NULL UNIQUE,
|
||||
description TEXT,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||||
)
|
||||
`);
|
||||
db.exec('CREATE INDEX IF NOT EXISTS idx_suppref_nom ON supports_referentiel(nom)');
|
||||
|
||||
// Lien N-N : quels supports sont disponibles dans quelle enveloppe (ex. Assurance-vie
|
||||
// multisupport ↔ fonds euros, ETF, SCPI...). Une enveloppe sans aucune ligne ici (Livret A,
|
||||
// PEL, Compte courant...) est une enveloppe "cash" : pas de choix de support, juste un solde.
|
||||
db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS enveloppes_supports_disponibles (
|
||||
enveloppe_id INTEGER NOT NULL REFERENCES enveloppes_referentiel(id) ON DELETE CASCADE,
|
||||
support_id INTEGER NOT NULL REFERENCES supports_referentiel(id) ON DELETE CASCADE,
|
||||
PRIMARY KEY (enveloppe_id, support_id)
|
||||
)
|
||||
`);
|
||||
|
||||
// Seed des catégories (familles) — idempotent, n'insère que si la table est vide (l'admin
|
||||
// reste libre de renommer/supprimer ensuite depuis l'écran dédié, comme pour les institutions).
|
||||
{
|
||||
const catEnvCount = db.prepare('SELECT COUNT(*) AS n FROM categories_enveloppes').get().n;
|
||||
if (catEnvCount === 0) {
|
||||
const insCatEnv = db.prepare('INSERT OR IGNORE INTO categories_enveloppes (nom) VALUES (?)');
|
||||
db.transaction(() => {
|
||||
for (const nom of [
|
||||
'Comptes courants',
|
||||
'Épargne réglementée',
|
||||
'Épargne bancaire non réglementée',
|
||||
'Assurance-vie',
|
||||
'Épargne retraite',
|
||||
'Épargne salariale',
|
||||
'Enveloppes titres / marchés financiers',
|
||||
'Immobilier papier',
|
||||
'Financement participatif / private equity',
|
||||
'Épargne solidaire',
|
||||
]) insCatEnv.run(nom);
|
||||
})();
|
||||
}
|
||||
}
|
||||
|
||||
// Seed des supports d'investissement (idempotent).
|
||||
{
|
||||
const suppRefCount = db.prepare('SELECT COUNT(*) AS n FROM supports_referentiel').get().n;
|
||||
if (suppRefCount === 0) {
|
||||
const SEED_SUPPORTS = [
|
||||
{ nom: 'Fonds en euros', description: "Fonds en euros classique ou nouvelle génération (eurocroissance) — capital garanti, ou partiellement pour l'eurocroissance." },
|
||||
{ nom: 'OPCVM / fonds actifs', description: 'Fonds gérés activement — actions, obligations, diversifiés...' },
|
||||
{ nom: 'ETF / trackers', description: 'Fonds indiciels cotés répliquant un indice.' },
|
||||
{ nom: 'Titres vifs (actions)', description: 'Actions détenues en direct.' },
|
||||
{ nom: 'Obligations / fonds datés', description: 'Obligations en direct ou fonds obligataires à échéance.' },
|
||||
{ nom: 'Immobilier papier (SCPI/SCI/OPCI)', description: "Parts de SCPI, SCI ou OPCI logées dans une enveloppe, par opposition à la détention en direct (cf. enveloppes SCPI/OPCI/SCI dédiées)." },
|
||||
{ nom: 'Private equity (FCPR)', description: 'Fonds de capital-investissement/capital-risque, logeable en assurance-vie ou PER.' },
|
||||
{ nom: 'Produits structurés', description: "Produits à formule, profil rendement/risque défini à l'avance." },
|
||||
{ nom: 'Matières premières / métaux précieux', description: 'Or et autres matières premières, via trackers ou supports dédiés.' },
|
||||
{ nom: 'Fonds solidaires (90/10)', description: "Fonds investissant 5 à 10 % dans l'économie solidaire — souvent logés en PEE ou assurance-vie." },
|
||||
];
|
||||
const insSupp = db.prepare('INSERT OR IGNORE INTO supports_referentiel (nom, description) VALUES (?, ?)');
|
||||
db.transaction(() => { for (const s of SEED_SUPPORTS) insSupp.run(s.nom, s.description); })();
|
||||
}
|
||||
}
|
||||
|
||||
// Seed des enveloppes + liens catégories + liens supports disponibles (idempotent) —
|
||||
// transcription de la liste fournie par Olivier le 28/09/26, dédupliquée (le PERCOL n'est
|
||||
// listé qu'une fois mais porte les deux catégories Épargne retraite + Épargne salariale ; le
|
||||
// FCPR et l'immobilier "papier" logé en enveloppe existent uniquement comme SUPPORT quand ils
|
||||
// sont hébergés dans une assurance-vie/PER, et comme ENVELOPPE à part quand ils sont détenus en
|
||||
// direct). La liste enveloppe→supports est une proposition par défaut, éditable ensuite depuis
|
||||
// l'écran admin (cases à cocher) — elle ne prétend pas capturer l'offre exacte de chaque contrat.
|
||||
{
|
||||
const envRefCount = db.prepare('SELECT COUNT(*) AS n FROM enveloppes_referentiel').get().n;
|
||||
if (envRefCount === 0) {
|
||||
const AV_PER_SUPPORTS = [
|
||||
'Fonds en euros', 'OPCVM / fonds actifs', 'ETF / trackers', 'Titres vifs (actions)',
|
||||
'Obligations / fonds datés', 'Immobilier papier (SCPI/SCI/OPCI)', 'Private equity (FCPR)',
|
||||
'Produits structurés', 'Matières premières / métaux précieux',
|
||||
'Fonds solidaires (90/10)',
|
||||
];
|
||||
const SEED_ENVELOPPES = [
|
||||
// 0. Comptes courants
|
||||
{ nom: 'Compte courant non rémunéré', categories: ['Comptes courants'], description: 'La norme en France — disponibilité totale, aucun risque, aucun rendement.', plafond_eur: null, regime_fiscal: null, supports: [] },
|
||||
{ nom: 'Compte courant rémunéré', categories: ['Comptes courants'], description: 'Proposé par certaines banques en ligne ou offres promotionnelles, taux faible.', plafond_eur: null, regime_fiscal: 'Flat tax (PFU)', supports: [] },
|
||||
{ nom: 'Compte rémunéré nouvelle génération (fintech)', categories: ['Comptes courants'], description: "« Super livret » nouvelle génération — néobanques/fintechs (Cashbee, Ramify Rendement...), souvent sans plafond, liquidité immédiate.", plafond_eur: null, regime_fiscal: 'Flat tax (PFU)', supports: [] },
|
||||
// 1. Épargne réglementée
|
||||
{ nom: 'Livret A', categories: ['Épargne réglementée'], description: null, plafond_eur: 22950, regime_fiscal: 'Exonéré (IR et prélèvements sociaux)', supports: [] },
|
||||
{ nom: 'LDDS', categories: ['Épargne réglementée'], description: 'Livret de Développement Durable et Solidaire.', plafond_eur: 12000, regime_fiscal: 'Exonéré (IR et prélèvements sociaux)', supports: [] },
|
||||
{ nom: 'LEP', categories: ['Épargne réglementée'], description: "Livret d'Épargne Populaire — sous conditions de revenus, taux généralement plus élevé.", plafond_eur: 10000, regime_fiscal: 'Exonéré (IR et prélèvements sociaux)', supports: [] },
|
||||
{ nom: 'Livret Jeune', categories: ['Épargne réglementée'], description: '12-25 ans.', plafond_eur: 1600, regime_fiscal: 'Exonéré (IR et prélèvements sociaux)', supports: [] },
|
||||
{ nom: 'CEL', categories: ['Épargne réglementée'], description: 'Compte Épargne Logement — droits à prêt immobilier.', plafond_eur: 15300, regime_fiscal: null, supports: [] },
|
||||
{ nom: 'PEL', categories: ['Épargne réglementée'], description: 'Plan Épargne Logement — droits à prêt immobilier.', plafond_eur: 61200, regime_fiscal: null, supports: [] },
|
||||
// 2. Épargne bancaire non réglementée
|
||||
{ nom: 'Livret bancaire non réglementé', categories: ['Épargne bancaire non réglementée'], description: 'Livret « boosté », taux libre.', plafond_eur: null, regime_fiscal: 'Flat tax (PFU) ou option barème', supports: [] },
|
||||
{ nom: 'Compte à terme (CAT)', categories: ['Épargne bancaire non réglementée'], description: 'Taux garanti sur une durée fixe.', plafond_eur: null, regime_fiscal: 'Flat tax (PFU) ou option barème', supports: [] },
|
||||
// 3. Assurance-vie
|
||||
{ nom: 'Assurance-vie monosupport (fonds euros)', categories: ['Assurance-vie'], description: 'Capital garanti.', plafond_eur: null, regime_fiscal: 'Avantageux après 8 ans (abattement annuel + PFU réduit), cadre privilégié pour la transmission', supports: ['Fonds en euros'] },
|
||||
{ nom: 'Assurance-vie multisupport', categories: ['Assurance-vie'], description: 'Fonds euros + unités de compte.', plafond_eur: null, regime_fiscal: 'Avantageux après 8 ans (abattement annuel + PFU réduit), cadre privilégié pour la transmission', supports: AV_PER_SUPPORTS },
|
||||
// 4. Épargne retraite
|
||||
{ nom: 'PER individuel', categories: ['Épargne retraite'], description: 'Ex-PERP, Madelin — sortie en capital ou rente.', plafond_eur: null, regime_fiscal: "Déduction fiscale à l'entrée, sortie imposée (capital ou rente)", supports: AV_PER_SUPPORTS },
|
||||
{ nom: 'PER collectif / obligatoire (PERCOL, article 83)', categories: ['Épargne retraite', 'Épargne salariale'], description: "Via l'entreprise.", plafond_eur: null, regime_fiscal: "Déduction fiscale à l'entrée, sortie imposée (capital ou rente)", supports: AV_PER_SUPPORTS },
|
||||
// 5. Épargne salariale
|
||||
{ nom: 'PEE', categories: ['Épargne salariale'], description: "Plan d'Épargne Entreprise — abondement possible, blocage 5 ans.", plafond_eur: null, regime_fiscal: "Exonéré d'IR sur les plus-values si blocage 5 ans respecté (hors prélèvements sociaux)", supports: ['OPCVM / fonds actifs', 'ETF / trackers', 'Titres vifs (actions)', 'Fonds solidaires (90/10)'] },
|
||||
// 6. Enveloppes titres / marchés financiers
|
||||
{ nom: 'PEA', categories: ['Enveloppes titres / marchés financiers'], description: 'Actions européennes.', plafond_eur: 150000, regime_fiscal: "Exonéré d'IR après 5 ans (hors prélèvements sociaux)", supports: ['Titres vifs (actions)', 'ETF / trackers'] },
|
||||
{ nom: 'PEA-PME', categories: ['Enveloppes titres / marchés financiers'], description: 'Dédié aux PME/ETI, cumulable avec le PEA.', plafond_eur: 225000, regime_fiscal: "Exonéré d'IR après 5 ans (hors prélèvements sociaux)", supports: ['Titres vifs (actions)', 'ETF / trackers'] },
|
||||
{ nom: 'Compte-titres ordinaire (CTO)', categories: ['Enveloppes titres / marchés financiers'], description: "Sans plafond, sans avantage fiscal, tous types d'actifs.", plafond_eur: null, regime_fiscal: "Flat tax (PFU) ou option barème, pas d'avantage fiscal", supports: ['Titres vifs (actions)', 'ETF / trackers', 'Obligations / fonds datés', 'Produits structurés', 'Matières premières / métaux précieux', 'OPCVM / fonds actifs'] },
|
||||
// 7. Immobilier papier
|
||||
{ nom: 'SCPI (détenue en direct ou à crédit)', categories: ['Immobilier papier'], description: 'Peut aussi être logée en assurance-vie/PER — cf. le support « Immobilier papier ».', plafond_eur: null, regime_fiscal: 'Revenus fonciers, fiscalité classique', supports: [] },
|
||||
{ nom: 'OPCI', categories: ['Immobilier papier'], description: 'Plus liquide que la SCPI — part immobilière + actifs financiers.', plafond_eur: null, regime_fiscal: 'Fiscalité classique selon la poche (foncière/financière)', supports: [] },
|
||||
{ nom: 'SCI', categories: ['Immobilier papier'], description: 'Support immobilier collectif, souvent logé en assurance-vie.', plafond_eur: null, regime_fiscal: "Fiscalité classique (ou celle de l'assurance-vie si logée dedans)", supports: [] },
|
||||
// 8. Financement participatif / private equity
|
||||
{ nom: 'Crowdfunding immobilier', categories: ['Financement participatif / private equity'], description: 'Hors enveloppe fiscale dédiée.', plafond_eur: null, regime_fiscal: 'Fiscalité classique sur les intérêts (flat tax / PFU, ou option barème)', supports: [] },
|
||||
{ nom: 'Crowdequity', categories: ['Financement participatif / private equity'], description: 'Prise de participation dans des entreprises, éligible parfois au PEA-PME.', plafond_eur: null, regime_fiscal: 'Fiscalité classique sur les plus-values (sauf logement en PEA-PME)', supports: [] },
|
||||
{ nom: 'FCPI / FIP', categories: ['Financement participatif / private equity'], description: 'Risque élevé, blocage long.', plafond_eur: null, regime_fiscal: "Réduction d'IR à l'entrée", supports: [] },
|
||||
// 9. Épargne solidaire
|
||||
{ nom: 'Livret solidaire', categories: ['Épargne solidaire'], description: null, plafond_eur: null, regime_fiscal: 'Selon le support (réglementé ou non)', supports: [] },
|
||||
];
|
||||
const insEnvRef = db.prepare('INSERT OR IGNORE INTO enveloppes_referentiel (nom, description, plafond_eur, regime_fiscal) VALUES (?, ?, ?, ?)');
|
||||
const getEnvRefId = db.prepare('SELECT id FROM enveloppes_referentiel WHERE nom = ?');
|
||||
const getCatEnvId = db.prepare('SELECT id FROM categories_enveloppes WHERE nom = ?');
|
||||
const linkEnvCat = db.prepare('INSERT OR IGNORE INTO referentiel_categories_enveloppes (enveloppe_id, categorie_id) VALUES (?, ?)');
|
||||
const getSuppRefId = db.prepare('SELECT id FROM supports_referentiel WHERE nom = ?');
|
||||
const linkEnvSupp = db.prepare('INSERT OR IGNORE INTO enveloppes_supports_disponibles (enveloppe_id, support_id) VALUES (?, ?)');
|
||||
db.transaction(() => {
|
||||
for (const { nom, description, plafond_eur, regime_fiscal, categories, supports } of SEED_ENVELOPPES) {
|
||||
insEnvRef.run(nom, description ?? null, plafond_eur ?? null, regime_fiscal ?? null);
|
||||
const env = getEnvRefId.get(nom);
|
||||
if (!env) continue;
|
||||
for (const catNom of categories) {
|
||||
const cat = getCatEnvId.get(catNom);
|
||||
if (cat) linkEnvCat.run(env.id, cat.id);
|
||||
}
|
||||
for (const suppNom of supports) {
|
||||
const supp = getSuppRefId.get(suppNom);
|
||||
if (supp) linkEnvSupp.run(env.id, supp.id);
|
||||
}
|
||||
}
|
||||
})();
|
||||
}
|
||||
}
|
||||
|
||||
// Backfill idempotent (29/09/26, demande Olivier) : plafonds réglementaires CEL/PEL, absents du
|
||||
// cadrage initial (non communiqués par Olivier, donc laissés à null pour ne rien inventer).
|
||||
// Toujours exécuté (pas de garde COUNT()===0) mais ne touche que plafond_eur IS NULL, pour ne
|
||||
// jamais écraser une valeur déjà éditée manuellement depuis l'écran admin.
|
||||
db.prepare(`
|
||||
UPDATE enveloppes_referentiel SET plafond_eur = 15300, updated_at = datetime('now')
|
||||
WHERE nom = 'CEL' AND plafond_eur IS NULL
|
||||
`).run();
|
||||
db.prepare(`
|
||||
UPDATE enveloppes_referentiel SET plafond_eur = 61200, updated_at = datetime('now')
|
||||
WHERE nom = 'PEL' AND plafond_eur IS NULL
|
||||
`).run();
|
||||
|
||||
// Extension du 29/09/26 (demande Olivier) : "je ne voudrais pas faire un nième référentiel [...]
|
||||
// il faut peut-être que cela soit des catégories supplémentaires même si ce n'a pas tout à fait
|
||||
// le même statut" — plutôt qu'un 3e référentiel séparé pour les actifs "physiques"/hors
|
||||
// enveloppe fiscale (immobilier direct, crypto, métaux précieux, objets de collection,
|
||||
// participations non cotées), ils sont intégrés au référentiel existant : quelques nouvelles
|
||||
// catégories (familles) dans categories_enveloppes, et des enveloppes rattachées, sur le même
|
||||
// principe que les enveloppes SCPI/OPCI/SCI détenues en direct déjà présentes (aucun support —
|
||||
// détention directe, pas de menu de supports ; aucun institution_id — catalogue seul, comme le
|
||||
// reste de ce référentiel). Les participations non cotées réutilisent la catégorie existante
|
||||
// "Financement participatif / private equity" plutôt que d'en créer une nouvelle (même famille
|
||||
// que le Crowdequity et les FCPI/FIP). Toujours exécuté (INSERT OR IGNORE, clés UNIQUE sur nom)
|
||||
// — s'ajoute sans écraser à un référentiel déjà seedé, comme le backfill CEL/PEL ci-dessus.
|
||||
{
|
||||
const insCatEnvExtra = db.prepare('INSERT OR IGNORE INTO categories_enveloppes (nom) VALUES (?)');
|
||||
for (const nom of ['Immobilier physique', 'Actifs numériques', 'Métaux précieux', 'Objets de collection', 'Investissement forestier & viticole']) {
|
||||
insCatEnvExtra.run(nom);
|
||||
}
|
||||
|
||||
const AV_PER_SUPPORTS_EXTRA = [
|
||||
'Fonds en euros', 'OPCVM / fonds actifs', 'ETF / trackers', 'Titres vifs (actions)',
|
||||
'Obligations / fonds datés', 'Immobilier papier (SCPI/SCI/OPCI)', 'Private equity (FCPR)',
|
||||
'Produits structurés', 'Matières premières / métaux précieux',
|
||||
'Fonds solidaires (90/10)',
|
||||
];
|
||||
|
||||
const EXTRA_ENVELOPPES = [
|
||||
{ nom: 'Résidence principale', categories: ['Immobilier physique'], description: "Logement occupé à titre de résidence principale.", plafond_eur: null, regime_fiscal: "Plus-value de cession exonérée (résidence principale)", supports: [] },
|
||||
{ nom: 'Résidence secondaire', categories: ['Immobilier physique'], description: "Logement occupé à titre de résidence secondaire (non loué).", plafond_eur: null, regime_fiscal: "Plus-value de cession imposable (abattement selon durée de détention, exonération totale après 22 ans IR / 30 ans prélèvements sociaux)", supports: [] },
|
||||
{ nom: 'Immobilier locatif (détention directe)', categories: ['Immobilier physique'], description: "Bien immobilier loué, détenu en direct (hors SCPI/SCI).", plafond_eur: null, regime_fiscal: "Revenus fonciers (micro-foncier ou réel) + plus-value immobilière (abattement selon durée de détention)", supports: [] },
|
||||
{ nom: 'Compte crypto (exchange)', categories: ['Actifs numériques'], description: "Cryptomonnaies détenues sur une plateforme d'échange centralisée.", plafond_eur: null, regime_fiscal: "Flat tax (PFU) sur les plus-values de cession (particuliers), déclaration 2086", supports: [] },
|
||||
{ nom: 'Wallet crypto (auto-conservation)', categories: ['Actifs numériques'], description: "Cryptomonnaies en auto-conservation (wallet matériel ou logiciel).", plafond_eur: null, regime_fiscal: "Flat tax (PFU) sur les plus-values de cession (particuliers), déclaration 2086", supports: [] },
|
||||
{ nom: 'Métaux précieux physiques', categories: ['Métaux précieux'], description: "Or, argent... détenus physiquement (lingots, pièces).", plafond_eur: null, regime_fiscal: "Taxe forfaitaire sur les métaux précieux, ou option pour le régime des plus-values sur biens meubles si justificatifs d'achat", supports: [] },
|
||||
{ nom: 'Objets de collection', categories: ['Objets de collection'], description: "Montres, art, antiquités...", plafond_eur: null, regime_fiscal: "Taxe forfaitaire sur les objets d'art/collection/antiquité, ou option pour le régime des plus-values sur biens meubles si justificatifs d'achat", supports: [] },
|
||||
{ nom: 'Participation directe non cotée (startups/PME)', categories: ['Financement participatif / private equity'], description: "Prise de participation directe au capital d'une société non cotée, hors plateforme crowdequity.", plafond_eur: null, regime_fiscal: "Flat tax (PFU) sur les plus-values (ou option barème) ; réduction d'IR à l'entrée possible sous conditions (IR-PME/Madelin)", supports: [] },
|
||||
{ nom: 'Contrat de capitalisation', categories: ['Assurance-vie'], description: "Support d'épargne proche de l'assurance-vie (fonds euros + unités de compte), mais sans clause bénéficiaire ni dénouement au décès — entre dans la succession classique. Peut être souscrit par une personne physique ou une personne morale (société, SCI...).", plafond_eur: null, regime_fiscal: "Fiscalité des gains identique à l'assurance-vie pour un particulier (abattement après 8 ans, PFU ou PFL) ; pas de régime de succession spécifique (articles 990 I / 757 B) ; transmissible par donation du vivant, contrairement à l'assurance-vie", supports: AV_PER_SUPPORTS_EXTRA },
|
||||
{ nom: 'Assurance-vie luxembourgeoise', categories: ['Assurance-vie'], description: "Contrat d'assurance-vie souscrit auprès d'un assureur luxembourgeois — triangle de sécurité (actifs ségrégués auprès d'une banque dépositaire distincte de l'assureur), univers d'investissement et devises généralement plus larges qu'un contrat français.", plafond_eur: null, regime_fiscal: "Fiscalité identique à l'assurance-vie française pour un résident fiscal français (abattement après 8 ans, PFU/PFL) ; déclaration du contrat étranger requise (formulaire 3916-3916 bis)", supports: AV_PER_SUPPORTS_EXTRA },
|
||||
{ nom: 'Girardin (industriel / social)', categories: ['Financement participatif / private equity'], description: "Investissement dans un dispositif de défiscalisation outre-mer — financement d'un investissement productif ou de logement social en DOM-TOM, en échange d'une réduction d'impôt immédiate. Capital non récupéré (pas de valeur résiduelle), contrairement à un FCPI/FIP.", plafond_eur: null, regime_fiscal: "Réduction d'IR l'année suivant l'investissement, généralement supérieure au montant investi (effet de levier) ; plafonnement spécifique des niches fiscales outre-mer ; risque de remise en cause fiscale si le monteur/exploitant ne respecte pas ses engagements (durée de conservation 5 ans)", supports: [] },
|
||||
{ nom: 'Wallet crypto (conservation par un tiers)', categories: ['Actifs numériques'], description: "Cryptomonnaies détenues dans un wallet dont les clés sont conservées par un tiers de confiance (service de custody dédié, family office...), par opposition à l'auto-conservation ou à un compte sur une plateforme d'échange.", plafond_eur: null, regime_fiscal: "Flat tax (PFU) sur les plus-values de cession (particuliers), déclaration 2086", supports: [] },
|
||||
{ nom: 'Crowdlending (prêts aux entreprises/PME)', categories: ['Financement participatif / private equity'], description: "Prêt participatif à des entreprises/PME hors immobilier (le coeur de métier de cette application) — distinct de « Crowdfunding immobilier » (prêts adossés à une opération immobilière) et de « Crowdequity » (prise de participation au capital, pas un prêt).", plafond_eur: null, regime_fiscal: "Fiscalité classique sur les intérêts (flat tax / PFU, ou option barème)", supports: [] },
|
||||
{ nom: 'Dette privée (fonds)', categories: ['Financement participatif / private equity'], description: "Fonds de dette privée (private debt) — prêts directs à des entreprises via un véhicule de fonds professionnel, hors plateforme de crowdlending grand public.", plafond_eur: null, regime_fiscal: "Fiscalité classique sur les revenus/plus-values de cession de parts (flat tax / PFU, ou option barème)", supports: [] },
|
||||
{ nom: 'Capital-investissement réservé (FPCI / SLP / FPS)', categories: ['Financement participatif / private equity'], description: "Fonds de capital-investissement réservés aux investisseurs avertis/professionnels — Fonds Professionnel de Capital Investissement (FPCI), Société de Libre Partenariat (SLP), Fonds Professionnel Spécialisé (FPS) ; distinct du FCPR grand public (support disponible en assurance-vie/PER/contrat de capitalisation) et de la participation directe non cotée (prise de participation en direct, hors fonds).", plafond_eur: null, regime_fiscal: "Fiscalité classique sur les plus-values de cession de parts (flat tax / PFU, ou option barème) ; exonération possible sous conditions de durée de détention, comme pour un FCPR", supports: [] },
|
||||
{ nom: 'Infrastructures non cotées', categories: ['Financement participatif / private equity'], description: "Fonds d'investissement dans des actifs d'infrastructure non cotés (énergie, transport, télécoms...).", plafond_eur: null, regime_fiscal: "Fiscalité classique sur les revenus/plus-values de cession de parts (flat tax / PFU, ou option barème)", supports: [] },
|
||||
{ nom: 'Groupement Foncier Forestier (GFF)', categories: ['Investissement forestier & viticole'], description: "Investissement forestier via un groupement foncier forestier — parts donnant une quote-part de bois et forêts en gestion collective.", plafond_eur: null, regime_fiscal: "Réduction d'IR à l'entrée possible sous conditions (dispositif DEFI-Forêt), exonération partielle d'IFI, transmission facilitée sous conditions d'engagement de gestion durable", supports: [] },
|
||||
{ nom: 'Groupement Foncier Viticole (GFV)', categories: ['Investissement forestier & viticole'], description: "Investissement viticole via un groupement foncier viticole — parts donnant une quote-part de vignes louées à un exploitant via un bail rural.", plafond_eur: null, regime_fiscal: "Revenus fonciers (loyers du bail rural), exonération partielle d'IFI et de droits de succession/donation sous conditions", supports: [] },
|
||||
{ nom: 'Immobilier locatif meublé (LMNP/LMP)', categories: ['Immobilier physique'], description: "Location meublée en direct — statut Loueur en Meublé Non Professionnel (LMNP) ou Professionnel (LMP) selon les seuils de recettes et la composition du foyer fiscal ; distinct de « Immobilier locatif (détention directe) » qui suppose une location nue (revenus fonciers).", plafond_eur: null, regime_fiscal: "Revenus imposés en BIC (micro-BIC ou régime réel avec amortissement du bien) ; le statut LMP ouvre en plus à l'imputation des déficits sur le revenu global et à un régime de plus-value professionnelle", supports: [] },
|
||||
{ nom: 'SCPI en démembrement (nue-propriété / usufruit temporaire)', categories: ['Immobilier papier'], description: "SCPI achetée en démembrement de propriété (nue-propriété ou usufruit temporaire), typiquement sur 5 à 10 ans.", plafond_eur: null, regime_fiscal: "Usufruitier : perçoit les loyers et est imposé en revenus fonciers pendant la durée du démembrement. Nu-propriétaire : aucun revenu ni imposition pendant la période, récupère la pleine propriété sans fiscalité à l'extinction ; non redevable de l'IFI sur la valeur du bien démembré pendant la durée (sauf exceptions)", supports: [] },
|
||||
];
|
||||
const insEnvRefExtra = db.prepare('INSERT OR IGNORE INTO enveloppes_referentiel (nom, description, plafond_eur, regime_fiscal) VALUES (?, ?, ?, ?)');
|
||||
const getEnvRefIdExtra = db.prepare('SELECT id FROM enveloppes_referentiel WHERE nom = ?');
|
||||
const getCatEnvIdExtra = db.prepare('SELECT id FROM categories_enveloppes WHERE nom = ?');
|
||||
const linkEnvCatExtra = db.prepare('INSERT OR IGNORE INTO referentiel_categories_enveloppes (enveloppe_id, categorie_id) VALUES (?, ?)');
|
||||
const getSuppRefIdExtra = db.prepare('SELECT id FROM supports_referentiel WHERE nom = ?');
|
||||
const linkEnvSuppExtra = db.prepare('INSERT OR IGNORE INTO enveloppes_supports_disponibles (enveloppe_id, support_id) VALUES (?, ?)');
|
||||
db.transaction(() => {
|
||||
for (const { nom, description, plafond_eur, regime_fiscal, categories, supports } of EXTRA_ENVELOPPES) {
|
||||
insEnvRefExtra.run(nom, description ?? null, plafond_eur ?? null, regime_fiscal ?? null);
|
||||
const env = getEnvRefIdExtra.get(nom);
|
||||
if (!env) continue;
|
||||
for (const catNom of categories) {
|
||||
const cat = getCatEnvIdExtra.get(catNom);
|
||||
if (cat) linkEnvCatExtra.run(env.id, cat.id);
|
||||
}
|
||||
for (const suppNom of (supports || [])) {
|
||||
const supp = getSuppRefIdExtra.get(suppNom);
|
||||
if (supp) linkEnvSuppExtra.run(env.id, supp.id);
|
||||
}
|
||||
}
|
||||
})();
|
||||
}
|
||||
|
||||
// Extension du 29/09/26 (demande Olivier, à propos de produits Spiko/Regular Finance) : "sais-tu
|
||||
// comment les classifier ?" — 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
|
||||
// lorsqu'il est tokenisé sur blockchain comme chez Spiko) : elles ont vocation à rejoindre
|
||||
// institutions_referentiel (catégorie à créer, ex. "Gestion de trésorerie"), pas
|
||||
// enveloppes_referentiel — le rattachement viendra en Phase 3. Ce qui manquait réellement à CE
|
||||
// référentiel, c'est le SUPPORT lui-même : "OPCVM / fonds actifs" existant désigne des fonds
|
||||
// actions/obligations/diversifiés gérés activement, pas un fonds monétaire (quasi-cash, duration
|
||||
// très courte, profil de risque très différent) — les deux ne doivent pas être confondus.
|
||||
// Toujours exécuté (INSERT OR IGNORE, clés UNIQUE sur nom), même principe que les extensions
|
||||
// précédentes.
|
||||
{
|
||||
db.prepare("INSERT OR IGNORE INTO supports_referentiel (nom, description) VALUES (?, ?)").run(
|
||||
'Fonds monétaires (OPCVM monétaire)',
|
||||
"Fonds monétaire classique ou tokenisé sur blockchain (ex. Spiko, Regular Finance) — quasi-cash, duration très courte, distinct d'un OPCVM actions/obligations/diversifié."
|
||||
);
|
||||
const getSuppRefIdMonetaire = db.prepare('SELECT id FROM supports_referentiel WHERE nom = ?');
|
||||
const suppMonetaire = getSuppRefIdMonetaire.get('Fonds monétaires (OPCVM monétaire)');
|
||||
if (suppMonetaire) {
|
||||
const getEnvRefIdMonetaire = db.prepare('SELECT id FROM enveloppes_referentiel WHERE nom = ?');
|
||||
const linkEnvSuppMonetaire = db.prepare('INSERT OR IGNORE INTO enveloppes_supports_disponibles (enveloppe_id, support_id) VALUES (?, ?)');
|
||||
// Enveloppes où un fonds monétaire a sa place (menu de supports déjà large, cohérent avec un
|
||||
// placement défensif) — PEA/PEA-PME exclus (non éligibles réglementairement), enveloppes
|
||||
// "cash" (Livret A, comptes courants...) exclues (déjà sans menu de supports par nature).
|
||||
for (const envNom of [
|
||||
'Compte-titres ordinaire (CTO)', 'Assurance-vie multisupport', 'PER individuel',
|
||||
'PER collectif / obligatoire (PERCOL, article 83)', 'Contrat de capitalisation',
|
||||
'Assurance-vie luxembourgeoise',
|
||||
]) {
|
||||
const env = getEnvRefIdMonetaire.get(envNom);
|
||||
if (env) linkEnvSuppMonetaire.run(env.id, suppMonetaire.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Correctif du 27/09/26 (demande Olivier : "Tu alimente la description avec un flat tax à
|
||||
// 30%, mais ce n'est plus vrai en 2026... évite de mettre le taux actuel qui peut varier") —
|
||||
// les taux d'imposition précis (flat tax 30 %, taxes forfaitaires 11 %/6 %...) changent dans
|
||||
// le temps et rien ici ne les tient à jour : les textes ci-dessus (SEED_ENVELOPPES /
|
||||
// EXTRA_ENVELOPPES) ont été réécrits pour décrire le MÉCANISME fiscal (flat tax/PFU, taxe
|
||||
// forfaitaire...) sans figer de pourcentage. Comme INSERT OR IGNORE ne corrige pas le texte
|
||||
// d'une ligne déjà présente, ce backfill patche les installations déjà seedées — toujours
|
||||
// exécuté, guardé par l'ancien texte exact pour ne jamais écraser une modification manuelle
|
||||
// faite depuis l'admin (même principe que le backfill des plafonds CEL/PEL).
|
||||
{
|
||||
db.prepare(
|
||||
"UPDATE enveloppes_referentiel SET regime_fiscal = ?, updated_at = datetime('now') " +
|
||||
"WHERE nom = ? AND regime_fiscal = ?"
|
||||
).run('Flat tax (PFU)', 'Compte courant rémunéré', 'Flat tax 30 %');
|
||||
db.prepare(
|
||||
"UPDATE enveloppes_referentiel SET regime_fiscal = ?, updated_at = datetime('now') " +
|
||||
"WHERE nom = ? AND regime_fiscal = ?"
|
||||
).run('Flat tax (PFU)', 'Compte rémunéré nouvelle génération (fintech)', 'Flat tax 30 %');
|
||||
db.prepare(
|
||||
"UPDATE enveloppes_referentiel SET regime_fiscal = ?, updated_at = datetime('now') " +
|
||||
"WHERE nom = ? AND regime_fiscal = ?"
|
||||
).run('Flat tax (PFU) ou option barème', 'Livret bancaire non réglementé', 'Flat tax 30 % (ou option barème)');
|
||||
db.prepare(
|
||||
"UPDATE enveloppes_referentiel SET regime_fiscal = ?, updated_at = datetime('now') " +
|
||||
"WHERE nom = ? AND regime_fiscal = ?"
|
||||
).run('Flat tax (PFU) ou option barème', 'Compte à terme (CAT)', 'Flat tax 30 % (ou option barème)');
|
||||
db.prepare(
|
||||
"UPDATE enveloppes_referentiel SET regime_fiscal = ?, updated_at = datetime('now') " +
|
||||
"WHERE nom = ? AND regime_fiscal = ?"
|
||||
).run('Flat tax (PFU) ou option barème, pas d\'avantage fiscal', 'Compte-titres ordinaire (CTO)', 'Flat tax 30 % (ou option barème), pas d\'avantage fiscal');
|
||||
db.prepare(
|
||||
"UPDATE enveloppes_referentiel SET regime_fiscal = ?, updated_at = datetime('now') " +
|
||||
"WHERE nom = ? AND regime_fiscal = ?"
|
||||
).run('Fiscalité classique sur les intérêts (flat tax / PFU, ou option barème)', 'Crowdfunding immobilier', 'Fiscalité classique sur les intérêts (flat tax 30 % ou barème)');
|
||||
db.prepare(
|
||||
"UPDATE enveloppes_referentiel SET regime_fiscal = ?, updated_at = datetime('now') " +
|
||||
"WHERE nom = ? AND regime_fiscal = ?"
|
||||
).run('Flat tax (PFU) sur les plus-values de cession (particuliers), déclaration 2086', 'Compte crypto (exchange)', 'Flat tax 30 % sur les plus-values de cession (particuliers), déclaration 2086');
|
||||
db.prepare(
|
||||
"UPDATE enveloppes_referentiel SET regime_fiscal = ?, updated_at = datetime('now') " +
|
||||
"WHERE nom = ? AND regime_fiscal = ?"
|
||||
).run('Flat tax (PFU) sur les plus-values de cession (particuliers), déclaration 2086', 'Wallet crypto (auto-conservation)', 'Flat tax 30 % sur les plus-values de cession (particuliers), déclaration 2086');
|
||||
db.prepare(
|
||||
"UPDATE enveloppes_referentiel SET regime_fiscal = ?, updated_at = datetime('now') " +
|
||||
"WHERE nom = ? AND regime_fiscal = ?"
|
||||
).run('Taxe forfaitaire sur les métaux précieux, ou option pour le régime des plus-values sur biens meubles si justificatifs d\'achat', 'Métaux précieux physiques', 'Taxe forfaitaire sur les métaux précieux (11 %), ou option pour le régime des plus-values sur biens meubles si justificatifs d\'achat');
|
||||
db.prepare(
|
||||
"UPDATE enveloppes_referentiel SET regime_fiscal = ?, updated_at = datetime('now') " +
|
||||
"WHERE nom = ? AND regime_fiscal = ?"
|
||||
).run('Taxe forfaitaire sur les objets d\'art/collection/antiquité, ou option pour le régime des plus-values sur biens meubles si justificatifs d\'achat', 'Objets de collection', 'Taxe forfaitaire sur les objets d\'art/collection/antiquité (6 %), ou option pour le régime des plus-values sur biens meubles si justificatifs d\'achat');
|
||||
db.prepare(
|
||||
"UPDATE enveloppes_referentiel SET regime_fiscal = ?, updated_at = datetime('now') " +
|
||||
"WHERE nom = ? AND regime_fiscal = ?"
|
||||
).run('Flat tax (PFU) sur les plus-values (ou option barème) ; réduction d\'IR à l\'entrée possible sous conditions (IR-PME/Madelin)', 'Participation directe non cotée (startups/PME)', 'Flat tax 30 % sur les plus-values (ou barème) ; réduction d\'IR à l\'entrée possible sous conditions (IR-PME/Madelin)');
|
||||
db.prepare(
|
||||
"UPDATE enveloppes_referentiel SET regime_fiscal = ?, updated_at = datetime('now') " +
|
||||
"WHERE nom = ? AND regime_fiscal = ?"
|
||||
).run('Flat tax (PFU) sur les plus-values de cession (particuliers), déclaration 2086', 'Wallet crypto (conservation par un tiers)', 'Flat tax 30 % sur les plus-values de cession (particuliers), déclaration 2086');
|
||||
}
|
||||
|
||||
// Extension du 29/09/26 (suite) — support "ETC" (demande Olivier, à partir d'une capture d'écran
|
||||
// d'un contrat d'assurance-vie distinguant "+200 ETF & 22 ETC" parmi ses supports) : un Exchange
|
||||
// Traded Commodity (ETC) est un instrument coté qui réplique le cours d'une matière première
|
||||
// (or, pétrole...), proche d'un ETF dans sa mécanique de cotation mais sans statut UCITS/OPCVM —
|
||||
// distinct du support "ETF / trackers" (fonds indiciels UCITS) et de "Matières premières / métaux
|
||||
// précieux" (autre modalité d'exposition aux matières premières, ex. en unités de compte
|
||||
// d'assurance-vie). Rattaché aux mêmes enveloppes que "Fonds monétaires" ci-dessus (menu de
|
||||
// supports déjà large) ; PEA/PEA-PME exclus, comme en pratique un ETC matières premières
|
||||
// physiques n'est pas éligible au PEA. Toujours exécuté (INSERT OR IGNORE, clés UNIQUE sur nom).
|
||||
{
|
||||
db.prepare("INSERT OR IGNORE INTO supports_referentiel (nom, description) VALUES (?, ?)").run(
|
||||
'ETC (Exchange Traded Commodities)',
|
||||
"Tracker coté sur une matière première (or, pétrole, panier de matières premières...) — proche de l'ETF dans sa mécanique de cotation, mais sans statut UCITS/OPCVM ; distinct du support « ETF / trackers » et de « Matières premières / métaux précieux »."
|
||||
);
|
||||
const getSuppRefIdEtc = db.prepare('SELECT id FROM supports_referentiel WHERE nom = ?');
|
||||
const suppEtc = getSuppRefIdEtc.get('ETC (Exchange Traded Commodities)');
|
||||
if (suppEtc) {
|
||||
const getEnvRefIdEtc = db.prepare('SELECT id FROM enveloppes_referentiel WHERE nom = ?');
|
||||
const linkEnvSuppEtc = db.prepare('INSERT OR IGNORE INTO enveloppes_supports_disponibles (enveloppe_id, support_id) VALUES (?, ?)');
|
||||
for (const envNom of [
|
||||
'Compte-titres ordinaire (CTO)', 'Assurance-vie multisupport', 'PER individuel',
|
||||
'PER collectif / obligatoire (PERCOL, article 83)', 'Contrat de capitalisation',
|
||||
'Assurance-vie luxembourgeoise',
|
||||
]) {
|
||||
const env = getEnvRefIdEtc.get(envNom);
|
||||
if (env) linkEnvSuppEtc.run(env.id, suppEtc.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Correctif du 29/09/26 (suite) — retrait du support "Gestion pilotée / mandat" (demande
|
||||
// Olivier, à propos d'une capture d'écran distinguant "3 modes de gestion" — libre / pilotée /
|
||||
// mixte — d'un contrat d'assurance-vie) : le "mode de gestion" décrit QUI décide de l'allocation
|
||||
// (le titulaire, ou un gérant délégué selon un mandat), c'est une dimension orthogonale au
|
||||
// SUPPORT (le type d'instrument financier détenu — ETF, actions, SCPI...). Une gestion pilotée
|
||||
// s'applique justement à un panier de vrais supports (fonds euros, fonds datés, SCPI, produits
|
||||
// structurés...), elle n'en est pas un elle-même. Cette ligne datait du seed initial du 28/09/26
|
||||
// et n'aurait pas dû être modélisée comme support ; elle est retirée du référentiel et de la
|
||||
// liste de supports "classiques" AV/PER (voir plus haut). Toujours exécuté et idempotent —
|
||||
// DELETE cascade automatiquement les liens vers cette entrée (enveloppes_supports_disponibles,
|
||||
// ON DELETE CASCADE) ; aucune donnée d'investissement réelle n'est rattachée à ce catalogue à ce
|
||||
// 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();
|
||||
|
||||
export default db;
|
||||
Reference in new issue
Block a user