Maj
This commit is contained in:
1 parent
624d32ff7c
commit
bce89d8112
18 files changed
+663
-28
No files matched your search
@@ -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;
|
||||
@@ -11,19 +11,26 @@ const Schema = z.object({
|
||||
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.
|
||||
enveloppe_id: z.number().int().positive().nullable().optional(),
|
||||
exoneration_fiscale: z.enum(['aucune', 'pfnl_5ans']).default('aucune'),
|
||||
});
|
||||
|
||||
router.get('/', (req, res) => {
|
||||
const rows = db.prepare(`
|
||||
SELECT c.id, c.nom, c.type, c.banque, c.institution_id, c.exoneration_fiscale, c.investisseur_id, c.created_at,
|
||||
SELECT c.id, c.nom, c.type, c.banque, c.institution_id, c.enveloppe_id, c.exoneration_fiscale, c.investisseur_id, 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,
|
||||
ir.logo_filename AS institution_logo_filename, ir.icone_filename AS institution_icone_filename
|
||||
ir.logo_filename AS institution_logo_filename, ir.icone_filename AS institution_icone_filename,
|
||||
er.nom AS enveloppe_nom
|
||||
FROM comptes c
|
||||
LEFT JOIN investisseurs inv ON inv.id = c.investisseur_id
|
||||
LEFT JOIN institutions_referentiel ir ON ir.id = c.institution_id
|
||||
LEFT JOIN enveloppes_referentiel er ON er.id = c.enveloppe_id
|
||||
WHERE c.user_id = ?
|
||||
ORDER BY c.nom
|
||||
`).all(req.user.id);
|
||||
@@ -34,8 +41,8 @@ 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, investisseur_id, exoneration_fiscale) VALUES (?,?,?,?,?,?,?)'
|
||||
).run(req.user.id, body.nom, body.type, body.banque || null, body.institution_id ?? null, body.investisseur_id ?? null, body.exoneration_fiscale ?? 'aucune');
|
||||
'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 });
|
||||
} catch (e) { next(e); }
|
||||
});
|
||||
@@ -44,8 +51,8 @@ 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=?, 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.investisseur_id ?? null, body.exoneration_fiscale ?? 'aucune', req.params.id, req.user.id);
|
||||
`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');
|
||||
res.json({ id: Number(req.params.id), ...body });
|
||||
} catch (e) { next(e); }
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
import { Router } from 'express';
|
||||
import { z } from 'zod';
|
||||
import db from '../db/index.js';
|
||||
import { HttpError } from '../middleware/errorHandler.js';
|
||||
|
||||
/**
|
||||
* routes/emprunts.js — Passif patrimonial (Phase 3c, 28/09/26, demande Olivier). Objet séparé
|
||||
* de comptes/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. Mêmes conventions que comptes.js : scope par user_id, détenteur
|
||||
* (investisseur_id) et établissement prêteur (institution_id) optionnels et nullables.
|
||||
*/
|
||||
|
||||
const router = Router();
|
||||
|
||||
const Schema = z.object({
|
||||
nom: z.string().min(1),
|
||||
investisseur_id: z.number().int().positive().nullable().optional(),
|
||||
institution_id: z.number().int().positive().nullable().optional(),
|
||||
montant_emprunte: z.number().positive(),
|
||||
capital_restant_du: z.number().nonnegative(),
|
||||
taux_nominal: z.number().nullable().optional(),
|
||||
date_debut: z.string().nullable().optional(),
|
||||
duree_mois: z.number().int().positive().nullable().optional(),
|
||||
mensualite: z.number().nullable().optional(),
|
||||
notes: z.string().nullable().optional(),
|
||||
});
|
||||
|
||||
router.get('/', (req, res) => {
|
||||
const rows = db.prepare(`
|
||||
SELECT e.id, e.nom, e.investisseur_id, e.institution_id, e.montant_emprunte, e.capital_restant_du,
|
||||
e.taux_nominal, e.date_debut, e.duree_mois, e.mensualite, e.notes, e.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,
|
||||
ir.logo_filename AS institution_logo_filename, ir.icone_filename AS institution_icone_filename
|
||||
FROM emprunts e
|
||||
LEFT JOIN investisseurs inv ON inv.id = e.investisseur_id
|
||||
LEFT JOIN institutions_referentiel ir ON ir.id = e.institution_id
|
||||
WHERE e.user_id = ?
|
||||
ORDER BY e.nom
|
||||
`).all(req.user.id);
|
||||
res.json(rows);
|
||||
});
|
||||
|
||||
router.post('/', (req, res, next) => {
|
||||
try {
|
||||
const body = Schema.parse(req.body);
|
||||
if (body.capital_restant_du > body.montant_emprunte) {
|
||||
throw new HttpError(400, 'Le capital restant dû ne peut pas dépasser le montant emprunté');
|
||||
}
|
||||
const r = db.prepare(`
|
||||
INSERT INTO emprunts (user_id, nom, investisseur_id, institution_id, montant_emprunte, capital_restant_du, taux_nominal, date_debut, duree_mois, mensualite, notes)
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?)
|
||||
`).run(
|
||||
req.user.id, body.nom, body.investisseur_id ?? null, body.institution_id ?? null,
|
||||
body.montant_emprunte, body.capital_restant_du, body.taux_nominal ?? null,
|
||||
body.date_debut || null, body.duree_mois ?? null, body.mensualite ?? null, body.notes || null,
|
||||
);
|
||||
res.status(201).json({ id: r.lastInsertRowid, ...body });
|
||||
} catch (e) { next(e); }
|
||||
});
|
||||
|
||||
router.put('/:id', (req, res, next) => {
|
||||
try {
|
||||
const body = Schema.parse(req.body);
|
||||
if (body.capital_restant_du > body.montant_emprunte) {
|
||||
throw new HttpError(400, 'Le capital restant dû ne peut pas dépasser le montant emprunté');
|
||||
}
|
||||
const r = db.prepare(`
|
||||
UPDATE emprunts
|
||||
SET nom=?, investisseur_id=?, institution_id=?, montant_emprunte=?, capital_restant_du=?,
|
||||
taux_nominal=?, date_debut=?, duree_mois=?, mensualite=?, notes=?, updated_at=datetime('now')
|
||||
WHERE id=? AND user_id=?
|
||||
`).run(
|
||||
body.nom, body.investisseur_id ?? null, body.institution_id ?? null,
|
||||
body.montant_emprunte, body.capital_restant_du, body.taux_nominal ?? null,
|
||||
body.date_debut || null, body.duree_mois ?? null, body.mensualite ?? null, body.notes || null,
|
||||
req.params.id, req.user.id,
|
||||
);
|
||||
if (r.changes === 0) throw new HttpError(404, 'Not found');
|
||||
res.json({ id: Number(req.params.id), ...body });
|
||||
} catch (e) { next(e); }
|
||||
});
|
||||
|
||||
router.delete('/:id', (req, res, next) => {
|
||||
try {
|
||||
const r = db.prepare('DELETE FROM emprunts WHERE id=? AND user_id=?')
|
||||
.run(req.params.id, req.user.id);
|
||||
if (r.changes === 0) throw new HttpError(404, 'Not found');
|
||||
res.status(204).end();
|
||||
} catch (e) { next(e); }
|
||||
});
|
||||
|
||||
export default router;
|
||||
@@ -25,12 +25,13 @@ const router = Router();
|
||||
// Toutes ces routes sont montées sous requireAdmin dans server.js
|
||||
|
||||
const Schema = z.object({
|
||||
nom: z.string().min(1).max(200),
|
||||
description: z.string().nullable().optional(),
|
||||
plafond_eur: z.number().positive().nullable().optional(),
|
||||
regime_fiscal: z.string().nullable().optional(),
|
||||
categories_ids: z.array(z.number().int()).optional().default([]),
|
||||
supports_ids: z.array(z.number().int()).optional().default([]),
|
||||
nom: z.string().min(1).max(200),
|
||||
description: z.string().nullable().optional(),
|
||||
plafond_eur: z.number().positive().nullable().optional(),
|
||||
regime_fiscal: z.string().nullable().optional(),
|
||||
eligible_compte: z.boolean().optional().default(false),
|
||||
categories_ids: z.array(z.number().int()).optional().default([]),
|
||||
supports_ids: z.array(z.number().int()).optional().default([]),
|
||||
});
|
||||
|
||||
// ── Helpers ────────────────────────────────────────────────────────────────
|
||||
@@ -114,9 +115,9 @@ router.post('/', (req, res, next) => {
|
||||
if (existing) throw new HttpError(409, `Une enveloppe "${data.nom}" existe déjà`);
|
||||
|
||||
const r = db.prepare(`
|
||||
INSERT INTO enveloppes_referentiel (nom, description, plafond_eur, regime_fiscal, updated_at)
|
||||
VALUES (?,?,?,?, datetime('now'))
|
||||
`).run(data.nom, data.description || null, data.plafond_eur ?? null, data.regime_fiscal || null);
|
||||
INSERT INTO enveloppes_referentiel (nom, description, plafond_eur, regime_fiscal, eligible_compte, updated_at)
|
||||
VALUES (?,?,?,?,?, datetime('now'))
|
||||
`).run(data.nom, data.description || null, data.plafond_eur ?? null, data.regime_fiscal || null, data.eligible_compte ? 1 : 0);
|
||||
|
||||
const enveloppeId = r.lastInsertRowid;
|
||||
db.transaction(() => {
|
||||
@@ -144,9 +145,9 @@ router.put('/:id', (req, res, next) => {
|
||||
|
||||
db.prepare(`
|
||||
UPDATE enveloppes_referentiel
|
||||
SET nom=?, description=?, plafond_eur=?, regime_fiscal=?, updated_at=datetime('now')
|
||||
SET nom=?, description=?, plafond_eur=?, regime_fiscal=?, eligible_compte=?, updated_at=datetime('now')
|
||||
WHERE id=?
|
||||
`).run(data.nom, data.description || null, data.plafond_eur ?? null, data.regime_fiscal || null, row.id);
|
||||
`).run(data.nom, data.description || null, data.plafond_eur ?? null, data.regime_fiscal || null, data.eligible_compte ? 1 : 0, row.id);
|
||||
|
||||
db.transaction(() => {
|
||||
saveCategories(row.id, data.categories_ids);
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
import { Router } from 'express';
|
||||
import db from '../db/index.js';
|
||||
|
||||
/**
|
||||
* routes/enveloppesReferentielPublic.js — lecture seule du référentiel "Enveloppes & Supports
|
||||
* d'épargne" pour tous les utilisateurs authentifiés (pas requireAdmin), sur le modèle de
|
||||
* institutionsReferentielPublic.js. Catalogue transverse : on expose ici une LISTE complète
|
||||
* (avec catégories, pour regroupement par familles côté frontend), utilisée par le sélecteur
|
||||
* "Enveloppe" de Paramètres > Mes comptes (Phase 3a, cf. plan de déploiement Enveloppes &
|
||||
* Supports). Le champ `eligible_compte` permet au frontend de ne proposer, dans ce sélecteur,
|
||||
* que le sous-ensemble "compte financier" — cadrage validé par Olivier via AskUserQuestion
|
||||
* (28/09/26) — sans empêcher un futur écran (Phase 3b) de consommer la liste complète.
|
||||
*/
|
||||
|
||||
const router = Router();
|
||||
// Monté sous requireAuth (sans requireAdmin) — lecture seule.
|
||||
|
||||
function attachCategories(rows) {
|
||||
if (rows.length === 0) return rows;
|
||||
const ids = rows.map(r => r.id);
|
||||
const links = db.prepare(`
|
||||
SELECT rc.enveloppe_id, c.id, c.nom
|
||||
FROM referentiel_categories_enveloppes rc
|
||||
JOIN categories_enveloppes c ON c.id = rc.categorie_id
|
||||
WHERE rc.enveloppe_id IN (${ids.map(() => '?').join(',')})
|
||||
ORDER BY c.nom
|
||||
`).all(...ids);
|
||||
const map = {};
|
||||
for (const l of links) {
|
||||
if (!map[l.enveloppe_id]) map[l.enveloppe_id] = [];
|
||||
map[l.enveloppe_id].push({ id: l.id, nom: l.nom });
|
||||
}
|
||||
return rows.map(r => ({ ...r, categories: map[r.id] || [] }));
|
||||
}
|
||||
|
||||
/* GET /api/enveloppes-referentiel-public — liste complète (lecture seule) */
|
||||
router.get('/', (_req, res) => {
|
||||
const rows = db.prepare(`
|
||||
SELECT id, nom, eligible_compte
|
||||
FROM enveloppes_referentiel
|
||||
ORDER BY nom
|
||||
`).all();
|
||||
res.json(attachCategories(rows));
|
||||
});
|
||||
|
||||
export default router;
|
||||
@@ -54,7 +54,9 @@ import institutionsReferentielRouter from './routes/institutionsReferentiel.js';
|
||||
import institutionsReferentielPublicRouter from './routes/institutionsReferentielPublic.js';
|
||||
import refCategoriesInstitutionsRouter from './routes/ref-categories-institutions.js';
|
||||
import enveloppesReferentielRouter from './routes/enveloppesReferentiel.js';
|
||||
import enveloppesReferentielPublicRouter from './routes/enveloppesReferentielPublic.js';
|
||||
import supportsReferentielRouter from './routes/supportsReferentiel.js';
|
||||
import empruntsRouter from './routes/emprunts.js';
|
||||
import refCategoriesEnveloppesRouter from './routes/ref-categories-enveloppes.js';
|
||||
import refCategoriesRouter from './routes/ref-categories.js';
|
||||
import refSecteursRouter from './routes/ref-secteurs.js';
|
||||
@@ -161,6 +163,7 @@ app.use('/api/reinvestissements', requireAuth, reinvestissementsRouter);
|
||||
app.use('/api/frais-operations', requireAuth, fraisOperationsRouter);
|
||||
app.use('/api/corrections', requireAuth, correctionsRouter);
|
||||
app.use('/api/comptes', requireAuth, comptesRouter);
|
||||
app.use('/api/emprunts', requireAuth, empruntsRouter);
|
||||
app.use('/api/preferences', requireAuth, preferencesRouter);
|
||||
app.use('/api/icons', requireAuth, iconsRouter);
|
||||
app.use('/api/admin', requireAuth, requireAdmin, adminRouter);
|
||||
@@ -183,6 +186,7 @@ app.use('/api/ref-categories-institutions', requireAuth, requireAdmin, refCateg
|
||||
// catalogue admin transverse aux workspaces, catalogue seul pour l'instant (pas de
|
||||
// rattachement à l'existant), cf. enveloppesReferentiel.js.
|
||||
app.use('/api/enveloppes-referentiel', requireAuth, requireAdmin, enveloppesReferentielRouter);
|
||||
app.use('/api/enveloppes-referentiel-public', requireAuth, enveloppesReferentielPublicRouter);
|
||||
app.use('/api/supports-referentiel', requireAuth, requireAdmin, supportsReferentielRouter);
|
||||
app.use('/api/ref-categories-enveloppes', requireAuth, requireAdmin, refCategoriesEnveloppesRouter);
|
||||
app.use('/api/ref-categories', requireAuth, requireAdmin, refCategoriesRouter);
|
||||
|
||||
Reference in new issue
Block a user