Files
crowdlending-app/_to_delete/extra_actifs_2_migration.py
2026-09-27 15:09:18 +02:00

85 lines
7.4 KiB
Python

path = "backend/src/db/index.js"
with open(path, "r", encoding="utf-8") as f:
content = f.read()
# 1) Insérer la liste des 11 supports (dupliquée localement, hors de portée du AV_PER_SUPPORTS
# du bloc de seed initial) juste avant EXTRA_ENVELOPPES.
anchor_supports = " const EXTRA_ENVELOPPES = [\n"
assert content.count(anchor_supports) == 1, f"anchor_supports count={content.count(anchor_supports)}"
supports_const = """ 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', 'Gestion pilotée / mandat',
'Fonds solidaires (90/10)',
];
"""
content = content.replace(anchor_supports, supports_const + anchor_supports, 1)
# 2) Ajouter les nouvelles enveloppes à la fin du tableau EXTRA_ENVELOPPES : Contrat de
# capitalisation, Assurance-vie luxembourgeoise, Girardin (demande du 29/09/26 sur les
# enveloppes manquantes / actifs "exotiques"), et Wallet crypto conservé par un tiers
# (demande du 29/09/26, distincte de l'auto-conservation et du compte sur exchange).
anchor_last_item = """ { 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 30 % sur les plus-values (ou barème) ; réduction d'IR à l'entrée possible sous conditions (IR-PME/Madelin)", supports: [] },
];"""
assert content.count(anchor_last_item) == 1, f"anchor_last_item count={content.count(anchor_last_item)}"
new_items = """ { 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 30 % sur les plus-values (ou 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 30 % sur les plus-values de cession (particuliers), déclaration 2086", supports: [] },
];"""
content = content.replace(anchor_last_item, new_items, 1)
# 3) Étendre la boucle de traitement pour gérer aussi les supports (jusqu'ici uniquement les
# catégories, car aucune des 7 enveloppes du 29/09/26 n'avait de support).
anchor_loop = """ 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 (?, ?)');
db.transaction(() => {
for (const { nom, description, plafond_eur, regime_fiscal, categories } 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);
}
}
})();
}"""
assert content.count(anchor_loop) == 1, f"anchor_loop count={content.count(anchor_loop)}"
new_loop = """ 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);
}
}
})();
}"""
content = content.replace(anchor_loop, new_loop, 1)
with open(path, "w", encoding="utf-8") as f:
f.write(content)
print("Extension 2 (contrat de capitalisation, AV luxembourgeoise, Girardin, wallet tiers) inserted OK, new length:", len(content))