-- ============================================================ -- V2.2 — SaaS : formules_abonnement + souscriptions_organisation -- Auteur: UnionFlow Team | BCEAO/OHADA compliant -- ============================================================ CREATE TABLE IF NOT EXISTS formules_abonnement ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), code VARCHAR(20) UNIQUE NOT NULL, -- STARTER, STANDARD, PREMIUM, CRYSTAL libelle VARCHAR(100) NOT NULL, description TEXT, max_membres INTEGER, -- NULL = illimité (Crystal+) max_stockage_mo INTEGER NOT NULL DEFAULT 1024, -- 1 Go par défaut prix_mensuel DECIMAL(10,2) NOT NULL CHECK (prix_mensuel >= 0), prix_annuel DECIMAL(10,2) NOT NULL CHECK (prix_annuel >= 0), actif BOOLEAN NOT NULL DEFAULT TRUE, ordre_affichage INTEGER DEFAULT 0, -- Métadonnées BaseEntity date_creation TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, date_modification TIMESTAMP, cree_par VARCHAR(255), modifie_par VARCHAR(255), version BIGINT NOT NULL DEFAULT 0, CONSTRAINT chk_formule_code CHECK (code IN ('STARTER','STANDARD','PREMIUM','CRYSTAL')) ); -- Données initiales des forfaits (XOF, 1er Janvier 2026) INSERT INTO formules_abonnement (id, code, libelle, description, max_membres, max_stockage_mo, prix_mensuel, prix_annuel, actif, ordre_affichage) VALUES (gen_random_uuid(), 'STARTER', 'Formule Starter', 'Idéal pour démarrer — jusqu''à 50 membres', 50, 1024, 5000.00, 50000.00, true, 1), (gen_random_uuid(), 'STANDARD', 'Formule Standard', 'Pour les organisations en croissance', 200, 1024, 7000.00, 70000.00, true, 2), (gen_random_uuid(), 'PREMIUM', 'Formule Premium', 'Organisations établies', 500, 1024, 9000.00, 90000.00, true, 3), (gen_random_uuid(), 'CRYSTAL', 'Formule Crystal', 'Fédérations et grandes organisations', NULL,1024, 10000.00, 100000.00, true, 4) ON CONFLICT (code) DO NOTHING; -- ============================================================ CREATE TABLE IF NOT EXISTS souscriptions_organisation ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), organisation_id UUID UNIQUE NOT NULL, formule_id UUID NOT NULL, type_periode VARCHAR(10) NOT NULL DEFAULT 'MENSUEL', -- MENSUEL | ANNUEL date_debut DATE NOT NULL, date_fin DATE NOT NULL, quota_max INTEGER, -- snapshot de formule.max_membres quota_utilise INTEGER NOT NULL DEFAULT 0, statut VARCHAR(30) NOT NULL DEFAULT 'ACTIVE', reference_paiement_wave VARCHAR(100), wave_session_id VARCHAR(255), date_dernier_paiement DATE, date_prochain_paiement DATE, -- Métadonnées BaseEntity actif BOOLEAN NOT NULL DEFAULT TRUE, date_creation TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, date_modification TIMESTAMP, cree_par VARCHAR(255), modifie_par VARCHAR(255), version BIGINT NOT NULL DEFAULT 0, CONSTRAINT fk_souscription_org FOREIGN KEY (organisation_id) REFERENCES organisations(id) ON DELETE CASCADE, CONSTRAINT fk_souscription_formule FOREIGN KEY (formule_id) REFERENCES formules_abonnement(id), CONSTRAINT chk_souscription_statut CHECK (statut IN ('ACTIVE','EXPIREE','SUSPENDUE','RESILIEE')), CONSTRAINT chk_souscription_periode CHECK (type_periode IN ('MENSUEL','ANNUEL')), CONSTRAINT chk_souscription_quota CHECK (quota_utilise >= 0) ); CREATE INDEX idx_souscription_org ON souscriptions_organisation(organisation_id); CREATE INDEX idx_souscription_statut ON souscriptions_organisation(statut); CREATE INDEX idx_souscription_fin ON souscriptions_organisation(date_fin); COMMENT ON TABLE formules_abonnement IS 'Catalogue des forfaits SaaS UnionFlow (Starter→Crystal, 5000–10000 XOF/mois)'; COMMENT ON TABLE souscriptions_organisation IS 'Abonnement actif d''une organisation racine — quota, durée, référence Wave'; COMMENT ON COLUMN souscriptions_organisation.quota_utilise IS 'Incrémenté automatiquement à chaque adhésion validée. Bloquant si = quota_max.';