-- ============================================================================= -- V11 — Workflow de souscription/onboarding UnionFlow -- ============================================================================= -- Auteur : UnionFlow Team -- Date : 2026-03-30 -- Objectif: -- 0. Renommer les tables singulières créées par V1 vers les noms pluriels attendus -- par les entités JPA (FormuleAbonnement → formules_abonnement, etc.) -- 1. Ajouter les colonnes manquantes à formules_abonnement (entité refactorisée) -- 2. Ajouter la colonne `plage` à formules_abonnement (PETITE/MOYENNE/GRANDE/TRES_GRANDE) -- 3. Supprimer le code unique sur `code` seul (nouvelle contrainte sur code+plage) -- 4. Migrer les anciennes valeurs TypeFormule (STARTER→BASIC, CRYSTAL supprimé) -- 5. Vider et re-seeder avec la matrice tarifaire 4×3 (12 formules) -- 6. Ajouter les colonnes du workflow de validation à souscriptions_organisation -- 7. Ajouter EN_ATTENTE au statut (déjà en DB comme VARCHAR, pas de contrainte CHECK) -- ============================================================================= -- ----------------------------------------------------------------------------- -- 0. Renommer les tables singulières → plurielles (alignement entités JPA) -- ----------------------------------------------------------------------------- -- formules_abonnement → formules_abonnement (FormuleAbonnement entity @Table) DO $$ BEGIN IF EXISTS (SELECT 1 FROM pg_tables WHERE schemaname = 'public' AND tablename = 'formules_abonnement') AND NOT EXISTS (SELECT 1 FROM pg_tables WHERE schemaname = 'public' AND tablename = 'formules_abonnement') THEN ALTER TABLE formules_abonnement RENAME TO formules_abonnement; END IF; END $$; -- souscriptions_organisation → souscriptions_organisation (SouscriptionOrganisation entity @Table) DO $$ BEGIN IF EXISTS (SELECT 1 FROM pg_tables WHERE schemaname = 'public' AND tablename = 'souscriptions_organisation') AND NOT EXISTS (SELECT 1 FROM pg_tables WHERE schemaname = 'public' AND tablename = 'souscriptions_organisation') THEN ALTER TABLE souscriptions_organisation RENAME TO souscriptions_organisation; END IF; END $$; -- Mise à jour des noms de contraintes FK après le renommage -- (PostgreSQL met à jour automatiquement les FK qui référencent la table renommée) -- ----------------------------------------------------------------------------- -- 0b. Ajouter les colonnes manquantes à formules_abonnement (entité refactorisée) -- ----------------------------------------------------------------------------- -- Colonne libelle (libellé de la formule — nullable temporairement, NOT NULL après seed) ALTER TABLE formules_abonnement ADD COLUMN IF NOT EXISTS libelle VARCHAR(100); -- Colonne plage (taille organisation cible) ALTER TABLE formules_abonnement ADD COLUMN IF NOT EXISTS plage VARCHAR(20); -- Nombre max de membres (NULL = illimité) ALTER TABLE formules_abonnement ADD COLUMN IF NOT EXISTS max_membres INTEGER; -- Stockage max en Mo (défaut 1 Go) ALTER TABLE formules_abonnement ADD COLUMN IF NOT EXISTS max_stockage_mo INTEGER DEFAULT 1024; -- Ordre d'affichage dans le catalogue ALTER TABLE formules_abonnement ADD COLUMN IF NOT EXISTS ordre_affichage INTEGER DEFAULT 0; -- La colonne `nom` de V1 n'est pas dans l'entité JPA — rendre nullable pour compatibilité ALTER TABLE formules_abonnement ALTER COLUMN nom DROP NOT NULL; -- ----------------------------------------------------------------------------- -- 0c. Supprimer les contraintes CHECK Hibernate obsolètes -- ----------------------------------------------------------------------------- -- Hibernate génère automatiquement des CHECK constraints pour les enums @Enumerated(STRING). -- Les valeurs ont changé (STARTER→BASIC, CRYSTAL supprimé) : on supprime ces contraintes -- avant toute modification de données. ALTER TABLE formules_abonnement DROP CONSTRAINT IF EXISTS formules_abonnement_code_check; ALTER TABLE souscriptions_organisation DROP CONSTRAINT IF EXISTS souscriptions_organisation_statut_check; ALTER TABLE souscriptions_organisation DROP CONSTRAINT IF EXISTS souscriptions_organisation_type_periode_check; ALTER TABLE souscriptions_organisation DROP CONSTRAINT IF EXISTS souscriptions_organisation_statut_validation_check; -- ----------------------------------------------------------------------------- -- 1. Préparer formules_abonnement -- ----------------------------------------------------------------------------- -- Ajouter la colonne plage si elle n'existe pas encore ALTER TABLE formules_abonnement ADD COLUMN IF NOT EXISTS plage VARCHAR(20); -- Supprimer toutes les contraintes unicité sur code seul (V1 ou Hibernate) DO $$ DECLARE r RECORD; BEGIN FOR r IN SELECT conname FROM pg_constraint WHERE conrelid = 'formules_abonnement'::regclass AND contype = 'u' AND conname NOT IN ('idx_formule_code_plage') AND array_length(conkey, 1) = 1 AND conkey[1] = ( SELECT attnum FROM pg_attribute WHERE attrelid = 'formules_abonnement'::regclass AND attname = 'code' ) LOOP EXECUTE 'ALTER TABLE formules_abonnement DROP CONSTRAINT ' || quote_ident(r.conname); END LOOP; END $$; -- Supprimer les index uniques sur code seul s'ils existent DROP INDEX IF EXISTS idx_formule_code; DROP INDEX IF EXISTS formule_abonnement_code_key; -- ----------------------------------------------------------------------------- -- 2. Migrer les anciennes données TypeFormule -- ----------------------------------------------------------------------------- UPDATE formules_abonnement SET code = 'BASIC' WHERE code = 'STARTER'; DELETE FROM formules_abonnement WHERE code = 'CRYSTAL'; -- ----------------------------------------------------------------------------- -- 3. Vider et re-seeder avec la nouvelle matrice 4 plages × 3 formules -- ----------------------------------------------------------------------------- DELETE FROM formules_abonnement; INSERT INTO formules_abonnement ( id, version, actif, cree_par, modifie_par, date_creation, date_modification, code, libelle, description, plage, max_membres, max_stockage_mo, prix_mensuel, prix_annuel, ordre_affichage ) VALUES -- ── PETITE (1–100 membres) ────────────────────────────────────────────────── (gen_random_uuid(), 0, true, 'SYSTEM', 'SYSTEM', NOW(), NOW(), 'BASIC', 'Basic — Petites structures', 'Idéal pour les petites associations de moins de 100 membres', 'PETITE', 100, 1024, 3000, 28800, 1), (gen_random_uuid(), 0, true, 'SYSTEM', 'SYSTEM', NOW(), NOW(), 'STANDARD', 'Standard — Petites structures', 'Pour les associations actives de moins de 100 membres', 'PETITE', 100, 5120, 6000, 57600, 2), (gen_random_uuid(), 0, true, 'SYSTEM', 'SYSTEM', NOW(), NOW(), 'PREMIUM', 'Premium — Petites structures', 'Fonctionnalités avancées pour petites structures ambitieuses', 'PETITE', 100, 10240, 10000, 96000, 3), -- ── MOYENNE (101–500 membres) ──────────────────────────────────────────────── (gen_random_uuid(), 0, true, 'SYSTEM', 'SYSTEM', NOW(), NOW(), 'BASIC', 'Basic — Moyennes structures', 'Gestion complète pour moyennes structures', 'MOYENNE', 500, 2048, 8000, 76800, 4), (gen_random_uuid(), 0, true, 'SYSTEM', 'SYSTEM', NOW(), NOW(), 'STANDARD', 'Standard — Moyennes structures', 'Fonctionnalités étendues pour organisations en croissance', 'MOYENNE', 500, 10240, 15000, 144000, 5), (gen_random_uuid(), 0, true, 'SYSTEM', 'SYSTEM', NOW(), NOW(), 'PREMIUM', 'Premium — Moyennes structures', 'Suite complète pour organisations actives', 'MOYENNE', 500, 20480, 25000, 240000, 6), -- ── GRANDE (501–2 000 membres) ──────────────────────────────────────────────── (gen_random_uuid(), 0, true, 'SYSTEM', 'SYSTEM', NOW(), NOW(), 'BASIC', 'Basic — Grandes structures', 'Solution économique pour grandes organisations', 'GRANDE', 2000, 5120, 20000, 192000, 7), (gen_random_uuid(), 0, true, 'SYSTEM', 'SYSTEM', NOW(), NOW(), 'STANDARD', 'Standard — Grandes structures', 'Gestion avancée pour grandes structures', 'GRANDE', 2000, 20480, 35000, 336000, 8), (gen_random_uuid(), 0, true, 'SYSTEM', 'SYSTEM', NOW(), NOW(), 'PREMIUM', 'Premium — Grandes structures', 'Fonctionnalités entreprise pour grandes organisations', 'GRANDE', 2000, 51200, 60000, 576000, 9), -- ── TRES_GRANDE (2 000+ membres) ───────────────────────────────────────────── (gen_random_uuid(), 0, true, 'SYSTEM', 'SYSTEM', NOW(), NOW(), 'BASIC', 'Basic — Très grandes structures', 'Gestion de base pour très grandes organisations', 'TRES_GRANDE', NULL, 10240, 50000, 480000, 10), (gen_random_uuid(), 0, true, 'SYSTEM', 'SYSTEM', NOW(), NOW(), 'STANDARD', 'Standard — Très grandes structures', 'Suite complète pour très grandes organisations', 'TRES_GRANDE', NULL, 51200, 80000, 768000, 11), (gen_random_uuid(), 0, true, 'SYSTEM', 'SYSTEM', NOW(), NOW(), 'PREMIUM', 'Premium — Très grandes structures', 'Solution enterprise multi-sites avec analytics avancé', 'TRES_GRANDE', NULL, 102400, 120000, 1152000, 12); -- Appliquer NOT NULL sur plage après le seed DO $$ BEGIN -- PostgreSQL ne supporte pas ADD COLUMN ... NOT NULL ... en une seule commande -- quand la table est déjà peuplée. On pose la contrainte séparément. ALTER TABLE formules_abonnement ALTER COLUMN plage SET NOT NULL; EXCEPTION WHEN others THEN RAISE NOTICE 'Contrainte NOT NULL sur plage déjà présente ou erreur: %', SQLERRM; END $$; -- Créer l'index unique sur la combinaison code+plage CREATE UNIQUE INDEX IF NOT EXISTS idx_formule_code_plage ON formules_abonnement (code, plage); CREATE INDEX IF NOT EXISTS idx_formule_plage ON formules_abonnement (plage); -- ----------------------------------------------------------------------------- -- 4. Colonnes workflow de validation dans souscriptions_organisation -- ----------------------------------------------------------------------------- ALTER TABLE souscriptions_organisation ADD COLUMN IF NOT EXISTS plage VARCHAR(20); ALTER TABLE souscriptions_organisation ADD COLUMN IF NOT EXISTS type_organisation VARCHAR(30); ALTER TABLE souscriptions_organisation ADD COLUMN IF NOT EXISTS coefficient_applique NUMERIC(4, 2); ALTER TABLE souscriptions_organisation ADD COLUMN IF NOT EXISTS statut_validation VARCHAR(40); ALTER TABLE souscriptions_organisation ADD COLUMN IF NOT EXISTS montant_total NUMERIC(12, 2); ALTER TABLE souscriptions_organisation ADD COLUMN IF NOT EXISTS date_validation DATE; ALTER TABLE souscriptions_organisation ADD COLUMN IF NOT EXISTS validated_by_id UUID; ALTER TABLE souscriptions_organisation ADD COLUMN IF NOT EXISTS commentaire_rejet VARCHAR(500); ALTER TABLE souscriptions_organisation ADD COLUMN IF NOT EXISTS mot_de_passe_temporaire VARCHAR(100); -- Backfill des lignes existantes avant d'ajouter les contraintes NOT NULL UPDATE souscriptions_organisation SET plage = 'PETITE', type_organisation = 'ASSOCIATION', coefficient_applique = 1.0, statut_validation = 'VALIDEE' WHERE plage IS NULL; -- Appliquer NOT NULL sur statut_validation uniquement (les autres sont optionnels) DO $$ BEGIN ALTER TABLE souscriptions_organisation ALTER COLUMN statut_validation SET NOT NULL; EXCEPTION WHEN others THEN RAISE NOTICE 'Contrainte NOT NULL sur statut_validation: %', SQLERRM; END $$; -- Valeur par défaut pour les nouvelles lignes ALTER TABLE souscriptions_organisation ALTER COLUMN statut_validation SET DEFAULT 'EN_ATTENTE_PAIEMENT'; -- Index pour les requêtes SuperAdmin sur le workflow CREATE INDEX IF NOT EXISTS idx_souscription_statut_validation ON souscriptions_organisation (statut_validation); CREATE INDEX IF NOT EXISTS idx_souscription_plage ON souscriptions_organisation (plage); -- Mise à jour du statut global pour les souscriptions existantes actives -- (l'ancienne valeur ACTIVE reste cohérente avec VALIDEE côté validation) UPDATE souscriptions_organisation SET statut = 'ACTIVE' WHERE statut IS NULL OR statut NOT IN ('ACTIVE', 'EXPIREE', 'SUSPENDUE', 'RESILIEE', 'EN_ATTENTE'); -- ============================================================================= -- Fin de V11 -- =============================================================================