Amélioration de la notion de workspace
This commit is contained in:
1 parent
1d5737703b
commit
c918de1e7d
11 files changed
+202
-58
No files matched your search
+60
-8
@@ -3050,6 +3050,7 @@ db.exec(`
|
||||
nom TEXT NOT NULL,
|
||||
libelle_menu TEXT NOT NULL,
|
||||
description TEXT,
|
||||
type TEXT NOT NULL DEFAULT 'private_equity',
|
||||
actif_global INTEGER NOT NULL DEFAULT 1,
|
||||
ordre INTEGER NOT NULL DEFAULT 0,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
@@ -3057,6 +3058,57 @@ db.exec(`
|
||||
)
|
||||
`);
|
||||
|
||||
// ── Migration : workspaces.type (19/09/26) ───────────────────────────────
|
||||
// Jusqu'ici, TOUT le routage crowdlending/PE (frontend ET backend — plus de
|
||||
// 20 endroits, cf. project_workspaces_transformation.md) reposait sur une
|
||||
// comparaison littérale `slug === 'crowdlending'` : le slug — pourtant un
|
||||
// simple identifiant technique choisi librement par l'admin (regex
|
||||
// minuscules/chiffres/tirets, cf. adminWorkspaces.js) — portait donc
|
||||
// implicitement toute la sémantique "quel modèle de page afficher ?".
|
||||
// Question d'Olivier (19/09/26) : comment le programme reconnaît-il qu'il
|
||||
// doit utiliser les pages Crowdlending ou Private Equity, sachant que
|
||||
// l'admin choisit juste un nom ? Réponse à l'époque : aucune vraie
|
||||
// typologie, seulement "crowdlending" vs "tout le reste" — décision
|
||||
// d'Olivier du même jour : corriger ça maintenant, dans tout le code.
|
||||
// `type` porte désormais cette sémantique explicitement ('crowdlending' ou
|
||||
// 'private_equity' à ce jour — toute valeur non 'crowdlending' continue,
|
||||
// pour l'instant, à être traitée comme "PE" côté UI, en attendant qu'un
|
||||
// 3e modèle de page existe réellement). Choisi à la création d'un
|
||||
// workspace (formulaire admin, cf. adminWorkspaces.js), immutable ensuite
|
||||
// — même régime que `slug`, pour la même raison : en changer sous un
|
||||
// workspace déjà peuplé de données changerait silencieusement quel modèle
|
||||
// de page/API s'applique à ces données. DEFAULT 'private_equity' ci-dessus
|
||||
// : filet de sécurité reproduisant le comportement historique implicite
|
||||
// ("pas crowdlending" = PE) si jamais une ligne était insérée sans passer
|
||||
// par l'API ; l'API elle-même exige toujours une valeur explicite.
|
||||
{
|
||||
const wsCols = db.prepare('PRAGMA table_info(workspaces)').all().map(c => c.name);
|
||||
if (!wsCols.includes('type')) {
|
||||
db.exec("ALTER TABLE workspaces ADD COLUMN type TEXT NOT NULL DEFAULT 'private_equity'");
|
||||
}
|
||||
// Backfill volontairement INCONDITIONNEL (pas seulement juste après l'ALTER ci-dessus) :
|
||||
// le seul workspace qui ait jamais eu un sens "crowdlending" est celui de slug
|
||||
// 'crowdlending' (seed ci-dessous, protégé, non supprimable) — tous les autres (aujourd'hui :
|
||||
// 'private-equity') étaient déjà traités comme "PE" par l'ancien code, donc reçoivent le
|
||||
// DEFAULT ci-dessus sans rien faire. Auto-réparateur à chaque démarrage (20/09/26) : une
|
||||
// version antérieure de cette migration ne faisait ce backfill qu'au moment de l'ALTER, donc
|
||||
// un redémarrage ayant appliqué l'ALTER avant la correction d'un bug dans cette ligne laissait
|
||||
// le workspace crowdlending bloqué sur le DEFAULT 'private_equity' — sans nouveau redémarrage
|
||||
// "à vide" pour le rattraper, ce cas ne pouvait jamais se corriger. Cette ligne s'exécute donc
|
||||
// désormais à chaque démarrage, sans condition, et ne fait rien dès que le type est déjà bon.
|
||||
db.exec("UPDATE workspaces SET type = 'crowdlending' WHERE slug = 'crowdlending' AND type <> 'crowdlending'");
|
||||
}
|
||||
// Au plus UN workspace de type 'crowdlending' (l'app entière suppose ce
|
||||
// singleton — resolveActiveWorkspaceId, grantDefaultWorkspace, etc. font
|
||||
// un .get() qui prend la première ligne trouvée). Index partiel (SQLite
|
||||
// supporte les index UNIQUE avec clause WHERE) : ne contraint que le type
|
||||
// 'crowdlending', laisse 'private_equity' libre en plusieurs exemplaires
|
||||
// si Olivier veut un jour plusieurs workspaces PE distincts.
|
||||
db.exec(`
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_workspaces_type_crowdlending_singleton
|
||||
ON workspaces(type) WHERE type = 'crowdlending'
|
||||
`);
|
||||
|
||||
db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS user_workspaces (
|
||||
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
@@ -3087,8 +3139,8 @@ db.exec('CREATE INDEX IF NOT EXISTS idx_user_workspaces_user ON user_workspaces(
|
||||
|
||||
// Seed : workspace "crowdlending" = l'app actuelle. Idempotent (slug UNIQUE).
|
||||
db.prepare(`
|
||||
INSERT OR IGNORE INTO workspaces (slug, nom, libelle_menu, description, actif_global, ordre)
|
||||
VALUES ('crowdlending', 'Crowdlending', 'Crowdlending', 'Suivi de portefeuille crowdlending', 1, 0)
|
||||
INSERT OR IGNORE INTO workspaces (slug, nom, libelle_menu, description, type, actif_global, ordre)
|
||||
VALUES ('crowdlending', 'Crowdlending', 'Crowdlending', 'Suivi de portefeuille crowdlending', 'crowdlending', 1, 0)
|
||||
`).run();
|
||||
|
||||
/** Accorde + active le workspace par défaut (crowdlending) à un utilisateur.
|
||||
@@ -3097,7 +3149,7 @@ db.prepare(`
|
||||
* auto du profil investisseur principal à ces 3 mêmes endroits, pour
|
||||
* qu'aucun nouveau compte ne se retrouve sans accès à l'app actuelle. */
|
||||
export function grantDefaultWorkspace(userId) {
|
||||
const ws = db.prepare("SELECT id FROM workspaces WHERE slug = 'crowdlending'").get();
|
||||
const ws = db.prepare("SELECT id FROM workspaces WHERE type = 'crowdlending'").get();
|
||||
if (!ws) return;
|
||||
db.prepare(`
|
||||
INSERT OR IGNORE INTO user_workspaces (user_id, workspace_id, granted_by_admin, active_by_user, granted_at)
|
||||
@@ -3110,7 +3162,7 @@ export function grantDefaultWorkspace(userId) {
|
||||
// rien casser du fonctionnement actuel. Idempotent (INSERT OR IGNORE sur la
|
||||
// PK composite user_id/workspace_id).
|
||||
{
|
||||
const wsBackfill = db.prepare("SELECT id FROM workspaces WHERE slug = 'crowdlending'").get();
|
||||
const wsBackfill = db.prepare("SELECT id FROM workspaces WHERE type = 'crowdlending'").get();
|
||||
if (wsBackfill) {
|
||||
db.prepare(`
|
||||
INSERT OR IGNORE INTO user_workspaces (user_id, workspace_id, granted_by_admin, active_by_user, granted_at)
|
||||
@@ -3141,7 +3193,7 @@ db.exec('CREATE INDEX IF NOT EXISTS idx_ref_workspaces_ws ON referentiel_workspa
|
||||
// (comptes créés avant cette migration, ou entrées jamais touchées depuis)
|
||||
// est rattachée au workspace "crowdlending" — seul workspace réel à ce jour.
|
||||
{
|
||||
const wsCrowdlending = db.prepare("SELECT id FROM workspaces WHERE slug = 'crowdlending'").get();
|
||||
const wsCrowdlending = db.prepare("SELECT id FROM workspaces WHERE type = 'crowdlending'").get();
|
||||
if (wsCrowdlending) {
|
||||
db.prepare(`
|
||||
INSERT OR IGNORE INTO referentiel_workspaces (referentiel_id, workspace_id)
|
||||
@@ -3174,7 +3226,7 @@ db.exec('CREATE INDEX IF NOT EXISTS idx_plat_workspaces_ws ON plateforme_workspa
|
||||
// Backfill idempotent : toute plateforme utilisateur sans aucune affectation
|
||||
// est rattachée au workspace "crowdlending" — seul workspace réel à ce jour.
|
||||
{
|
||||
const wsCrowdlending = db.prepare("SELECT id FROM workspaces WHERE slug = 'crowdlending'").get();
|
||||
const wsCrowdlending = db.prepare("SELECT id FROM workspaces WHERE type = 'crowdlending'").get();
|
||||
if (wsCrowdlending) {
|
||||
db.prepare(`
|
||||
INSERT OR IGNORE INTO plateforme_workspaces (plateforme_id, workspace_id)
|
||||
@@ -3204,7 +3256,7 @@ db.exec('CREATE INDEX IF NOT EXISTS idx_depret_workspace ON depots_retraits(work
|
||||
// Backfill idempotent : tout dépôt/retrait sans workspace (créé avant cette
|
||||
// migration) est rattaché à "crowdlending" — seul workspace réel à ce jour.
|
||||
{
|
||||
const wsCrowdlending2 = db.prepare("SELECT id FROM workspaces WHERE slug = 'crowdlending'").get();
|
||||
const wsCrowdlending2 = db.prepare("SELECT id FROM workspaces WHERE type = 'crowdlending'").get();
|
||||
if (wsCrowdlending2) {
|
||||
db.prepare('UPDATE depots_retraits SET workspace_id = ? WHERE workspace_id IS NULL').run(wsCrowdlending2.id);
|
||||
}
|
||||
@@ -3226,7 +3278,7 @@ db.exec('CREATE INDEX IF NOT EXISTS idx_remb_workspace ON remboursements(workspa
|
||||
// Backfill idempotent : tout remboursement sans workspace (créé avant cette
|
||||
// migration) est rattaché à "crowdlending" — seul workspace réel à ce jour.
|
||||
{
|
||||
const wsCrowdlending3 = db.prepare("SELECT id FROM workspaces WHERE slug = 'crowdlending'").get();
|
||||
const wsCrowdlending3 = db.prepare("SELECT id FROM workspaces WHERE type = 'crowdlending'").get();
|
||||
if (wsCrowdlending3) {
|
||||
db.prepare('UPDATE remboursements SET workspace_id = ? WHERE workspace_id IS NULL').run(wsCrowdlending3.id);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user