254 lines
13 KiB
SQL
254 lines
13 KiB
SQL
-- =====================================================================
|
|
-- Crowdlending Tracker - SQLite schema
|
|
-- =====================================================================
|
|
-- Conventions:
|
|
-- * Monetary amounts: REAL (cents-level precision OK for personal use)
|
|
-- * Dates: TEXT in ISO 8601 (YYYY-MM-DD) for stable sort & SQLite date()
|
|
-- * All tables use AUTOINCREMENT-free INTEGER PK (rowid alias)
|
|
-- * created_at/updated_at: TEXT ISO timestamp, set by app
|
|
-- =====================================================================
|
|
|
|
PRAGMA foreign_keys = ON;
|
|
PRAGMA journal_mode = WAL;
|
|
|
|
-- ---------------------------------------------------------------------
|
|
-- USERS (login accounts)
|
|
-- ---------------------------------------------------------------------
|
|
CREATE TABLE IF NOT EXISTS users (
|
|
id INTEGER PRIMARY KEY,
|
|
email TEXT NOT NULL UNIQUE,
|
|
password_hash TEXT NOT NULL,
|
|
display_name TEXT,
|
|
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
);
|
|
|
|
-- ---------------------------------------------------------------------
|
|
-- INVESTISSEURS (profils d'investissement sous un même login)
|
|
-- Ex.: "Monsieur", "Madame", "SCI Croguennec", "PEA-PME", ...
|
|
-- ---------------------------------------------------------------------
|
|
CREATE TABLE IF NOT EXISTS investisseurs (
|
|
id INTEGER PRIMARY KEY,
|
|
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
nom TEXT NOT NULL, -- nom complet (famille) ou raison sociale (entreprise)
|
|
prenom TEXT, -- prénom (famille uniquement)
|
|
type TEXT NOT NULL DEFAULT 'famille'
|
|
CHECK(type IN ('famille','entreprise')),
|
|
type_fiscal TEXT, -- 'PP', 'PM', 'SCI', 'SCPI'...
|
|
notes TEXT,
|
|
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
UNIQUE(user_id, nom)
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_investisseurs_user ON investisseurs(user_id);
|
|
|
|
-- ---------------------------------------------------------------------
|
|
-- PLATEFORMES (ClubFunding, October, Lendix, La Première Brique, ...)
|
|
-- ---------------------------------------------------------------------
|
|
CREATE TABLE IF NOT EXISTS plateformes (
|
|
id INTEGER PRIMARY KEY,
|
|
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
nom TEXT NOT NULL,
|
|
url TEXT,
|
|
notes TEXT,
|
|
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
domiciliation TEXT NOT NULL DEFAULT 'france',
|
|
fiscalite TEXT NOT NULL DEFAULT 'flat_tax',
|
|
taux_fiscalite_locale REAL,
|
|
methode_remboursement TEXT NOT NULL DEFAULT 'portefeuille',
|
|
investisseur_id INTEGER REFERENCES investisseurs(id) ON DELETE SET NULL,
|
|
date_ouverture TEXT,
|
|
logo_filename TEXT,
|
|
UNIQUE(user_id, nom, investisseur_id)
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_plateformes_user ON plateformes(user_id);
|
|
|
|
-- ---------------------------------------------------------------------
|
|
-- DEPOTS_RETRAITS (mouvements de cash sur les plateformes)
|
|
-- type: 'depot' (versement) | 'retrait' (retrait)
|
|
-- ---------------------------------------------------------------------
|
|
CREATE TABLE IF NOT EXISTS depots_retraits (
|
|
id INTEGER PRIMARY KEY,
|
|
investisseur_id INTEGER NOT NULL REFERENCES investisseurs(id) ON DELETE CASCADE,
|
|
plateforme_id INTEGER NOT NULL REFERENCES plateformes(id) ON DELETE CASCADE,
|
|
date_operation TEXT NOT NULL, -- ISO YYYY-MM-DD
|
|
type TEXT NOT NULL CHECK(type IN ('depot','retrait')),
|
|
montant REAL NOT NULL CHECK(montant >= 0),
|
|
libelle TEXT,
|
|
reference TEXT, -- ref bancaire / plateforme
|
|
source TEXT NOT NULL DEFAULT 'manuel', -- 'manuel' | 'import_excel'
|
|
notes TEXT,
|
|
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_depret_inv ON depots_retraits(investisseur_id);
|
|
CREATE INDEX IF NOT EXISTS idx_depret_plat ON depots_retraits(plateforme_id);
|
|
CREATE INDEX IF NOT EXISTS idx_depret_date ON depots_retraits(date_operation);
|
|
|
|
-- ---------------------------------------------------------------------
|
|
-- INVESTISSEMENTS (CF Investissements - liste des projets souscrits)
|
|
-- statut: 'en_cours' | 'rembourse' | 'en_retard' | 'procedure' | 'cloture' | 'perte_definitive' | 'prolongation'
|
|
-- ---------------------------------------------------------------------
|
|
CREATE TABLE IF NOT EXISTS investissements (
|
|
id INTEGER PRIMARY KEY,
|
|
investisseur_id INTEGER NOT NULL REFERENCES investisseurs(id) ON DELETE CASCADE,
|
|
plateforme_id INTEGER NOT NULL REFERENCES plateformes(id) ON DELETE CASCADE,
|
|
nom_projet TEXT NOT NULL,
|
|
emetteur TEXT, -- nom de la société emprunteuse
|
|
date_souscription TEXT NOT NULL,
|
|
date_premiere_echeance TEXT, -- date de la 1ère échéance (intérêts / remboursement)
|
|
date_cible TEXT, -- date contractuelle du dernier versement (calculée)
|
|
montant_investi REAL NOT NULL CHECK(montant_investi > 0),
|
|
taux_interet REAL, -- en % annuel (ex. 9.5)
|
|
duree_mois INTEGER,
|
|
type_remb TEXT, -- 'in_fine' | 'amortissable' | 'differe'
|
|
freq_interets TEXT NOT NULL DEFAULT 'mensuel', -- 'mensuel' | 'trimestriel' | 'in_fine'
|
|
statut TEXT NOT NULL DEFAULT 'en_cours'
|
|
CHECK(statut IN ('en_cours','rembourse','en_retard','procedure','cloture','perte_definitive','prolongation')),
|
|
reference TEXT, -- ID projet sur la plateforme
|
|
source TEXT NOT NULL DEFAULT 'manuel',
|
|
notes TEXT,
|
|
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_inv_inv ON investissements(investisseur_id);
|
|
CREATE INDEX IF NOT EXISTS idx_inv_plat ON investissements(plateforme_id);
|
|
CREATE INDEX IF NOT EXISTS idx_inv_statut ON investissements(statut);
|
|
CREATE INDEX IF NOT EXISTS idx_inv_date ON investissements(date_souscription);
|
|
|
|
-- ---------------------------------------------------------------------
|
|
-- REMBOURSEMENTS (échéances perçues, réelles)
|
|
-- ---------------------------------------------------------------------
|
|
CREATE TABLE IF NOT EXISTS remboursements (
|
|
id INTEGER PRIMARY KEY,
|
|
investissement_id INTEGER NOT NULL REFERENCES investissements(id) ON DELETE CASCADE,
|
|
date_remb TEXT NOT NULL,
|
|
capital REAL NOT NULL DEFAULT 0,
|
|
interets_bruts REAL NOT NULL DEFAULT 0, -- intérêts AVANT prélèvements
|
|
prelev_sociaux REAL NOT NULL DEFAULT 0, -- 17.2 % typiquement
|
|
prelev_forfaitaire REAL NOT NULL DEFAULT 0, -- 12.8 % (PFU IR)
|
|
cashback REAL NOT NULL DEFAULT 0, -- remboursement non taxé (bonus plateforme, etc.)
|
|
interets_nets REAL NOT NULL DEFAULT 0, -- interets_bruts - prelev_sociaux - prelev_forfaitaire
|
|
net_recu REAL NOT NULL DEFAULT 0, -- capital + cashback + interets_nets
|
|
statut TEXT NOT NULL DEFAULT 'paye'
|
|
CHECK(statut IN ('paye','retard','partiel','impaye')),
|
|
source TEXT NOT NULL DEFAULT 'manuel',
|
|
notes TEXT,
|
|
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_remb_inv ON remboursements(investissement_id);
|
|
CREATE INDEX IF NOT EXISTS idx_remb_date ON remboursements(date_remb);
|
|
CREATE INDEX IF NOT EXISTS idx_remb_statut ON remboursements(statut);
|
|
|
|
-- ---------------------------------------------------------------------
|
|
-- SIMUL_REMBOURSEMENTS (échéances prévisionnelles / théoriques)
|
|
-- ---------------------------------------------------------------------
|
|
CREATE TABLE IF NOT EXISTS simul_remboursements (
|
|
id INTEGER PRIMARY KEY,
|
|
investissement_id INTEGER NOT NULL REFERENCES investissements(id) ON DELETE CASCADE,
|
|
numero_echeance INTEGER NOT NULL,
|
|
date_prevue TEXT NOT NULL,
|
|
capital_prevu REAL NOT NULL DEFAULT 0,
|
|
interets_prevus REAL NOT NULL DEFAULT 0,
|
|
total_prevu REAL NOT NULL DEFAULT 0,
|
|
notes TEXT,
|
|
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
UNIQUE(investissement_id, numero_echeance)
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_simul_inv ON simul_remboursements(investissement_id);
|
|
CREATE INDEX IF NOT EXISTS idx_simul_date ON simul_remboursements(date_prevue);
|
|
|
|
-- ---------------------------------------------------------------------
|
|
-- IMPORTS (historique des imports Excel pour traçabilité)
|
|
-- ---------------------------------------------------------------------
|
|
CREATE TABLE IF NOT EXISTS imports (
|
|
id INTEGER PRIMARY KEY,
|
|
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
investisseur_id INTEGER REFERENCES investisseurs(id) ON DELETE SET NULL,
|
|
module TEXT NOT NULL, -- 'depots_retraits' | 'investissements' | ...
|
|
filename TEXT NOT NULL,
|
|
rows_total INTEGER NOT NULL DEFAULT 0,
|
|
rows_inserted INTEGER NOT NULL DEFAULT 0,
|
|
rows_skipped INTEGER NOT NULL DEFAULT 0,
|
|
mapping_json TEXT, -- JSON: mappage colonnes
|
|
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_imports_user ON imports(user_id);
|
|
|
|
-- ---------------------------------------------------------------------
|
|
-- CATEGORIES_PLATEFORME (tags libres associés à une plateforme)
|
|
-- Semées par défaut à la première utilisation (voir route /categories)
|
|
-- ---------------------------------------------------------------------
|
|
CREATE TABLE IF NOT EXISTS categories_plateforme (
|
|
id INTEGER PRIMARY KEY,
|
|
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
nom TEXT NOT NULL,
|
|
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
UNIQUE(user_id, nom)
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_catplat_user ON categories_plateforme(user_id);
|
|
|
|
-- Junction : une plateforme peut avoir plusieurs catégories
|
|
CREATE TABLE IF NOT EXISTS plateforme_categories (
|
|
plateforme_id INTEGER NOT NULL REFERENCES plateformes(id) ON DELETE CASCADE,
|
|
categorie_id INTEGER NOT NULL REFERENCES categories_plateforme(id) ON DELETE CASCADE,
|
|
PRIMARY KEY(plateforme_id, categorie_id)
|
|
);
|
|
|
|
-- ---------------------------------------------------------------------
|
|
-- TAUX_PFU (historique Flat Tax France — table de référence globale)
|
|
-- ---------------------------------------------------------------------
|
|
CREATE TABLE IF NOT EXISTS taux_pfu (
|
|
id INTEGER PRIMARY KEY,
|
|
annee INTEGER NOT NULL UNIQUE,
|
|
pfu_total REAL NOT NULL, -- ex. 30.0
|
|
impot_revenu REAL NOT NULL, -- ex. 12.8
|
|
prelev_sociaux REAL NOT NULL, -- ex. 17.2
|
|
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_pfu_annee ON taux_pfu(annee);
|
|
|
|
-- ---------------------------------------------------------------------
|
|
-- VUES utiles
|
|
-- ---------------------------------------------------------------------
|
|
|
|
-- Solde courant par (investisseur, plateforme)
|
|
CREATE VIEW IF NOT EXISTS v_solde_plateforme AS
|
|
SELECT
|
|
dr.investisseur_id,
|
|
dr.plateforme_id,
|
|
SUM(CASE WHEN dr.type='depot' THEN dr.montant ELSE 0 END) AS total_depots,
|
|
SUM(CASE WHEN dr.type='retrait' THEN dr.montant ELSE 0 END) AS total_retraits,
|
|
SUM(CASE WHEN dr.type='depot' THEN dr.montant
|
|
WHEN dr.type='retrait' THEN -dr.montant END) AS solde_net
|
|
FROM depots_retraits dr
|
|
GROUP BY dr.investisseur_id, dr.plateforme_id;
|
|
|
|
-- Synthèse investissements par investisseur
|
|
CREATE VIEW IF NOT EXISTS v_synthese_inv AS
|
|
SELECT
|
|
i.investisseur_id,
|
|
COUNT(*) AS nb_projets,
|
|
SUM(i.montant_investi) AS total_investi,
|
|
SUM(CASE WHEN i.statut='en_cours' THEN i.montant_investi ELSE 0 END) AS encours,
|
|
SUM(CASE WHEN i.statut='rembourse' THEN i.montant_investi ELSE 0 END) AS rembourse,
|
|
SUM(CASE WHEN i.statut IN ('en_retard','procedure') THEN i.montant_investi ELSE 0 END) AS en_defaut
|
|
FROM investissements i
|
|
GROUP BY i.investisseur_id;
|
|
|
|
-- Intérêts perçus par année (pour le 2778-SD)
|
|
CREATE VIEW IF NOT EXISTS v_interets_annuels AS
|
|
SELECT
|
|
i.investisseur_id,
|
|
substr(r.date_remb,1,4) AS annee,
|
|
SUM(r.interets_bruts) AS interets_bruts,
|
|
SUM(r.prelev_sociaux) AS prelev_sociaux,
|
|
SUM(r.prelev_forfaitaire) AS prelev_forfaitaire,
|
|
SUM(r.net_recu) AS net_recu
|
|
FROM remboursements r
|
|
JOIN investissements i ON i.id = r.investissement_id
|
|
WHERE r.statut IN ('paye','partiel')
|
|
GROUP BY i.investisseur_id, substr(r.date_remb,1,4);
|