chore(quarkus-327): bump to Quarkus 3.27.3 LTS, make pom autonomous, fix 3 tests (NPE guard, equalsHashCode with shared refs), rename deprecated config keys

This commit is contained in:
2026-04-23 14:45:54 +00:00
parent 8cec38f7b3
commit fb3a32817b
312 changed files with 50688 additions and 50645 deletions

View File

@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee
https://jakarta.ee/xml/ns/jakartaee/beans_4_0.xsd"
version="4.0"
bean-discovery-mode="annotated">
</beans>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee
https://jakarta.ee/xml/ns/jakartaee/beans_4_0.xsd"
version="4.0"
bean-discovery-mode="annotated">
</beans>

View File

@@ -16,7 +16,7 @@ quarkus.datasource.jdbc.min-size=2
quarkus.datasource.jdbc.max-size=10
# Hibernate — Mode update pour créer automatiquement les colonnes manquantes
quarkus.hibernate-orm.database.generation=update
quarkus.hibernate-orm.schema-management.strategy=update
quarkus.hibernate-orm.log.sql=true
# Flyway — activé avec réparation auto des checksums modifiés

View File

@@ -15,7 +15,7 @@ quarkus.datasource.jdbc.idle-removal-interval=PT2M
quarkus.datasource.jdbc.max-lifetime=PT30M
# Hibernate — Validate uniquement (Flyway gère le schéma)
quarkus.hibernate-orm.database.generation=validate
quarkus.hibernate-orm.schema-management.strategy=validate
quarkus.hibernate-orm.statistics=false
# Flyway — ignorer les migrations appliquées en DB mais absentes localement
@@ -49,7 +49,7 @@ quarkus.smallrye-openapi.oidc-open-id-connect-url=${quarkus.oidc.auth-server-url
quarkus.swagger-ui.always-include=false
# Logging — fichier en production (le répertoire doit exister dans le container)
quarkus.log.file.enable=false
quarkus.log.file.enabled=false
quarkus.log.file.path=/var/log/unionflow/server.log
quarkus.log.file.rotation.max-file-size=10M
quarkus.log.file.rotation.max-backup-index=5

View File

@@ -8,7 +8,7 @@ quarkus.datasource.password=sa
quarkus.datasource.jdbc.url=jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;MODE=PostgreSQL;NON_KEYWORDS=MONTH,YEAR
# Configuration Hibernate pour tests
quarkus.hibernate-orm.database.generation=update
quarkus.hibernate-orm.schema-management.strategy=update
# Désactiver complètement l'exécution des scripts SQL au démarrage
quarkus.hibernate-orm.sql-load-script=no-file
# Empêcher Hibernate d'exécuter les scripts SQL automatiquement

View File

@@ -40,7 +40,7 @@ quarkus.datasource.password=${DB_PASSWORD:changeme}
quarkus.datasource.jdbc.url=${DB_URL:jdbc:postgresql://localhost:5432/unionflow}
# Configuration CORS
quarkus.http.cors=true
quarkus.http.cors.enabled=true
quarkus.http.cors.methods=GET,POST,PUT,DELETE,OPTIONS
quarkus.http.cors.headers=Content-Type,Authorization
@@ -49,7 +49,7 @@ quarkus.http.auth.permission.public.paths=/health,/q/*,/favicon.ico,/auth/callba
quarkus.http.auth.permission.public.policy=permit
# Configuration Hibernate — base commune
quarkus.hibernate-orm.database.generation=update
quarkus.hibernate-orm.schema-management.strategy=update
quarkus.hibernate-orm.log.sql=false
quarkus.hibernate-orm.jdbc.timezone=UTC
# Configuration Flyway — base commune
@@ -96,7 +96,7 @@ quarkus.hibernate-orm.metrics.enabled=true
# JVM + HTTP server + datasource metrics activés par défaut avec quarkus-micrometer
# Logging — base commune
quarkus.log.console.enable=true
quarkus.log.console.enabled=true
quarkus.log.console.level=INFO
quarkus.log.console.format=%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p [%c{2.}] (%t) %s%e%n
quarkus.log.category."dev.lions.unionflow".level=INFO

View File

@@ -1,419 +1,419 @@
-- Migration V1.3: Conversion des colonnes ID de BIGINT vers UUID
-- Auteur: UnionFlow Team
-- Date: 2025-01-16
-- Description: Convertit toutes les colonnes ID et clés étrangères de BIGINT vers UUID
-- ATTENTION: Cette migration supprime toutes les données existantes pour simplifier la conversion
-- Pour une migration avec préservation des données, voir V1.3.1__Convert_Ids_To_UUID_With_Data.sql
-- ============================================
-- ÉTAPE 1: Suppression des contraintes de clés étrangères
-- ============================================
-- Supprimer les contraintes de clés étrangères existantes
DO $$
BEGIN
-- Supprimer FK membres -> organisations
IF EXISTS (
SELECT 1 FROM information_schema.table_constraints
WHERE constraint_name = 'fk_membre_organisation'
AND table_name = 'membres'
) THEN
ALTER TABLE membres DROP CONSTRAINT fk_membre_organisation;
END IF;
-- Supprimer FK cotisations -> membres
IF EXISTS (
SELECT 1 FROM information_schema.table_constraints
WHERE constraint_name LIKE 'fk_cotisation%'
AND table_name = 'cotisations'
) THEN
ALTER TABLE cotisations DROP CONSTRAINT IF EXISTS fk_cotisation_membre CASCADE;
END IF;
-- Supprimer FK evenements -> organisations
IF EXISTS (
SELECT 1 FROM information_schema.table_constraints
WHERE constraint_name LIKE 'fk_evenement%'
AND table_name = 'evenements'
) THEN
ALTER TABLE evenements DROP CONSTRAINT IF EXISTS fk_evenement_organisation CASCADE;
END IF;
-- Supprimer FK inscriptions_evenement -> membres et evenements
IF EXISTS (
SELECT 1 FROM information_schema.table_constraints
WHERE constraint_name LIKE 'fk_inscription%'
AND table_name = 'inscriptions_evenement'
) THEN
ALTER TABLE inscriptions_evenement DROP CONSTRAINT IF EXISTS fk_inscription_membre CASCADE;
ALTER TABLE inscriptions_evenement DROP CONSTRAINT IF EXISTS fk_inscription_evenement CASCADE;
END IF;
-- Supprimer FK demandes_aide -> membres et organisations
IF EXISTS (
SELECT 1 FROM information_schema.table_constraints
WHERE constraint_name LIKE 'fk_demande%'
AND table_name = 'demandes_aide'
) THEN
ALTER TABLE demandes_aide DROP CONSTRAINT IF EXISTS fk_demande_demandeur CASCADE;
ALTER TABLE demandes_aide DROP CONSTRAINT IF EXISTS fk_demande_evaluateur CASCADE;
ALTER TABLE demandes_aide DROP CONSTRAINT IF EXISTS fk_demande_organisation CASCADE;
END IF;
END $$;
-- ============================================
-- ÉTAPE 2: Supprimer les séquences (BIGSERIAL)
-- ============================================
DROP SEQUENCE IF EXISTS membres_SEQ CASCADE;
DROP SEQUENCE IF EXISTS cotisations_SEQ CASCADE;
DROP SEQUENCE IF EXISTS evenements_SEQ CASCADE;
DROP SEQUENCE IF EXISTS organisations_id_seq CASCADE;
-- ============================================
-- ÉTAPE 3: Supprimer les tables existantes (pour recréation avec UUID)
-- ============================================
-- Supprimer les tables dans l'ordre inverse des dépendances
DROP TABLE IF EXISTS inscriptions_evenement CASCADE;
DROP TABLE IF EXISTS demandes_aide CASCADE;
DROP TABLE IF EXISTS cotisations CASCADE;
DROP TABLE IF EXISTS evenements CASCADE;
DROP TABLE IF EXISTS membres CASCADE;
DROP TABLE IF EXISTS organisations CASCADE;
-- ============================================
-- ÉTAPE 4: Recréer les tables avec UUID
-- ============================================
-- Table organisations avec UUID
CREATE TABLE organisations (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
-- Informations de base
nom VARCHAR(200) NOT NULL,
nom_court VARCHAR(50),
type_organisation VARCHAR(50) NOT NULL DEFAULT 'ASSOCIATION',
statut VARCHAR(50) NOT NULL DEFAULT 'ACTIVE',
description TEXT,
date_fondation DATE,
numero_enregistrement VARCHAR(100) UNIQUE,
-- Informations de contact
email VARCHAR(255) NOT NULL UNIQUE,
telephone VARCHAR(20),
telephone_secondaire VARCHAR(20),
email_secondaire VARCHAR(255),
-- Adresse
adresse VARCHAR(500),
ville VARCHAR(100),
code_postal VARCHAR(20),
region VARCHAR(100),
pays VARCHAR(100),
-- Coordonnées géographiques
latitude DECIMAL(9,6) CHECK (latitude >= -90 AND latitude <= 90),
longitude DECIMAL(9,6) CHECK (longitude >= -180 AND longitude <= 180),
-- Web et réseaux sociaux
site_web VARCHAR(500),
logo VARCHAR(500),
reseaux_sociaux VARCHAR(1000),
-- Hiérarchie
organisation_parente_id UUID,
niveau_hierarchique INTEGER NOT NULL DEFAULT 0,
-- Statistiques
nombre_membres INTEGER NOT NULL DEFAULT 0,
nombre_administrateurs INTEGER NOT NULL DEFAULT 0,
-- Finances
budget_annuel DECIMAL(14,2) CHECK (budget_annuel >= 0),
devise VARCHAR(3) DEFAULT 'XOF',
cotisation_obligatoire BOOLEAN NOT NULL DEFAULT FALSE,
montant_cotisation_annuelle DECIMAL(12,2) CHECK (montant_cotisation_annuelle >= 0),
-- Informations complémentaires
objectifs TEXT,
activites_principales TEXT,
certifications VARCHAR(500),
partenaires VARCHAR(1000),
notes VARCHAR(1000),
-- Paramètres
organisation_publique BOOLEAN NOT NULL DEFAULT TRUE,
accepte_nouveaux_membres BOOLEAN NOT NULL DEFAULT TRUE,
-- Métadonnées (héritées de 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,
-- Contraintes
CONSTRAINT chk_organisation_statut CHECK (statut IN ('ACTIVE', 'SUSPENDUE', 'DISSOUTE', 'EN_ATTENTE')),
CONSTRAINT chk_organisation_type CHECK (type_organisation IN (
'ASSOCIATION', 'LIONS_CLUB', 'ROTARY_CLUB', 'COOPERATIVE',
'FONDATION', 'ONG', 'SYNDICAT', 'AUTRE'
)),
CONSTRAINT chk_organisation_devise CHECK (devise IN ('XOF', 'EUR', 'USD', 'GBP', 'CHF')),
CONSTRAINT chk_organisation_niveau CHECK (niveau_hierarchique >= 0 AND niveau_hierarchique <= 10),
CONSTRAINT chk_organisation_membres CHECK (nombre_membres >= 0),
CONSTRAINT chk_organisation_admins CHECK (nombre_administrateurs >= 0),
-- Clé étrangère pour hiérarchie
CONSTRAINT fk_organisation_parente FOREIGN KEY (organisation_parente_id)
REFERENCES organisations(id) ON DELETE SET NULL
);
-- Table membres avec UUID
CREATE TABLE membres (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
numero_membre VARCHAR(20) UNIQUE NOT NULL,
prenom VARCHAR(100) NOT NULL,
nom VARCHAR(100) NOT NULL,
email VARCHAR(255) UNIQUE NOT NULL,
mot_de_passe VARCHAR(255),
telephone VARCHAR(20),
date_naissance DATE NOT NULL,
date_adhesion DATE NOT NULL,
roles VARCHAR(500),
-- Clé étrangère vers organisations
organisation_id UUID,
-- Métadonnées (héritées de 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_membre_organisation FOREIGN KEY (organisation_id)
REFERENCES organisations(id) ON DELETE SET NULL
);
-- Table cotisations avec UUID
CREATE TABLE cotisations (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
numero_reference VARCHAR(50) UNIQUE NOT NULL,
membre_id UUID NOT NULL,
type_cotisation VARCHAR(50) NOT NULL,
montant_du DECIMAL(12,2) NOT NULL CHECK (montant_du >= 0),
montant_paye DECIMAL(12,2) NOT NULL DEFAULT 0 CHECK (montant_paye >= 0),
code_devise VARCHAR(3) NOT NULL DEFAULT 'XOF',
statut VARCHAR(30) NOT NULL,
date_echeance DATE NOT NULL,
date_paiement TIMESTAMP,
description VARCHAR(500),
periode VARCHAR(20),
annee INTEGER NOT NULL CHECK (annee >= 2020 AND annee <= 2100),
mois INTEGER CHECK (mois >= 1 AND mois <= 12),
observations VARCHAR(1000),
recurrente BOOLEAN NOT NULL DEFAULT FALSE,
nombre_rappels INTEGER NOT NULL DEFAULT 0 CHECK (nombre_rappels >= 0),
date_dernier_rappel TIMESTAMP,
valide_par_id UUID,
nom_validateur VARCHAR(100),
date_validation TIMESTAMP,
methode_paiement VARCHAR(50),
reference_paiement VARCHAR(100),
-- Métadonnées (héritées de 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_cotisation_membre FOREIGN KEY (membre_id)
REFERENCES membres(id) ON DELETE CASCADE,
CONSTRAINT chk_cotisation_statut CHECK (statut IN ('EN_ATTENTE', 'PAYEE', 'EN_RETARD', 'PARTIELLEMENT_PAYEE', 'ANNULEE')),
CONSTRAINT chk_cotisation_devise CHECK (code_devise ~ '^[A-Z]{3}$')
);
-- Table evenements avec UUID
CREATE TABLE evenements (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
titre VARCHAR(200) NOT NULL,
description VARCHAR(2000),
date_debut TIMESTAMP NOT NULL,
date_fin TIMESTAMP,
lieu VARCHAR(255) NOT NULL,
adresse VARCHAR(500),
ville VARCHAR(100),
pays VARCHAR(100),
code_postal VARCHAR(20),
latitude DECIMAL(9,6),
longitude DECIMAL(9,6),
type_evenement VARCHAR(50) NOT NULL,
statut VARCHAR(50) NOT NULL,
url_inscription VARCHAR(500),
url_informations VARCHAR(500),
image_url VARCHAR(500),
capacite_max INTEGER,
cout_participation DECIMAL(12,2),
devise VARCHAR(3),
est_public BOOLEAN NOT NULL DEFAULT TRUE,
tags VARCHAR(500),
notes VARCHAR(1000),
-- Clé étrangère vers organisations
organisation_id UUID,
-- Métadonnées (héritées de 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_evenement_organisation FOREIGN KEY (organisation_id)
REFERENCES organisations(id) ON DELETE SET NULL
);
-- Table inscriptions_evenement avec UUID
CREATE TABLE inscriptions_evenement (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
membre_id UUID NOT NULL,
evenement_id UUID NOT NULL,
date_inscription TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
statut VARCHAR(20) DEFAULT 'CONFIRMEE',
commentaire VARCHAR(500),
-- Métadonnées (héritées de 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_inscription_membre FOREIGN KEY (membre_id)
REFERENCES membres(id) ON DELETE CASCADE,
CONSTRAINT fk_inscription_evenement FOREIGN KEY (evenement_id)
REFERENCES evenements(id) ON DELETE CASCADE,
CONSTRAINT chk_inscription_statut CHECK (statut IN ('CONFIRMEE', 'EN_ATTENTE', 'ANNULEE', 'REFUSEE')),
CONSTRAINT uk_inscription_membre_evenement UNIQUE (membre_id, evenement_id)
);
-- Table demandes_aide avec UUID
CREATE TABLE demandes_aide (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
titre VARCHAR(200) NOT NULL,
description TEXT NOT NULL,
type_aide VARCHAR(50) NOT NULL,
statut VARCHAR(50) NOT NULL,
montant_demande DECIMAL(10,2),
montant_approuve DECIMAL(10,2),
date_demande TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
date_evaluation TIMESTAMP,
date_versement TIMESTAMP,
justification TEXT,
commentaire_evaluation TEXT,
urgence BOOLEAN NOT NULL DEFAULT FALSE,
documents_fournis VARCHAR(500),
-- Clés étrangères
demandeur_id UUID NOT NULL,
evaluateur_id UUID,
organisation_id UUID NOT NULL,
-- Métadonnées (héritées de 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_demande_demandeur FOREIGN KEY (demandeur_id)
REFERENCES membres(id) ON DELETE CASCADE,
CONSTRAINT fk_demande_evaluateur FOREIGN KEY (evaluateur_id)
REFERENCES membres(id) ON DELETE SET NULL,
CONSTRAINT fk_demande_organisation FOREIGN KEY (organisation_id)
REFERENCES organisations(id) ON DELETE CASCADE
);
-- ============================================
-- ÉTAPE 5: Recréer les index
-- ============================================
-- Index pour organisations
CREATE INDEX idx_organisation_nom ON organisations(nom);
CREATE INDEX idx_organisation_email ON organisations(email);
CREATE INDEX idx_organisation_statut ON organisations(statut);
CREATE INDEX idx_organisation_type ON organisations(type_organisation);
CREATE INDEX idx_organisation_ville ON organisations(ville);
CREATE INDEX idx_organisation_pays ON organisations(pays);
CREATE INDEX idx_organisation_parente ON organisations(organisation_parente_id);
CREATE INDEX idx_organisation_numero_enregistrement ON organisations(numero_enregistrement);
CREATE INDEX idx_organisation_actif ON organisations(actif);
CREATE INDEX idx_organisation_date_creation ON organisations(date_creation);
CREATE INDEX idx_organisation_publique ON organisations(organisation_publique);
CREATE INDEX idx_organisation_accepte_membres ON organisations(accepte_nouveaux_membres);
CREATE INDEX idx_organisation_statut_actif ON organisations(statut, actif);
-- Index pour membres
CREATE INDEX idx_membre_email ON membres(email);
CREATE INDEX idx_membre_numero ON membres(numero_membre);
CREATE INDEX idx_membre_actif ON membres(actif);
CREATE INDEX idx_membre_organisation ON membres(organisation_id);
-- Index pour cotisations
CREATE INDEX idx_cotisation_membre ON cotisations(membre_id);
CREATE INDEX idx_cotisation_reference ON cotisations(numero_reference);
CREATE INDEX idx_cotisation_statut ON cotisations(statut);
CREATE INDEX idx_cotisation_echeance ON cotisations(date_echeance);
CREATE INDEX idx_cotisation_type ON cotisations(type_cotisation);
CREATE INDEX idx_cotisation_annee_mois ON cotisations(annee, mois);
-- Index pour evenements
CREATE INDEX idx_evenement_date_debut ON evenements(date_debut);
CREATE INDEX idx_evenement_statut ON evenements(statut);
CREATE INDEX idx_evenement_type ON evenements(type_evenement);
CREATE INDEX idx_evenement_organisation ON evenements(organisation_id);
-- Index pour inscriptions_evenement
CREATE INDEX idx_inscription_membre ON inscriptions_evenement(membre_id);
CREATE INDEX idx_inscription_evenement ON inscriptions_evenement(evenement_id);
CREATE INDEX idx_inscription_date ON inscriptions_evenement(date_inscription);
-- Index pour demandes_aide
CREATE INDEX idx_demande_demandeur ON demandes_aide(demandeur_id);
CREATE INDEX idx_demande_evaluateur ON demandes_aide(evaluateur_id);
CREATE INDEX idx_demande_organisation ON demandes_aide(organisation_id);
CREATE INDEX idx_demande_statut ON demandes_aide(statut);
CREATE INDEX idx_demande_type ON demandes_aide(type_aide);
CREATE INDEX idx_demande_date_demande ON demandes_aide(date_demande);
-- ============================================
-- ÉTAPE 6: Commentaires sur les tables
-- ============================================
COMMENT ON TABLE organisations IS 'Table des organisations (Lions Clubs, Associations, Coopératives, etc.) avec UUID';
COMMENT ON TABLE membres IS 'Table des membres avec UUID';
COMMENT ON TABLE cotisations IS 'Table des cotisations avec UUID';
COMMENT ON TABLE evenements IS 'Table des événements avec UUID';
COMMENT ON TABLE inscriptions_evenement IS 'Table des inscriptions aux événements avec UUID';
COMMENT ON TABLE demandes_aide IS 'Table des demandes d''aide avec UUID';
COMMENT ON COLUMN organisations.id IS 'UUID unique de l''organisation';
COMMENT ON COLUMN membres.id IS 'UUID unique du membre';
COMMENT ON COLUMN cotisations.id IS 'UUID unique de la cotisation';
COMMENT ON COLUMN evenements.id IS 'UUID unique de l''événement';
COMMENT ON COLUMN inscriptions_evenement.id IS 'UUID unique de l''inscription';
COMMENT ON COLUMN demandes_aide.id IS 'UUID unique de la demande d''aide';
-- Migration V1.3: Conversion des colonnes ID de BIGINT vers UUID
-- Auteur: UnionFlow Team
-- Date: 2025-01-16
-- Description: Convertit toutes les colonnes ID et clés étrangères de BIGINT vers UUID
-- ATTENTION: Cette migration supprime toutes les données existantes pour simplifier la conversion
-- Pour une migration avec préservation des données, voir V1.3.1__Convert_Ids_To_UUID_With_Data.sql
-- ============================================
-- ÉTAPE 1: Suppression des contraintes de clés étrangères
-- ============================================
-- Supprimer les contraintes de clés étrangères existantes
DO $$
BEGIN
-- Supprimer FK membres -> organisations
IF EXISTS (
SELECT 1 FROM information_schema.table_constraints
WHERE constraint_name = 'fk_membre_organisation'
AND table_name = 'membres'
) THEN
ALTER TABLE membres DROP CONSTRAINT fk_membre_organisation;
END IF;
-- Supprimer FK cotisations -> membres
IF EXISTS (
SELECT 1 FROM information_schema.table_constraints
WHERE constraint_name LIKE 'fk_cotisation%'
AND table_name = 'cotisations'
) THEN
ALTER TABLE cotisations DROP CONSTRAINT IF EXISTS fk_cotisation_membre CASCADE;
END IF;
-- Supprimer FK evenements -> organisations
IF EXISTS (
SELECT 1 FROM information_schema.table_constraints
WHERE constraint_name LIKE 'fk_evenement%'
AND table_name = 'evenements'
) THEN
ALTER TABLE evenements DROP CONSTRAINT IF EXISTS fk_evenement_organisation CASCADE;
END IF;
-- Supprimer FK inscriptions_evenement -> membres et evenements
IF EXISTS (
SELECT 1 FROM information_schema.table_constraints
WHERE constraint_name LIKE 'fk_inscription%'
AND table_name = 'inscriptions_evenement'
) THEN
ALTER TABLE inscriptions_evenement DROP CONSTRAINT IF EXISTS fk_inscription_membre CASCADE;
ALTER TABLE inscriptions_evenement DROP CONSTRAINT IF EXISTS fk_inscription_evenement CASCADE;
END IF;
-- Supprimer FK demandes_aide -> membres et organisations
IF EXISTS (
SELECT 1 FROM information_schema.table_constraints
WHERE constraint_name LIKE 'fk_demande%'
AND table_name = 'demandes_aide'
) THEN
ALTER TABLE demandes_aide DROP CONSTRAINT IF EXISTS fk_demande_demandeur CASCADE;
ALTER TABLE demandes_aide DROP CONSTRAINT IF EXISTS fk_demande_evaluateur CASCADE;
ALTER TABLE demandes_aide DROP CONSTRAINT IF EXISTS fk_demande_organisation CASCADE;
END IF;
END $$;
-- ============================================
-- ÉTAPE 2: Supprimer les séquences (BIGSERIAL)
-- ============================================
DROP SEQUENCE IF EXISTS membres_SEQ CASCADE;
DROP SEQUENCE IF EXISTS cotisations_SEQ CASCADE;
DROP SEQUENCE IF EXISTS evenements_SEQ CASCADE;
DROP SEQUENCE IF EXISTS organisations_id_seq CASCADE;
-- ============================================
-- ÉTAPE 3: Supprimer les tables existantes (pour recréation avec UUID)
-- ============================================
-- Supprimer les tables dans l'ordre inverse des dépendances
DROP TABLE IF EXISTS inscriptions_evenement CASCADE;
DROP TABLE IF EXISTS demandes_aide CASCADE;
DROP TABLE IF EXISTS cotisations CASCADE;
DROP TABLE IF EXISTS evenements CASCADE;
DROP TABLE IF EXISTS membres CASCADE;
DROP TABLE IF EXISTS organisations CASCADE;
-- ============================================
-- ÉTAPE 4: Recréer les tables avec UUID
-- ============================================
-- Table organisations avec UUID
CREATE TABLE organisations (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
-- Informations de base
nom VARCHAR(200) NOT NULL,
nom_court VARCHAR(50),
type_organisation VARCHAR(50) NOT NULL DEFAULT 'ASSOCIATION',
statut VARCHAR(50) NOT NULL DEFAULT 'ACTIVE',
description TEXT,
date_fondation DATE,
numero_enregistrement VARCHAR(100) UNIQUE,
-- Informations de contact
email VARCHAR(255) NOT NULL UNIQUE,
telephone VARCHAR(20),
telephone_secondaire VARCHAR(20),
email_secondaire VARCHAR(255),
-- Adresse
adresse VARCHAR(500),
ville VARCHAR(100),
code_postal VARCHAR(20),
region VARCHAR(100),
pays VARCHAR(100),
-- Coordonnées géographiques
latitude DECIMAL(9,6) CHECK (latitude >= -90 AND latitude <= 90),
longitude DECIMAL(9,6) CHECK (longitude >= -180 AND longitude <= 180),
-- Web et réseaux sociaux
site_web VARCHAR(500),
logo VARCHAR(500),
reseaux_sociaux VARCHAR(1000),
-- Hiérarchie
organisation_parente_id UUID,
niveau_hierarchique INTEGER NOT NULL DEFAULT 0,
-- Statistiques
nombre_membres INTEGER NOT NULL DEFAULT 0,
nombre_administrateurs INTEGER NOT NULL DEFAULT 0,
-- Finances
budget_annuel DECIMAL(14,2) CHECK (budget_annuel >= 0),
devise VARCHAR(3) DEFAULT 'XOF',
cotisation_obligatoire BOOLEAN NOT NULL DEFAULT FALSE,
montant_cotisation_annuelle DECIMAL(12,2) CHECK (montant_cotisation_annuelle >= 0),
-- Informations complémentaires
objectifs TEXT,
activites_principales TEXT,
certifications VARCHAR(500),
partenaires VARCHAR(1000),
notes VARCHAR(1000),
-- Paramètres
organisation_publique BOOLEAN NOT NULL DEFAULT TRUE,
accepte_nouveaux_membres BOOLEAN NOT NULL DEFAULT TRUE,
-- Métadonnées (héritées de 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,
-- Contraintes
CONSTRAINT chk_organisation_statut CHECK (statut IN ('ACTIVE', 'SUSPENDUE', 'DISSOUTE', 'EN_ATTENTE')),
CONSTRAINT chk_organisation_type CHECK (type_organisation IN (
'ASSOCIATION', 'LIONS_CLUB', 'ROTARY_CLUB', 'COOPERATIVE',
'FONDATION', 'ONG', 'SYNDICAT', 'AUTRE'
)),
CONSTRAINT chk_organisation_devise CHECK (devise IN ('XOF', 'EUR', 'USD', 'GBP', 'CHF')),
CONSTRAINT chk_organisation_niveau CHECK (niveau_hierarchique >= 0 AND niveau_hierarchique <= 10),
CONSTRAINT chk_organisation_membres CHECK (nombre_membres >= 0),
CONSTRAINT chk_organisation_admins CHECK (nombre_administrateurs >= 0),
-- Clé étrangère pour hiérarchie
CONSTRAINT fk_organisation_parente FOREIGN KEY (organisation_parente_id)
REFERENCES organisations(id) ON DELETE SET NULL
);
-- Table membres avec UUID
CREATE TABLE membres (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
numero_membre VARCHAR(20) UNIQUE NOT NULL,
prenom VARCHAR(100) NOT NULL,
nom VARCHAR(100) NOT NULL,
email VARCHAR(255) UNIQUE NOT NULL,
mot_de_passe VARCHAR(255),
telephone VARCHAR(20),
date_naissance DATE NOT NULL,
date_adhesion DATE NOT NULL,
roles VARCHAR(500),
-- Clé étrangère vers organisations
organisation_id UUID,
-- Métadonnées (héritées de 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_membre_organisation FOREIGN KEY (organisation_id)
REFERENCES organisations(id) ON DELETE SET NULL
);
-- Table cotisations avec UUID
CREATE TABLE cotisations (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
numero_reference VARCHAR(50) UNIQUE NOT NULL,
membre_id UUID NOT NULL,
type_cotisation VARCHAR(50) NOT NULL,
montant_du DECIMAL(12,2) NOT NULL CHECK (montant_du >= 0),
montant_paye DECIMAL(12,2) NOT NULL DEFAULT 0 CHECK (montant_paye >= 0),
code_devise VARCHAR(3) NOT NULL DEFAULT 'XOF',
statut VARCHAR(30) NOT NULL,
date_echeance DATE NOT NULL,
date_paiement TIMESTAMP,
description VARCHAR(500),
periode VARCHAR(20),
annee INTEGER NOT NULL CHECK (annee >= 2020 AND annee <= 2100),
mois INTEGER CHECK (mois >= 1 AND mois <= 12),
observations VARCHAR(1000),
recurrente BOOLEAN NOT NULL DEFAULT FALSE,
nombre_rappels INTEGER NOT NULL DEFAULT 0 CHECK (nombre_rappels >= 0),
date_dernier_rappel TIMESTAMP,
valide_par_id UUID,
nom_validateur VARCHAR(100),
date_validation TIMESTAMP,
methode_paiement VARCHAR(50),
reference_paiement VARCHAR(100),
-- Métadonnées (héritées de 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_cotisation_membre FOREIGN KEY (membre_id)
REFERENCES membres(id) ON DELETE CASCADE,
CONSTRAINT chk_cotisation_statut CHECK (statut IN ('EN_ATTENTE', 'PAYEE', 'EN_RETARD', 'PARTIELLEMENT_PAYEE', 'ANNULEE')),
CONSTRAINT chk_cotisation_devise CHECK (code_devise ~ '^[A-Z]{3}$')
);
-- Table evenements avec UUID
CREATE TABLE evenements (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
titre VARCHAR(200) NOT NULL,
description VARCHAR(2000),
date_debut TIMESTAMP NOT NULL,
date_fin TIMESTAMP,
lieu VARCHAR(255) NOT NULL,
adresse VARCHAR(500),
ville VARCHAR(100),
pays VARCHAR(100),
code_postal VARCHAR(20),
latitude DECIMAL(9,6),
longitude DECIMAL(9,6),
type_evenement VARCHAR(50) NOT NULL,
statut VARCHAR(50) NOT NULL,
url_inscription VARCHAR(500),
url_informations VARCHAR(500),
image_url VARCHAR(500),
capacite_max INTEGER,
cout_participation DECIMAL(12,2),
devise VARCHAR(3),
est_public BOOLEAN NOT NULL DEFAULT TRUE,
tags VARCHAR(500),
notes VARCHAR(1000),
-- Clé étrangère vers organisations
organisation_id UUID,
-- Métadonnées (héritées de 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_evenement_organisation FOREIGN KEY (organisation_id)
REFERENCES organisations(id) ON DELETE SET NULL
);
-- Table inscriptions_evenement avec UUID
CREATE TABLE inscriptions_evenement (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
membre_id UUID NOT NULL,
evenement_id UUID NOT NULL,
date_inscription TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
statut VARCHAR(20) DEFAULT 'CONFIRMEE',
commentaire VARCHAR(500),
-- Métadonnées (héritées de 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_inscription_membre FOREIGN KEY (membre_id)
REFERENCES membres(id) ON DELETE CASCADE,
CONSTRAINT fk_inscription_evenement FOREIGN KEY (evenement_id)
REFERENCES evenements(id) ON DELETE CASCADE,
CONSTRAINT chk_inscription_statut CHECK (statut IN ('CONFIRMEE', 'EN_ATTENTE', 'ANNULEE', 'REFUSEE')),
CONSTRAINT uk_inscription_membre_evenement UNIQUE (membre_id, evenement_id)
);
-- Table demandes_aide avec UUID
CREATE TABLE demandes_aide (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
titre VARCHAR(200) NOT NULL,
description TEXT NOT NULL,
type_aide VARCHAR(50) NOT NULL,
statut VARCHAR(50) NOT NULL,
montant_demande DECIMAL(10,2),
montant_approuve DECIMAL(10,2),
date_demande TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
date_evaluation TIMESTAMP,
date_versement TIMESTAMP,
justification TEXT,
commentaire_evaluation TEXT,
urgence BOOLEAN NOT NULL DEFAULT FALSE,
documents_fournis VARCHAR(500),
-- Clés étrangères
demandeur_id UUID NOT NULL,
evaluateur_id UUID,
organisation_id UUID NOT NULL,
-- Métadonnées (héritées de 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_demande_demandeur FOREIGN KEY (demandeur_id)
REFERENCES membres(id) ON DELETE CASCADE,
CONSTRAINT fk_demande_evaluateur FOREIGN KEY (evaluateur_id)
REFERENCES membres(id) ON DELETE SET NULL,
CONSTRAINT fk_demande_organisation FOREIGN KEY (organisation_id)
REFERENCES organisations(id) ON DELETE CASCADE
);
-- ============================================
-- ÉTAPE 5: Recréer les index
-- ============================================
-- Index pour organisations
CREATE INDEX idx_organisation_nom ON organisations(nom);
CREATE INDEX idx_organisation_email ON organisations(email);
CREATE INDEX idx_organisation_statut ON organisations(statut);
CREATE INDEX idx_organisation_type ON organisations(type_organisation);
CREATE INDEX idx_organisation_ville ON organisations(ville);
CREATE INDEX idx_organisation_pays ON organisations(pays);
CREATE INDEX idx_organisation_parente ON organisations(organisation_parente_id);
CREATE INDEX idx_organisation_numero_enregistrement ON organisations(numero_enregistrement);
CREATE INDEX idx_organisation_actif ON organisations(actif);
CREATE INDEX idx_organisation_date_creation ON organisations(date_creation);
CREATE INDEX idx_organisation_publique ON organisations(organisation_publique);
CREATE INDEX idx_organisation_accepte_membres ON organisations(accepte_nouveaux_membres);
CREATE INDEX idx_organisation_statut_actif ON organisations(statut, actif);
-- Index pour membres
CREATE INDEX idx_membre_email ON membres(email);
CREATE INDEX idx_membre_numero ON membres(numero_membre);
CREATE INDEX idx_membre_actif ON membres(actif);
CREATE INDEX idx_membre_organisation ON membres(organisation_id);
-- Index pour cotisations
CREATE INDEX idx_cotisation_membre ON cotisations(membre_id);
CREATE INDEX idx_cotisation_reference ON cotisations(numero_reference);
CREATE INDEX idx_cotisation_statut ON cotisations(statut);
CREATE INDEX idx_cotisation_echeance ON cotisations(date_echeance);
CREATE INDEX idx_cotisation_type ON cotisations(type_cotisation);
CREATE INDEX idx_cotisation_annee_mois ON cotisations(annee, mois);
-- Index pour evenements
CREATE INDEX idx_evenement_date_debut ON evenements(date_debut);
CREATE INDEX idx_evenement_statut ON evenements(statut);
CREATE INDEX idx_evenement_type ON evenements(type_evenement);
CREATE INDEX idx_evenement_organisation ON evenements(organisation_id);
-- Index pour inscriptions_evenement
CREATE INDEX idx_inscription_membre ON inscriptions_evenement(membre_id);
CREATE INDEX idx_inscription_evenement ON inscriptions_evenement(evenement_id);
CREATE INDEX idx_inscription_date ON inscriptions_evenement(date_inscription);
-- Index pour demandes_aide
CREATE INDEX idx_demande_demandeur ON demandes_aide(demandeur_id);
CREATE INDEX idx_demande_evaluateur ON demandes_aide(evaluateur_id);
CREATE INDEX idx_demande_organisation ON demandes_aide(organisation_id);
CREATE INDEX idx_demande_statut ON demandes_aide(statut);
CREATE INDEX idx_demande_type ON demandes_aide(type_aide);
CREATE INDEX idx_demande_date_demande ON demandes_aide(date_demande);
-- ============================================
-- ÉTAPE 6: Commentaires sur les tables
-- ============================================
COMMENT ON TABLE organisations IS 'Table des organisations (Lions Clubs, Associations, Coopératives, etc.) avec UUID';
COMMENT ON TABLE membres IS 'Table des membres avec UUID';
COMMENT ON TABLE cotisations IS 'Table des cotisations avec UUID';
COMMENT ON TABLE evenements IS 'Table des événements avec UUID';
COMMENT ON TABLE inscriptions_evenement IS 'Table des inscriptions aux événements avec UUID';
COMMENT ON TABLE demandes_aide IS 'Table des demandes d''aide avec UUID';
COMMENT ON COLUMN organisations.id IS 'UUID unique de l''organisation';
COMMENT ON COLUMN membres.id IS 'UUID unique du membre';
COMMENT ON COLUMN cotisations.id IS 'UUID unique de la cotisation';
COMMENT ON COLUMN evenements.id IS 'UUID unique de l''événement';
COMMENT ON COLUMN inscriptions_evenement.id IS 'UUID unique de l''inscription';
COMMENT ON COLUMN demandes_aide.id IS 'UUID unique de la demande d''aide';

View File

@@ -1,7 +1,7 @@
-- Migration V1.4: Ajout de la colonne profession à la table membres
-- Auteur: UnionFlow Team
-- Date: 2026-02-19
-- Description: Permet l'autocomplétion et le filtrage par profession (MembreDTO, MembreSearchCriteria)
ALTER TABLE membres ADD COLUMN IF NOT EXISTS profession VARCHAR(100);
COMMENT ON COLUMN membres.profession IS 'Profession du membre (ex. Ingénieur, Médecin)';
-- Migration V1.4: Ajout de la colonne profession à la table membres
-- Auteur: UnionFlow Team
-- Date: 2026-02-19
-- Description: Permet l'autocomplétion et le filtrage par profession (MembreDTO, MembreSearchCriteria)
ALTER TABLE membres ADD COLUMN IF NOT EXISTS profession VARCHAR(100);
COMMENT ON COLUMN membres.profession IS 'Profession du membre (ex. Ingénieur, Médecin)';

View File

@@ -1,217 +1,217 @@
-- Migration V1.4: Création des tables Tickets, Suggestions, Favoris et Configuration
-- Auteur: UnionFlow Team
-- Date: 2025-12-18
-- Description: Création des tables pour la gestion des tickets support, suggestions utilisateur, favoris et configuration système
-- ============================================
-- TABLE: tickets
-- ============================================
CREATE TABLE IF NOT EXISTS tickets (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
-- Champs de base
numero_ticket VARCHAR(50) NOT NULL UNIQUE,
utilisateur_id UUID NOT NULL,
sujet VARCHAR(255) NOT NULL,
description TEXT,
-- Classification
categorie VARCHAR(50), -- TECHNIQUE, FONCTIONNALITE, UTILISATION, COMPTE, AUTRE
priorite VARCHAR(50), -- BASSE, NORMALE, HAUTE, URGENTE
statut VARCHAR(50) DEFAULT 'OUVERT', -- OUVERT, EN_COURS, EN_ATTENTE, RESOLU, FERME
-- Gestion
agent_id UUID,
agent_nom VARCHAR(255),
-- Dates
date_creation TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
date_derniere_reponse TIMESTAMP,
date_resolution TIMESTAMP,
date_fermeture TIMESTAMP,
-- Statistiques
nb_messages INTEGER DEFAULT 0,
nb_fichiers INTEGER DEFAULT 0,
note_satisfaction INTEGER CHECK (note_satisfaction >= 1 AND note_satisfaction <= 5),
-- Résolution
resolution TEXT,
-- Audit (BaseEntity)
date_modification TIMESTAMP,
cree_par VARCHAR(255),
modifie_par VARCHAR(255),
version BIGINT DEFAULT 0,
actif BOOLEAN DEFAULT true NOT NULL,
-- Indexes
CONSTRAINT fk_ticket_utilisateur FOREIGN KEY (utilisateur_id) REFERENCES membres(id) ON DELETE CASCADE
);
CREATE INDEX idx_ticket_utilisateur ON tickets(utilisateur_id);
CREATE INDEX idx_ticket_statut ON tickets(statut);
CREATE INDEX idx_ticket_categorie ON tickets(categorie);
CREATE INDEX idx_ticket_numero ON tickets(numero_ticket);
CREATE INDEX idx_ticket_date_creation ON tickets(date_creation DESC);
-- ============================================
-- TABLE: suggestions
-- ============================================
CREATE TABLE IF NOT EXISTS suggestions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
-- Utilisateur
utilisateur_id UUID NOT NULL,
utilisateur_nom VARCHAR(255),
-- Contenu
titre VARCHAR(255) NOT NULL,
description TEXT,
justification TEXT,
-- Classification
categorie VARCHAR(50), -- UI, FEATURE, PERFORMANCE, SECURITE, INTEGRATION, MOBILE, REPORTING
priorite_estimee VARCHAR(50), -- BASSE, MOYENNE, HAUTE, CRITIQUE
statut VARCHAR(50) DEFAULT 'NOUVELLE', -- NOUVELLE, EVALUATION, APPROUVEE, DEVELOPPEMENT, IMPLEMENTEE, REJETEE
-- Statistiques
nb_votes INTEGER DEFAULT 0,
nb_commentaires INTEGER DEFAULT 0,
nb_vues INTEGER DEFAULT 0,
-- Dates
date_soumission TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
date_evaluation TIMESTAMP,
date_implementation TIMESTAMP,
-- Version
version_ciblee VARCHAR(50),
mise_a_jour TEXT,
-- Audit (BaseEntity)
date_creation TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
date_modification TIMESTAMP,
cree_par VARCHAR(255),
modifie_par VARCHAR(255),
version BIGINT DEFAULT 0,
actif BOOLEAN DEFAULT true NOT NULL
);
CREATE INDEX idx_suggestion_utilisateur ON suggestions(utilisateur_id);
CREATE INDEX idx_suggestion_statut ON suggestions(statut);
CREATE INDEX idx_suggestion_categorie ON suggestions(categorie);
CREATE INDEX idx_suggestion_date_soumission ON suggestions(date_soumission DESC);
CREATE INDEX idx_suggestion_nb_votes ON suggestions(nb_votes DESC);
-- ============================================
-- TABLE: suggestion_votes
-- ============================================
CREATE TABLE IF NOT EXISTS suggestion_votes (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
suggestion_id UUID NOT NULL,
utilisateur_id UUID NOT NULL,
date_vote TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
-- Audit
date_creation TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
actif BOOLEAN DEFAULT true NOT NULL,
-- Contrainte d'unicité : un utilisateur ne peut voter qu'une fois par suggestion
CONSTRAINT uk_suggestion_vote UNIQUE (suggestion_id, utilisateur_id),
CONSTRAINT fk_vote_suggestion FOREIGN KEY (suggestion_id) REFERENCES suggestions(id) ON DELETE CASCADE
);
CREATE INDEX idx_vote_suggestion ON suggestion_votes(suggestion_id);
CREATE INDEX idx_vote_utilisateur ON suggestion_votes(utilisateur_id);
-- ============================================
-- TABLE: favoris
-- ============================================
CREATE TABLE IF NOT EXISTS favoris (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
-- Utilisateur
utilisateur_id UUID NOT NULL,
-- Type et contenu
type_favori VARCHAR(50) NOT NULL, -- PAGE, DOCUMENT, CONTACT, RACCOURCI
titre VARCHAR(255) NOT NULL,
description VARCHAR(1000),
url VARCHAR(1000),
-- Présentation
icon VARCHAR(100),
couleur VARCHAR(50),
categorie VARCHAR(100),
-- Organisation
ordre INTEGER DEFAULT 0,
-- Statistiques
nb_visites INTEGER DEFAULT 0,
derniere_visite TIMESTAMP,
est_plus_utilise BOOLEAN DEFAULT false,
-- Audit (BaseEntity)
date_creation TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
date_modification TIMESTAMP,
cree_par VARCHAR(255),
modifie_par VARCHAR(255),
version BIGINT DEFAULT 0,
actif BOOLEAN DEFAULT true NOT NULL,
CONSTRAINT fk_favori_utilisateur FOREIGN KEY (utilisateur_id) REFERENCES membres(id) ON DELETE CASCADE
);
CREATE INDEX idx_favori_utilisateur ON favoris(utilisateur_id);
CREATE INDEX idx_favori_type ON favoris(type_favori);
CREATE INDEX idx_favori_categorie ON favoris(categorie);
CREATE INDEX idx_favori_ordre ON favoris(utilisateur_id, ordre);
-- ============================================
-- TABLE: configurations
-- ============================================
CREATE TABLE IF NOT EXISTS configurations (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
-- Clé unique
cle VARCHAR(255) NOT NULL UNIQUE,
-- Valeur
valeur TEXT,
type VARCHAR(50), -- STRING, NUMBER, BOOLEAN, JSON, DATE
-- Classification
categorie VARCHAR(50), -- SYSTEME, SECURITE, NOTIFICATION, INTEGRATION, APPEARANCE
description VARCHAR(1000),
-- Contrôles
modifiable BOOLEAN DEFAULT true,
visible BOOLEAN DEFAULT true,
-- Métadonnées (JSON stocké en TEXT)
metadonnees TEXT,
-- Audit (BaseEntity)
date_creation TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
date_modification TIMESTAMP,
cree_par VARCHAR(255),
modifie_par VARCHAR(255),
version BIGINT DEFAULT 0,
actif BOOLEAN DEFAULT true NOT NULL
);
CREATE INDEX idx_config_cle ON configurations(cle);
CREATE INDEX idx_config_categorie ON configurations(categorie);
CREATE INDEX idx_config_visible ON configurations(visible) WHERE visible = true;
-- ============================================
-- COMMENTAIRES
-- ============================================
COMMENT ON TABLE tickets IS 'Table pour la gestion des tickets support';
COMMENT ON TABLE suggestions IS 'Table pour la gestion des suggestions utilisateur';
COMMENT ON TABLE suggestion_votes IS 'Table pour gérer les votes sur les suggestions (évite les votes multiples)';
COMMENT ON TABLE favoris IS 'Table pour la gestion des favoris utilisateur';
COMMENT ON TABLE configurations IS 'Table pour la gestion de la configuration système';
-- Migration V1.4: Création des tables Tickets, Suggestions, Favoris et Configuration
-- Auteur: UnionFlow Team
-- Date: 2025-12-18
-- Description: Création des tables pour la gestion des tickets support, suggestions utilisateur, favoris et configuration système
-- ============================================
-- TABLE: tickets
-- ============================================
CREATE TABLE IF NOT EXISTS tickets (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
-- Champs de base
numero_ticket VARCHAR(50) NOT NULL UNIQUE,
utilisateur_id UUID NOT NULL,
sujet VARCHAR(255) NOT NULL,
description TEXT,
-- Classification
categorie VARCHAR(50), -- TECHNIQUE, FONCTIONNALITE, UTILISATION, COMPTE, AUTRE
priorite VARCHAR(50), -- BASSE, NORMALE, HAUTE, URGENTE
statut VARCHAR(50) DEFAULT 'OUVERT', -- OUVERT, EN_COURS, EN_ATTENTE, RESOLU, FERME
-- Gestion
agent_id UUID,
agent_nom VARCHAR(255),
-- Dates
date_creation TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
date_derniere_reponse TIMESTAMP,
date_resolution TIMESTAMP,
date_fermeture TIMESTAMP,
-- Statistiques
nb_messages INTEGER DEFAULT 0,
nb_fichiers INTEGER DEFAULT 0,
note_satisfaction INTEGER CHECK (note_satisfaction >= 1 AND note_satisfaction <= 5),
-- Résolution
resolution TEXT,
-- Audit (BaseEntity)
date_modification TIMESTAMP,
cree_par VARCHAR(255),
modifie_par VARCHAR(255),
version BIGINT DEFAULT 0,
actif BOOLEAN DEFAULT true NOT NULL,
-- Indexes
CONSTRAINT fk_ticket_utilisateur FOREIGN KEY (utilisateur_id) REFERENCES membres(id) ON DELETE CASCADE
);
CREATE INDEX idx_ticket_utilisateur ON tickets(utilisateur_id);
CREATE INDEX idx_ticket_statut ON tickets(statut);
CREATE INDEX idx_ticket_categorie ON tickets(categorie);
CREATE INDEX idx_ticket_numero ON tickets(numero_ticket);
CREATE INDEX idx_ticket_date_creation ON tickets(date_creation DESC);
-- ============================================
-- TABLE: suggestions
-- ============================================
CREATE TABLE IF NOT EXISTS suggestions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
-- Utilisateur
utilisateur_id UUID NOT NULL,
utilisateur_nom VARCHAR(255),
-- Contenu
titre VARCHAR(255) NOT NULL,
description TEXT,
justification TEXT,
-- Classification
categorie VARCHAR(50), -- UI, FEATURE, PERFORMANCE, SECURITE, INTEGRATION, MOBILE, REPORTING
priorite_estimee VARCHAR(50), -- BASSE, MOYENNE, HAUTE, CRITIQUE
statut VARCHAR(50) DEFAULT 'NOUVELLE', -- NOUVELLE, EVALUATION, APPROUVEE, DEVELOPPEMENT, IMPLEMENTEE, REJETEE
-- Statistiques
nb_votes INTEGER DEFAULT 0,
nb_commentaires INTEGER DEFAULT 0,
nb_vues INTEGER DEFAULT 0,
-- Dates
date_soumission TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
date_evaluation TIMESTAMP,
date_implementation TIMESTAMP,
-- Version
version_ciblee VARCHAR(50),
mise_a_jour TEXT,
-- Audit (BaseEntity)
date_creation TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
date_modification TIMESTAMP,
cree_par VARCHAR(255),
modifie_par VARCHAR(255),
version BIGINT DEFAULT 0,
actif BOOLEAN DEFAULT true NOT NULL
);
CREATE INDEX idx_suggestion_utilisateur ON suggestions(utilisateur_id);
CREATE INDEX idx_suggestion_statut ON suggestions(statut);
CREATE INDEX idx_suggestion_categorie ON suggestions(categorie);
CREATE INDEX idx_suggestion_date_soumission ON suggestions(date_soumission DESC);
CREATE INDEX idx_suggestion_nb_votes ON suggestions(nb_votes DESC);
-- ============================================
-- TABLE: suggestion_votes
-- ============================================
CREATE TABLE IF NOT EXISTS suggestion_votes (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
suggestion_id UUID NOT NULL,
utilisateur_id UUID NOT NULL,
date_vote TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
-- Audit
date_creation TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
actif BOOLEAN DEFAULT true NOT NULL,
-- Contrainte d'unicité : un utilisateur ne peut voter qu'une fois par suggestion
CONSTRAINT uk_suggestion_vote UNIQUE (suggestion_id, utilisateur_id),
CONSTRAINT fk_vote_suggestion FOREIGN KEY (suggestion_id) REFERENCES suggestions(id) ON DELETE CASCADE
);
CREATE INDEX idx_vote_suggestion ON suggestion_votes(suggestion_id);
CREATE INDEX idx_vote_utilisateur ON suggestion_votes(utilisateur_id);
-- ============================================
-- TABLE: favoris
-- ============================================
CREATE TABLE IF NOT EXISTS favoris (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
-- Utilisateur
utilisateur_id UUID NOT NULL,
-- Type et contenu
type_favori VARCHAR(50) NOT NULL, -- PAGE, DOCUMENT, CONTACT, RACCOURCI
titre VARCHAR(255) NOT NULL,
description VARCHAR(1000),
url VARCHAR(1000),
-- Présentation
icon VARCHAR(100),
couleur VARCHAR(50),
categorie VARCHAR(100),
-- Organisation
ordre INTEGER DEFAULT 0,
-- Statistiques
nb_visites INTEGER DEFAULT 0,
derniere_visite TIMESTAMP,
est_plus_utilise BOOLEAN DEFAULT false,
-- Audit (BaseEntity)
date_creation TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
date_modification TIMESTAMP,
cree_par VARCHAR(255),
modifie_par VARCHAR(255),
version BIGINT DEFAULT 0,
actif BOOLEAN DEFAULT true NOT NULL,
CONSTRAINT fk_favori_utilisateur FOREIGN KEY (utilisateur_id) REFERENCES membres(id) ON DELETE CASCADE
);
CREATE INDEX idx_favori_utilisateur ON favoris(utilisateur_id);
CREATE INDEX idx_favori_type ON favoris(type_favori);
CREATE INDEX idx_favori_categorie ON favoris(categorie);
CREATE INDEX idx_favori_ordre ON favoris(utilisateur_id, ordre);
-- ============================================
-- TABLE: configurations
-- ============================================
CREATE TABLE IF NOT EXISTS configurations (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
-- Clé unique
cle VARCHAR(255) NOT NULL UNIQUE,
-- Valeur
valeur TEXT,
type VARCHAR(50), -- STRING, NUMBER, BOOLEAN, JSON, DATE
-- Classification
categorie VARCHAR(50), -- SYSTEME, SECURITE, NOTIFICATION, INTEGRATION, APPEARANCE
description VARCHAR(1000),
-- Contrôles
modifiable BOOLEAN DEFAULT true,
visible BOOLEAN DEFAULT true,
-- Métadonnées (JSON stocké en TEXT)
metadonnees TEXT,
-- Audit (BaseEntity)
date_creation TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
date_modification TIMESTAMP,
cree_par VARCHAR(255),
modifie_par VARCHAR(255),
version BIGINT DEFAULT 0,
actif BOOLEAN DEFAULT true NOT NULL
);
CREATE INDEX idx_config_cle ON configurations(cle);
CREATE INDEX idx_config_categorie ON configurations(categorie);
CREATE INDEX idx_config_visible ON configurations(visible) WHERE visible = true;
-- ============================================
-- COMMENTAIRES
-- ============================================
COMMENT ON TABLE tickets IS 'Table pour la gestion des tickets support';
COMMENT ON TABLE suggestions IS 'Table pour la gestion des suggestions utilisateur';
COMMENT ON TABLE suggestion_votes IS 'Table pour gérer les votes sur les suggestions (évite les votes multiples)';
COMMENT ON TABLE favoris IS 'Table pour la gestion des favoris utilisateur';
COMMENT ON TABLE configurations IS 'Table pour la gestion de la configuration système';

View File

@@ -1,24 +1,24 @@
-- Migration V1.5: Ajout des champs de liaison avec Keycloak dans la table membres
-- Date: 2025-12-24
-- Description: Permet de lier un Membre (business) à un User Keycloak (authentification)
-- Ajouter la colonne keycloak_user_id pour stocker l'UUID du user Keycloak
ALTER TABLE membres
ADD COLUMN IF NOT EXISTS keycloak_user_id VARCHAR(36);
-- Ajouter la colonne keycloak_realm pour stocker le nom du realm (généralement "unionflow")
ALTER TABLE membres
ADD COLUMN IF NOT EXISTS keycloak_realm VARCHAR(50);
-- Créer un index unique sur keycloak_user_id pour garantir l'unicité et optimiser les recherches
-- Un user Keycloak ne peut être lié qu'à un seul Membre
CREATE UNIQUE INDEX IF NOT EXISTS idx_membre_keycloak_user
ON membres(keycloak_user_id)
WHERE keycloak_user_id IS NOT NULL;
-- Ajouter un commentaire pour la documentation
COMMENT ON COLUMN membres.keycloak_user_id IS 'UUID du user Keycloak lié à ce membre. NULL si le membre n''a pas de compte de connexion.';
COMMENT ON COLUMN membres.keycloak_realm IS 'Nom du realm Keycloak où le user est enregistré (ex: "unionflow", "btpxpress"). NULL si pas de compte Keycloak.';
-- Note: Le champ mot_de_passe existant devrait être déprécié car Keycloak est la source de vérité pour l'authentification
-- Cependant, on le conserve pour compatibilité avec les données existantes et migration progressive
-- Migration V1.5: Ajout des champs de liaison avec Keycloak dans la table membres
-- Date: 2025-12-24
-- Description: Permet de lier un Membre (business) à un User Keycloak (authentification)
-- Ajouter la colonne keycloak_user_id pour stocker l'UUID du user Keycloak
ALTER TABLE membres
ADD COLUMN IF NOT EXISTS keycloak_user_id VARCHAR(36);
-- Ajouter la colonne keycloak_realm pour stocker le nom du realm (généralement "unionflow")
ALTER TABLE membres
ADD COLUMN IF NOT EXISTS keycloak_realm VARCHAR(50);
-- Créer un index unique sur keycloak_user_id pour garantir l'unicité et optimiser les recherches
-- Un user Keycloak ne peut être lié qu'à un seul Membre
CREATE UNIQUE INDEX IF NOT EXISTS idx_membre_keycloak_user
ON membres(keycloak_user_id)
WHERE keycloak_user_id IS NOT NULL;
-- Ajouter un commentaire pour la documentation
COMMENT ON COLUMN membres.keycloak_user_id IS 'UUID du user Keycloak lié à ce membre. NULL si le membre n''a pas de compte de connexion.';
COMMENT ON COLUMN membres.keycloak_realm IS 'Nom du realm Keycloak où le user est enregistré (ex: "unionflow", "btpxpress"). NULL si pas de compte Keycloak.';
-- Note: Le champ mot_de_passe existant devrait être déprécié car Keycloak est la source de vérité pour l'authentification
-- Cependant, on le conserve pour compatibilité avec les données existantes et migration progressive

View File

@@ -1,96 +1,96 @@
-- ============================================================
-- V2.0 — Refactoring: membres → utilisateurs
-- Sépare l'identité globale (utilisateurs) du lien organisationnel
-- Auteur: UnionFlow Team | BCEAO/OHADA compliant
-- ============================================================
-- Renommer la table membres → utilisateurs
ALTER TABLE membres RENAME TO utilisateurs;
-- Supprimer l'ancien lien unique membre↔organisation (maintenant dans membres_organisations)
ALTER TABLE utilisateurs DROP COLUMN IF EXISTS organisation_id;
ALTER TABLE utilisateurs DROP COLUMN IF EXISTS date_adhesion;
ALTER TABLE utilisateurs DROP COLUMN IF EXISTS mot_de_passe;
ALTER TABLE utilisateurs DROP COLUMN IF EXISTS roles;
-- Ajouter les nouveaux champs identité globale
ALTER TABLE utilisateurs ADD COLUMN IF NOT EXISTS keycloak_id UUID UNIQUE;
ALTER TABLE utilisateurs ADD COLUMN IF NOT EXISTS photo_url VARCHAR(500);
ALTER TABLE utilisateurs ADD COLUMN IF NOT EXISTS statut_compte VARCHAR(30) NOT NULL DEFAULT 'ACTIF';
ALTER TABLE utilisateurs ADD COLUMN IF NOT EXISTS telephone_wave VARCHAR(13);
-- Mettre à jour la contrainte de statut compte
ALTER TABLE utilisateurs
ADD CONSTRAINT chk_utilisateur_statut_compte
CHECK (statut_compte IN ('ACTIF', 'SUSPENDU', 'DESACTIVE'));
-- Mettre à jour les index
DROP INDEX IF EXISTS idx_membre_organisation;
DROP INDEX IF EXISTS idx_membre_email;
DROP INDEX IF EXISTS idx_membre_numero;
DROP INDEX IF EXISTS idx_membre_actif;
CREATE UNIQUE INDEX IF NOT EXISTS idx_utilisateur_email ON utilisateurs(email);
CREATE UNIQUE INDEX IF NOT EXISTS idx_utilisateur_numero ON utilisateurs(numero_membre);
CREATE INDEX IF NOT EXISTS idx_utilisateur_actif ON utilisateurs(actif);
CREATE UNIQUE INDEX IF NOT EXISTS idx_utilisateur_keycloak ON utilisateurs(keycloak_id);
CREATE INDEX IF NOT EXISTS idx_utilisateur_statut_compte ON utilisateurs(statut_compte);
-- ============================================================
-- Table membres_organisations : lien utilisateur ↔ organisation
-- ============================================================
CREATE TABLE IF NOT EXISTS membres_organisations (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
utilisateur_id UUID NOT NULL,
organisation_id UUID NOT NULL,
unite_id UUID, -- agence/bureau d'affectation (null = siège)
statut_membre VARCHAR(30) NOT NULL DEFAULT 'EN_ATTENTE_VALIDATION',
date_adhesion DATE,
date_changement_statut DATE,
motif_statut VARCHAR(500),
approuve_par_id UUID,
-- 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_mo_utilisateur FOREIGN KEY (utilisateur_id) REFERENCES utilisateurs(id) ON DELETE CASCADE,
CONSTRAINT fk_mo_organisation FOREIGN KEY (organisation_id) REFERENCES organisations(id) ON DELETE CASCADE,
CONSTRAINT fk_mo_unite FOREIGN KEY (unite_id) REFERENCES organisations(id) ON DELETE SET NULL,
CONSTRAINT fk_mo_approuve_par FOREIGN KEY (approuve_par_id) REFERENCES utilisateurs(id) ON DELETE SET NULL,
CONSTRAINT uk_mo_utilisateur_organisation UNIQUE (utilisateur_id, organisation_id),
CONSTRAINT chk_mo_statut CHECK (statut_membre IN (
'EN_ATTENTE_VALIDATION','ACTIF','INACTIF',
'SUSPENDU','DEMISSIONNAIRE','RADIE','HONORAIRE','DECEDE'
))
);
CREATE INDEX idx_mo_utilisateur ON membres_organisations(utilisateur_id);
CREATE INDEX idx_mo_organisation ON membres_organisations(organisation_id);
CREATE INDEX idx_mo_statut ON membres_organisations(statut_membre);
CREATE INDEX idx_mo_unite ON membres_organisations(unite_id);
-- Mettre à jour les FK des tables existantes qui pointaient sur membres(id)
ALTER TABLE cotisations
DROP CONSTRAINT IF EXISTS fk_cotisation_membre,
ADD CONSTRAINT fk_cotisation_membre FOREIGN KEY (membre_id) REFERENCES utilisateurs(id);
ALTER TABLE inscriptions_evenement
DROP CONSTRAINT IF EXISTS fk_inscription_membre,
ADD CONSTRAINT fk_inscription_membre FOREIGN KEY (membre_id) REFERENCES utilisateurs(id);
ALTER TABLE demandes_aide
DROP CONSTRAINT IF EXISTS fk_demande_demandeur,
DROP CONSTRAINT IF EXISTS fk_demande_evaluateur,
ADD CONSTRAINT fk_demande_demandeur FOREIGN KEY (demandeur_id) REFERENCES utilisateurs(id),
ADD CONSTRAINT fk_demande_evaluateur FOREIGN KEY (evaluateur_id) REFERENCES utilisateurs(id) ON DELETE SET NULL;
COMMENT ON TABLE utilisateurs IS 'Identité globale unique de chaque utilisateur UnionFlow (1 compte = 1 profil)';
COMMENT ON TABLE membres_organisations IS 'Lien utilisateur ↔ organisation avec statut de membership';
COMMENT ON COLUMN membres_organisations.unite_id IS 'Agence/bureau d''affectation au sein de la hiérarchie. NULL = siège';
-- ============================================================
-- V2.0 — Refactoring: membres → utilisateurs
-- Sépare l'identité globale (utilisateurs) du lien organisationnel
-- Auteur: UnionFlow Team | BCEAO/OHADA compliant
-- ============================================================
-- Renommer la table membres → utilisateurs
ALTER TABLE membres RENAME TO utilisateurs;
-- Supprimer l'ancien lien unique membre↔organisation (maintenant dans membres_organisations)
ALTER TABLE utilisateurs DROP COLUMN IF EXISTS organisation_id;
ALTER TABLE utilisateurs DROP COLUMN IF EXISTS date_adhesion;
ALTER TABLE utilisateurs DROP COLUMN IF EXISTS mot_de_passe;
ALTER TABLE utilisateurs DROP COLUMN IF EXISTS roles;
-- Ajouter les nouveaux champs identité globale
ALTER TABLE utilisateurs ADD COLUMN IF NOT EXISTS keycloak_id UUID UNIQUE;
ALTER TABLE utilisateurs ADD COLUMN IF NOT EXISTS photo_url VARCHAR(500);
ALTER TABLE utilisateurs ADD COLUMN IF NOT EXISTS statut_compte VARCHAR(30) NOT NULL DEFAULT 'ACTIF';
ALTER TABLE utilisateurs ADD COLUMN IF NOT EXISTS telephone_wave VARCHAR(13);
-- Mettre à jour la contrainte de statut compte
ALTER TABLE utilisateurs
ADD CONSTRAINT chk_utilisateur_statut_compte
CHECK (statut_compte IN ('ACTIF', 'SUSPENDU', 'DESACTIVE'));
-- Mettre à jour les index
DROP INDEX IF EXISTS idx_membre_organisation;
DROP INDEX IF EXISTS idx_membre_email;
DROP INDEX IF EXISTS idx_membre_numero;
DROP INDEX IF EXISTS idx_membre_actif;
CREATE UNIQUE INDEX IF NOT EXISTS idx_utilisateur_email ON utilisateurs(email);
CREATE UNIQUE INDEX IF NOT EXISTS idx_utilisateur_numero ON utilisateurs(numero_membre);
CREATE INDEX IF NOT EXISTS idx_utilisateur_actif ON utilisateurs(actif);
CREATE UNIQUE INDEX IF NOT EXISTS idx_utilisateur_keycloak ON utilisateurs(keycloak_id);
CREATE INDEX IF NOT EXISTS idx_utilisateur_statut_compte ON utilisateurs(statut_compte);
-- ============================================================
-- Table membres_organisations : lien utilisateur ↔ organisation
-- ============================================================
CREATE TABLE IF NOT EXISTS membres_organisations (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
utilisateur_id UUID NOT NULL,
organisation_id UUID NOT NULL,
unite_id UUID, -- agence/bureau d'affectation (null = siège)
statut_membre VARCHAR(30) NOT NULL DEFAULT 'EN_ATTENTE_VALIDATION',
date_adhesion DATE,
date_changement_statut DATE,
motif_statut VARCHAR(500),
approuve_par_id UUID,
-- 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_mo_utilisateur FOREIGN KEY (utilisateur_id) REFERENCES utilisateurs(id) ON DELETE CASCADE,
CONSTRAINT fk_mo_organisation FOREIGN KEY (organisation_id) REFERENCES organisations(id) ON DELETE CASCADE,
CONSTRAINT fk_mo_unite FOREIGN KEY (unite_id) REFERENCES organisations(id) ON DELETE SET NULL,
CONSTRAINT fk_mo_approuve_par FOREIGN KEY (approuve_par_id) REFERENCES utilisateurs(id) ON DELETE SET NULL,
CONSTRAINT uk_mo_utilisateur_organisation UNIQUE (utilisateur_id, organisation_id),
CONSTRAINT chk_mo_statut CHECK (statut_membre IN (
'EN_ATTENTE_VALIDATION','ACTIF','INACTIF',
'SUSPENDU','DEMISSIONNAIRE','RADIE','HONORAIRE','DECEDE'
))
);
CREATE INDEX idx_mo_utilisateur ON membres_organisations(utilisateur_id);
CREATE INDEX idx_mo_organisation ON membres_organisations(organisation_id);
CREATE INDEX idx_mo_statut ON membres_organisations(statut_membre);
CREATE INDEX idx_mo_unite ON membres_organisations(unite_id);
-- Mettre à jour les FK des tables existantes qui pointaient sur membres(id)
ALTER TABLE cotisations
DROP CONSTRAINT IF EXISTS fk_cotisation_membre,
ADD CONSTRAINT fk_cotisation_membre FOREIGN KEY (membre_id) REFERENCES utilisateurs(id);
ALTER TABLE inscriptions_evenement
DROP CONSTRAINT IF EXISTS fk_inscription_membre,
ADD CONSTRAINT fk_inscription_membre FOREIGN KEY (membre_id) REFERENCES utilisateurs(id);
ALTER TABLE demandes_aide
DROP CONSTRAINT IF EXISTS fk_demande_demandeur,
DROP CONSTRAINT IF EXISTS fk_demande_evaluateur,
ADD CONSTRAINT fk_demande_demandeur FOREIGN KEY (demandeur_id) REFERENCES utilisateurs(id),
ADD CONSTRAINT fk_demande_evaluateur FOREIGN KEY (evaluateur_id) REFERENCES utilisateurs(id) ON DELETE SET NULL;
COMMENT ON TABLE utilisateurs IS 'Identité globale unique de chaque utilisateur UnionFlow (1 compte = 1 profil)';
COMMENT ON TABLE membres_organisations IS 'Lien utilisateur ↔ organisation avec statut de membership';
COMMENT ON COLUMN membres_organisations.unite_id IS 'Agence/bureau d''affectation au sein de la hiérarchie. NULL = siège';

View File

@@ -1,20 +1,20 @@
-- ============================================================
-- V2.10 — Devises : liste strictement africaine
-- Remplace EUR, USD, GBP, CHF par des codes africains (XOF par défaut)
-- ============================================================
-- Migrer les organisations avec une devise non africaine vers XOF
UPDATE organisations
SET devise = 'XOF'
WHERE devise IS NOT NULL
AND devise NOT IN ('XOF', 'XAF', 'MAD', 'DZD', 'TND', 'NGN', 'GHS', 'KES', 'ZAR');
-- Remplacer la contrainte par une liste africaine uniquement
ALTER TABLE organisations DROP CONSTRAINT IF EXISTS chk_organisation_devise;
ALTER TABLE organisations
ADD CONSTRAINT chk_organisation_devise CHECK (
devise IN ('XOF', 'XAF', 'MAD', 'DZD', 'TND', 'NGN', 'GHS', 'KES', 'ZAR')
);
COMMENT ON COLUMN organisations.devise IS 'Code ISO 4217 — devises africaines uniquement (XOF, XAF, MAD, DZD, TND, NGN, GHS, KES, ZAR)';
-- ============================================================
-- V2.10 — Devises : liste strictement africaine
-- Remplace EUR, USD, GBP, CHF par des codes africains (XOF par défaut)
-- ============================================================
-- Migrer les organisations avec une devise non africaine vers XOF
UPDATE organisations
SET devise = 'XOF'
WHERE devise IS NOT NULL
AND devise NOT IN ('XOF', 'XAF', 'MAD', 'DZD', 'TND', 'NGN', 'GHS', 'KES', 'ZAR');
-- Remplacer la contrainte par une liste africaine uniquement
ALTER TABLE organisations DROP CONSTRAINT IF EXISTS chk_organisation_devise;
ALTER TABLE organisations
ADD CONSTRAINT chk_organisation_devise CHECK (
devise IN ('XOF', 'XAF', 'MAD', 'DZD', 'TND', 'NGN', 'GHS', 'KES', 'ZAR')
);
COMMENT ON COLUMN organisations.devise IS 'Code ISO 4217 — devises africaines uniquement (XOF, XAF, MAD, DZD, TND, NGN, GHS, KES, ZAR)';

View File

@@ -1,44 +1,44 @@
-- ============================================================
-- V2.1 — Hiérarchie organisations + corrections
-- Auteur: UnionFlow Team | BCEAO/OHADA compliant
-- ============================================================
-- Ajouter la FK propre pour la hiérarchie (remplace le UUID nu)
ALTER TABLE organisations
DROP CONSTRAINT IF EXISTS fk_organisation_parente;
ALTER TABLE organisations
ADD CONSTRAINT fk_organisation_parente
FOREIGN KEY (organisation_parente_id) REFERENCES organisations(id) ON DELETE SET NULL;
-- Nouveaux champs hiérarchie et modules
ALTER TABLE organisations
ADD COLUMN IF NOT EXISTS est_organisation_racine BOOLEAN NOT NULL DEFAULT TRUE,
ADD COLUMN IF NOT EXISTS chemin_hierarchique VARCHAR(2000),
ADD COLUMN IF NOT EXISTS type_organisation_code VARCHAR(50);
-- Élargir la contrainte de type_organisation pour couvrir tous les métiers
ALTER TABLE organisations DROP CONSTRAINT IF EXISTS chk_organisation_type;
ALTER TABLE organisations
ADD CONSTRAINT chk_organisation_type CHECK (type_organisation IN (
'ASSOCIATION','MUTUELLE_EPARGNE_CREDIT','MUTUELLE_SANTE',
'TONTINE','ONG','COOPERATIVE_AGRICOLE','ASSOCIATION_PROFESSIONNELLE',
'ASSOCIATION_COMMUNAUTAIRE','ORGANISATION_RELIGIEUSE',
'FEDERATION','SYNDICAT','LIONS_CLUB','ROTARY_CLUB','AUTRE'
));
-- Règle : organisation sans parent = racine
UPDATE organisations
SET est_organisation_racine = TRUE
WHERE organisation_parente_id IS NULL;
UPDATE organisations
SET est_organisation_racine = FALSE
WHERE organisation_parente_id IS NOT NULL;
-- Index pour les requêtes hiérarchiques
CREATE INDEX IF NOT EXISTS idx_org_racine ON organisations(est_organisation_racine);
CREATE INDEX IF NOT EXISTS idx_org_chemin ON organisations(chemin_hierarchique);
COMMENT ON COLUMN organisations.est_organisation_racine IS 'TRUE si c''est l''organisation mère (souscrit au forfait pour toute la hiérarchie)';
COMMENT ON COLUMN organisations.chemin_hierarchique IS 'Chemin UUID ex: /uuid-racine/uuid-inter/uuid-feuille — requêtes récursives optimisées';
-- ============================================================
-- V2.1 — Hiérarchie organisations + corrections
-- Auteur: UnionFlow Team | BCEAO/OHADA compliant
-- ============================================================
-- Ajouter la FK propre pour la hiérarchie (remplace le UUID nu)
ALTER TABLE organisations
DROP CONSTRAINT IF EXISTS fk_organisation_parente;
ALTER TABLE organisations
ADD CONSTRAINT fk_organisation_parente
FOREIGN KEY (organisation_parente_id) REFERENCES organisations(id) ON DELETE SET NULL;
-- Nouveaux champs hiérarchie et modules
ALTER TABLE organisations
ADD COLUMN IF NOT EXISTS est_organisation_racine BOOLEAN NOT NULL DEFAULT TRUE,
ADD COLUMN IF NOT EXISTS chemin_hierarchique VARCHAR(2000),
ADD COLUMN IF NOT EXISTS type_organisation_code VARCHAR(50);
-- Élargir la contrainte de type_organisation pour couvrir tous les métiers
ALTER TABLE organisations DROP CONSTRAINT IF EXISTS chk_organisation_type;
ALTER TABLE organisations
ADD CONSTRAINT chk_organisation_type CHECK (type_organisation IN (
'ASSOCIATION','MUTUELLE_EPARGNE_CREDIT','MUTUELLE_SANTE',
'TONTINE','ONG','COOPERATIVE_AGRICOLE','ASSOCIATION_PROFESSIONNELLE',
'ASSOCIATION_COMMUNAUTAIRE','ORGANISATION_RELIGIEUSE',
'FEDERATION','SYNDICAT','LIONS_CLUB','ROTARY_CLUB','AUTRE'
));
-- Règle : organisation sans parent = racine
UPDATE organisations
SET est_organisation_racine = TRUE
WHERE organisation_parente_id IS NULL;
UPDATE organisations
SET est_organisation_racine = FALSE
WHERE organisation_parente_id IS NOT NULL;
-- Index pour les requêtes hiérarchiques
CREATE INDEX IF NOT EXISTS idx_org_racine ON organisations(est_organisation_racine);
CREATE INDEX IF NOT EXISTS idx_org_chemin ON organisations(chemin_hierarchique);
COMMENT ON COLUMN organisations.est_organisation_racine IS 'TRUE si c''est l''organisation mère (souscrit au forfait pour toute la hiérarchie)';
COMMENT ON COLUMN organisations.chemin_hierarchique IS 'Chemin UUID ex: /uuid-racine/uuid-inter/uuid-feuille — requêtes récursives optimisées';

View File

@@ -1,76 +1,76 @@
-- ============================================================
-- 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, 500010000 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.';
-- ============================================================
-- 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, 500010000 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.';

View File

@@ -1,61 +1,61 @@
-- ============================================================
-- V2.3 — Hub de paiement Wave : intentions_paiement
-- Chaque paiement Wave est initié depuis UnionFlow.
-- Auteur: UnionFlow Team | BCEAO/OHADA compliant
-- ============================================================
CREATE TABLE IF NOT EXISTS intentions_paiement (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
utilisateur_id UUID NOT NULL,
organisation_id UUID, -- NULL pour abonnements UnionFlow SA
montant_total DECIMAL(14,2) NOT NULL CHECK (montant_total > 0),
code_devise VARCHAR(3) NOT NULL DEFAULT 'XOF',
type_objet VARCHAR(30) NOT NULL, -- COTISATION|ADHESION|EVENEMENT|ABONNEMENT_UNIONFLOW
statut VARCHAR(20) NOT NULL DEFAULT 'INITIEE',
-- Wave API
wave_checkout_session_id VARCHAR(255) UNIQUE,
wave_launch_url VARCHAR(1000),
wave_transaction_id VARCHAR(100),
-- Traçabilité des objets payés (JSON: [{type,id,montant},...])
objets_cibles TEXT,
date_creation TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
date_expiration TIMESTAMP, -- TTL 30 min
date_completion TIMESTAMP,
-- Métadonnées BaseEntity
actif BOOLEAN NOT NULL DEFAULT TRUE,
date_modification TIMESTAMP,
cree_par VARCHAR(255),
modifie_par VARCHAR(255),
version BIGINT NOT NULL DEFAULT 0,
CONSTRAINT fk_intention_utilisateur FOREIGN KEY (utilisateur_id) REFERENCES utilisateurs(id),
CONSTRAINT fk_intention_organisation FOREIGN KEY (organisation_id) REFERENCES organisations(id) ON DELETE SET NULL,
CONSTRAINT chk_intention_type CHECK (type_objet IN ('COTISATION','ADHESION','EVENEMENT','ABONNEMENT_UNIONFLOW')),
CONSTRAINT chk_intention_statut CHECK (statut IN ('INITIEE','EN_COURS','COMPLETEE','EXPIREE','ECHOUEE')),
CONSTRAINT chk_intention_devise CHECK (code_devise ~ '^[A-Z]{3}$')
);
CREATE INDEX idx_intention_utilisateur ON intentions_paiement(utilisateur_id);
CREATE INDEX idx_intention_statut ON intentions_paiement(statut);
CREATE INDEX idx_intention_wave_session ON intentions_paiement(wave_checkout_session_id);
CREATE INDEX idx_intention_expiration ON intentions_paiement(date_expiration);
-- Supprimer les champs paiement redondants de cotisations (centralisés dans intentions_paiement)
ALTER TABLE cotisations
DROP COLUMN IF EXISTS methode_paiement,
DROP COLUMN IF EXISTS reference_paiement;
-- Ajouter le lien cotisation → intention de paiement
ALTER TABLE cotisations
ADD COLUMN IF NOT EXISTS intention_paiement_id UUID,
ADD CONSTRAINT fk_cotisation_intention
FOREIGN KEY (intention_paiement_id) REFERENCES intentions_paiement(id) ON DELETE SET NULL;
COMMENT ON TABLE intentions_paiement IS 'Hub centralisé Wave : chaque paiement est initié depuis UnionFlow avant appel API Wave';
COMMENT ON COLUMN intentions_paiement.objets_cibles IS 'JSON: liste des objets couverts par ce paiement — ex: 3 cotisations mensuelles';
COMMENT ON COLUMN intentions_paiement.wave_checkout_session_id IS 'ID de session Wave — clé de réconciliation sur réception webhook';
-- ============================================================
-- V2.3 — Hub de paiement Wave : intentions_paiement
-- Chaque paiement Wave est initié depuis UnionFlow.
-- Auteur: UnionFlow Team | BCEAO/OHADA compliant
-- ============================================================
CREATE TABLE IF NOT EXISTS intentions_paiement (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
utilisateur_id UUID NOT NULL,
organisation_id UUID, -- NULL pour abonnements UnionFlow SA
montant_total DECIMAL(14,2) NOT NULL CHECK (montant_total > 0),
code_devise VARCHAR(3) NOT NULL DEFAULT 'XOF',
type_objet VARCHAR(30) NOT NULL, -- COTISATION|ADHESION|EVENEMENT|ABONNEMENT_UNIONFLOW
statut VARCHAR(20) NOT NULL DEFAULT 'INITIEE',
-- Wave API
wave_checkout_session_id VARCHAR(255) UNIQUE,
wave_launch_url VARCHAR(1000),
wave_transaction_id VARCHAR(100),
-- Traçabilité des objets payés (JSON: [{type,id,montant},...])
objets_cibles TEXT,
date_creation TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
date_expiration TIMESTAMP, -- TTL 30 min
date_completion TIMESTAMP,
-- Métadonnées BaseEntity
actif BOOLEAN NOT NULL DEFAULT TRUE,
date_modification TIMESTAMP,
cree_par VARCHAR(255),
modifie_par VARCHAR(255),
version BIGINT NOT NULL DEFAULT 0,
CONSTRAINT fk_intention_utilisateur FOREIGN KEY (utilisateur_id) REFERENCES utilisateurs(id),
CONSTRAINT fk_intention_organisation FOREIGN KEY (organisation_id) REFERENCES organisations(id) ON DELETE SET NULL,
CONSTRAINT chk_intention_type CHECK (type_objet IN ('COTISATION','ADHESION','EVENEMENT','ABONNEMENT_UNIONFLOW')),
CONSTRAINT chk_intention_statut CHECK (statut IN ('INITIEE','EN_COURS','COMPLETEE','EXPIREE','ECHOUEE')),
CONSTRAINT chk_intention_devise CHECK (code_devise ~ '^[A-Z]{3}$')
);
CREATE INDEX idx_intention_utilisateur ON intentions_paiement(utilisateur_id);
CREATE INDEX idx_intention_statut ON intentions_paiement(statut);
CREATE INDEX idx_intention_wave_session ON intentions_paiement(wave_checkout_session_id);
CREATE INDEX idx_intention_expiration ON intentions_paiement(date_expiration);
-- Supprimer les champs paiement redondants de cotisations (centralisés dans intentions_paiement)
ALTER TABLE cotisations
DROP COLUMN IF EXISTS methode_paiement,
DROP COLUMN IF EXISTS reference_paiement;
-- Ajouter le lien cotisation → intention de paiement
ALTER TABLE cotisations
ADD COLUMN IF NOT EXISTS intention_paiement_id UUID,
ADD CONSTRAINT fk_cotisation_intention
FOREIGN KEY (intention_paiement_id) REFERENCES intentions_paiement(id) ON DELETE SET NULL;
COMMENT ON TABLE intentions_paiement IS 'Hub centralisé Wave : chaque paiement est initié depuis UnionFlow avant appel API Wave';
COMMENT ON COLUMN intentions_paiement.objets_cibles IS 'JSON: liste des objets couverts par ce paiement — ex: 3 cotisations mensuelles';
COMMENT ON COLUMN intentions_paiement.wave_checkout_session_id IS 'ID de session Wave — clé de réconciliation sur réception webhook';

View File

@@ -1,51 +1,51 @@
-- ============================================================
-- V2.4 — Cotisations : ajout organisation_id + parametres
-- Une cotisation est toujours liée à un membre ET à une organisation
-- Auteur: UnionFlow Team | BCEAO/OHADA compliant
-- ============================================================
-- Ajouter organisation_id sur cotisations
ALTER TABLE cotisations
ADD COLUMN IF NOT EXISTS organisation_id UUID;
ALTER TABLE cotisations
ADD CONSTRAINT fk_cotisation_organisation
FOREIGN KEY (organisation_id) REFERENCES organisations(id) ON DELETE CASCADE;
CREATE INDEX IF NOT EXISTS idx_cotisation_organisation ON cotisations(organisation_id);
-- Mettre à jour les types de cotisation
ALTER TABLE cotisations DROP CONSTRAINT IF EXISTS chk_cotisation_type;
ALTER TABLE cotisations
ADD CONSTRAINT chk_cotisation_type CHECK (type_cotisation IN (
'ANNUELLE','MENSUELLE','EVENEMENTIELLE','SOLIDARITE','EXCEPTIONNELLE','AUTRE'
));
-- ============================================================
-- Paramètres de cotisation par organisation (montants fixés par l'org)
-- ============================================================
CREATE TABLE IF NOT EXISTS parametres_cotisation_organisation (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
organisation_id UUID UNIQUE NOT NULL,
montant_cotisation_mensuelle DECIMAL(12,2) DEFAULT 0 CHECK (montant_cotisation_mensuelle >= 0),
montant_cotisation_annuelle DECIMAL(12,2) DEFAULT 0 CHECK (montant_cotisation_annuelle >= 0),
devise VARCHAR(3) NOT NULL DEFAULT 'XOF',
date_debut_calcul_ajour DATE, -- configurable: depuis quand calculer les impayés
delai_retard_avant_inactif_jours INTEGER NOT NULL DEFAULT 30,
cotisation_obligatoire BOOLEAN NOT NULL DEFAULT TRUE,
-- 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_param_cotisation_org FOREIGN KEY (organisation_id) REFERENCES organisations(id) ON DELETE CASCADE
);
COMMENT ON TABLE parametres_cotisation_organisation IS 'Paramètres de cotisation configurés par le manager de chaque organisation';
COMMENT ON COLUMN parametres_cotisation_organisation.date_debut_calcul_ajour IS 'Date de référence pour le calcul membre «à jour». Configurable par le manager.';
COMMENT ON COLUMN parametres_cotisation_organisation.delai_retard_avant_inactif_jours IS 'Jours de retard après lesquels un membre passe INACTIF automatiquement';
-- ============================================================
-- V2.4 — Cotisations : ajout organisation_id + parametres
-- Une cotisation est toujours liée à un membre ET à une organisation
-- Auteur: UnionFlow Team | BCEAO/OHADA compliant
-- ============================================================
-- Ajouter organisation_id sur cotisations
ALTER TABLE cotisations
ADD COLUMN IF NOT EXISTS organisation_id UUID;
ALTER TABLE cotisations
ADD CONSTRAINT fk_cotisation_organisation
FOREIGN KEY (organisation_id) REFERENCES organisations(id) ON DELETE CASCADE;
CREATE INDEX IF NOT EXISTS idx_cotisation_organisation ON cotisations(organisation_id);
-- Mettre à jour les types de cotisation
ALTER TABLE cotisations DROP CONSTRAINT IF EXISTS chk_cotisation_type;
ALTER TABLE cotisations
ADD CONSTRAINT chk_cotisation_type CHECK (type_cotisation IN (
'ANNUELLE','MENSUELLE','EVENEMENTIELLE','SOLIDARITE','EXCEPTIONNELLE','AUTRE'
));
-- ============================================================
-- Paramètres de cotisation par organisation (montants fixés par l'org)
-- ============================================================
CREATE TABLE IF NOT EXISTS parametres_cotisation_organisation (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
organisation_id UUID UNIQUE NOT NULL,
montant_cotisation_mensuelle DECIMAL(12,2) DEFAULT 0 CHECK (montant_cotisation_mensuelle >= 0),
montant_cotisation_annuelle DECIMAL(12,2) DEFAULT 0 CHECK (montant_cotisation_annuelle >= 0),
devise VARCHAR(3) NOT NULL DEFAULT 'XOF',
date_debut_calcul_ajour DATE, -- configurable: depuis quand calculer les impayés
delai_retard_avant_inactif_jours INTEGER NOT NULL DEFAULT 30,
cotisation_obligatoire BOOLEAN NOT NULL DEFAULT TRUE,
-- 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_param_cotisation_org FOREIGN KEY (organisation_id) REFERENCES organisations(id) ON DELETE CASCADE
);
COMMENT ON TABLE parametres_cotisation_organisation IS 'Paramètres de cotisation configurés par le manager de chaque organisation';
COMMENT ON COLUMN parametres_cotisation_organisation.date_debut_calcul_ajour IS 'Date de référence pour le calcul membre «à jour». Configurable par le manager.';
COMMENT ON COLUMN parametres_cotisation_organisation.delai_retard_avant_inactif_jours IS 'Jours de retard après lesquels un membre passe INACTIF automatiquement';

View File

@@ -1,114 +1,114 @@
-- ============================================================
-- V2.5 — Workflow solidarité configurable (max 3 étapes)
-- + demandes_adhesion (remplace adhesions)
-- Auteur: UnionFlow Team | BCEAO/OHADA compliant
-- ============================================================
-- ============================================================
-- Workflow de validation configurable par organisation
-- ============================================================
CREATE TABLE IF NOT EXISTS workflow_validation_config (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
organisation_id UUID NOT NULL,
type_workflow VARCHAR(30) NOT NULL DEFAULT 'DEMANDE_AIDE',
etape_numero INTEGER NOT NULL CHECK (etape_numero BETWEEN 1 AND 3),
role_requis_id UUID, -- rôle nécessaire pour valider cette étape
libelle_etape VARCHAR(200) NOT NULL,
delai_max_heures INTEGER DEFAULT 72,
actif BOOLEAN NOT NULL DEFAULT TRUE,
-- 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 fk_wf_organisation FOREIGN KEY (organisation_id) REFERENCES organisations(id) ON DELETE CASCADE,
CONSTRAINT fk_wf_role FOREIGN KEY (role_requis_id) REFERENCES roles(id) ON DELETE SET NULL,
CONSTRAINT uk_wf_org_type_etape UNIQUE (organisation_id, type_workflow, etape_numero),
CONSTRAINT chk_wf_type CHECK (type_workflow IN ('DEMANDE_AIDE','ADHESION','AUTRE'))
);
CREATE INDEX idx_wf_organisation ON workflow_validation_config(organisation_id);
CREATE INDEX idx_wf_type ON workflow_validation_config(type_workflow);
-- ============================================================
-- Historique des validations d'une demande d'aide
-- ============================================================
CREATE TABLE IF NOT EXISTS validation_etapes_demande (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
demande_aide_id UUID NOT NULL,
etape_numero INTEGER NOT NULL CHECK (etape_numero BETWEEN 1 AND 3),
valideur_id UUID,
statut VARCHAR(20) NOT NULL DEFAULT 'EN_ATTENTE',
date_validation TIMESTAMP,
commentaire VARCHAR(1000),
delegue_par_id UUID, -- si désactivation du véto par supérieur
trace_delegation TEXT, -- motif + traçabilité BCEAO/OHADA
-- 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_ved_demande FOREIGN KEY (demande_aide_id) REFERENCES demandes_aide(id) ON DELETE CASCADE,
CONSTRAINT fk_ved_valideur FOREIGN KEY (valideur_id) REFERENCES utilisateurs(id) ON DELETE SET NULL,
CONSTRAINT fk_ved_delegue_par FOREIGN KEY (delegue_par_id) REFERENCES utilisateurs(id) ON DELETE SET NULL,
CONSTRAINT chk_ved_statut CHECK (statut IN ('EN_ATTENTE','APPROUVEE','REJETEE','DELEGUEE','EXPIREE'))
);
CREATE INDEX idx_ved_demande ON validation_etapes_demande(demande_aide_id);
CREATE INDEX idx_ved_valideur ON validation_etapes_demande(valideur_id);
CREATE INDEX idx_ved_statut ON validation_etapes_demande(statut);
-- ============================================================
-- demandes_adhesion (remplace adhesions avec modèle enrichi)
-- ============================================================
DROP TABLE IF EXISTS adhesions CASCADE;
CREATE TABLE IF NOT EXISTS demandes_adhesion (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
numero_reference VARCHAR(50) UNIQUE NOT NULL,
utilisateur_id UUID NOT NULL,
organisation_id UUID NOT NULL,
statut VARCHAR(20) NOT NULL DEFAULT 'EN_ATTENTE',
frais_adhesion DECIMAL(12,2) NOT NULL DEFAULT 0 CHECK (frais_adhesion >= 0),
montant_paye DECIMAL(12,2) NOT NULL DEFAULT 0,
code_devise VARCHAR(3) NOT NULL DEFAULT 'XOF',
intention_paiement_id UUID,
date_demande TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
date_traitement TIMESTAMP,
traite_par_id UUID,
motif_rejet VARCHAR(1000),
observations VARCHAR(1000),
-- 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_da_utilisateur FOREIGN KEY (utilisateur_id) REFERENCES utilisateurs(id) ON DELETE CASCADE,
CONSTRAINT fk_da_organisation FOREIGN KEY (organisation_id) REFERENCES organisations(id) ON DELETE CASCADE,
CONSTRAINT fk_da_intention FOREIGN KEY (intention_paiement_id) REFERENCES intentions_paiement(id) ON DELETE SET NULL,
CONSTRAINT fk_da_traite_par FOREIGN KEY (traite_par_id) REFERENCES utilisateurs(id) ON DELETE SET NULL,
CONSTRAINT chk_da_statut CHECK (statut IN ('EN_ATTENTE','APPROUVEE','REJETEE','ANNULEE'))
);
CREATE INDEX idx_da_utilisateur ON demandes_adhesion(utilisateur_id);
CREATE INDEX idx_da_organisation ON demandes_adhesion(organisation_id);
CREATE INDEX idx_da_statut ON demandes_adhesion(statut);
CREATE INDEX idx_da_date ON demandes_adhesion(date_demande);
COMMENT ON TABLE workflow_validation_config IS 'Configuration du workflow de validation par organisation (max 3 étapes)';
COMMENT ON TABLE validation_etapes_demande IS 'Historique des validations — tracé BCEAO/OHADA — délégation de véto incluse';
COMMENT ON TABLE demandes_adhesion IS 'Demande d''adhésion d''un utilisateur à une organisation avec paiement Wave intégré';
-- ============================================================
-- V2.5 — Workflow solidarité configurable (max 3 étapes)
-- + demandes_adhesion (remplace adhesions)
-- Auteur: UnionFlow Team | BCEAO/OHADA compliant
-- ============================================================
-- ============================================================
-- Workflow de validation configurable par organisation
-- ============================================================
CREATE TABLE IF NOT EXISTS workflow_validation_config (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
organisation_id UUID NOT NULL,
type_workflow VARCHAR(30) NOT NULL DEFAULT 'DEMANDE_AIDE',
etape_numero INTEGER NOT NULL CHECK (etape_numero BETWEEN 1 AND 3),
role_requis_id UUID, -- rôle nécessaire pour valider cette étape
libelle_etape VARCHAR(200) NOT NULL,
delai_max_heures INTEGER DEFAULT 72,
actif BOOLEAN NOT NULL DEFAULT TRUE,
-- 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 fk_wf_organisation FOREIGN KEY (organisation_id) REFERENCES organisations(id) ON DELETE CASCADE,
CONSTRAINT fk_wf_role FOREIGN KEY (role_requis_id) REFERENCES roles(id) ON DELETE SET NULL,
CONSTRAINT uk_wf_org_type_etape UNIQUE (organisation_id, type_workflow, etape_numero),
CONSTRAINT chk_wf_type CHECK (type_workflow IN ('DEMANDE_AIDE','ADHESION','AUTRE'))
);
CREATE INDEX idx_wf_organisation ON workflow_validation_config(organisation_id);
CREATE INDEX idx_wf_type ON workflow_validation_config(type_workflow);
-- ============================================================
-- Historique des validations d'une demande d'aide
-- ============================================================
CREATE TABLE IF NOT EXISTS validation_etapes_demande (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
demande_aide_id UUID NOT NULL,
etape_numero INTEGER NOT NULL CHECK (etape_numero BETWEEN 1 AND 3),
valideur_id UUID,
statut VARCHAR(20) NOT NULL DEFAULT 'EN_ATTENTE',
date_validation TIMESTAMP,
commentaire VARCHAR(1000),
delegue_par_id UUID, -- si désactivation du véto par supérieur
trace_delegation TEXT, -- motif + traçabilité BCEAO/OHADA
-- 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_ved_demande FOREIGN KEY (demande_aide_id) REFERENCES demandes_aide(id) ON DELETE CASCADE,
CONSTRAINT fk_ved_valideur FOREIGN KEY (valideur_id) REFERENCES utilisateurs(id) ON DELETE SET NULL,
CONSTRAINT fk_ved_delegue_par FOREIGN KEY (delegue_par_id) REFERENCES utilisateurs(id) ON DELETE SET NULL,
CONSTRAINT chk_ved_statut CHECK (statut IN ('EN_ATTENTE','APPROUVEE','REJETEE','DELEGUEE','EXPIREE'))
);
CREATE INDEX idx_ved_demande ON validation_etapes_demande(demande_aide_id);
CREATE INDEX idx_ved_valideur ON validation_etapes_demande(valideur_id);
CREATE INDEX idx_ved_statut ON validation_etapes_demande(statut);
-- ============================================================
-- demandes_adhesion (remplace adhesions avec modèle enrichi)
-- ============================================================
DROP TABLE IF EXISTS adhesions CASCADE;
CREATE TABLE IF NOT EXISTS demandes_adhesion (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
numero_reference VARCHAR(50) UNIQUE NOT NULL,
utilisateur_id UUID NOT NULL,
organisation_id UUID NOT NULL,
statut VARCHAR(20) NOT NULL DEFAULT 'EN_ATTENTE',
frais_adhesion DECIMAL(12,2) NOT NULL DEFAULT 0 CHECK (frais_adhesion >= 0),
montant_paye DECIMAL(12,2) NOT NULL DEFAULT 0,
code_devise VARCHAR(3) NOT NULL DEFAULT 'XOF',
intention_paiement_id UUID,
date_demande TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
date_traitement TIMESTAMP,
traite_par_id UUID,
motif_rejet VARCHAR(1000),
observations VARCHAR(1000),
-- 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_da_utilisateur FOREIGN KEY (utilisateur_id) REFERENCES utilisateurs(id) ON DELETE CASCADE,
CONSTRAINT fk_da_organisation FOREIGN KEY (organisation_id) REFERENCES organisations(id) ON DELETE CASCADE,
CONSTRAINT fk_da_intention FOREIGN KEY (intention_paiement_id) REFERENCES intentions_paiement(id) ON DELETE SET NULL,
CONSTRAINT fk_da_traite_par FOREIGN KEY (traite_par_id) REFERENCES utilisateurs(id) ON DELETE SET NULL,
CONSTRAINT chk_da_statut CHECK (statut IN ('EN_ATTENTE','APPROUVEE','REJETEE','ANNULEE'))
);
CREATE INDEX idx_da_utilisateur ON demandes_adhesion(utilisateur_id);
CREATE INDEX idx_da_organisation ON demandes_adhesion(organisation_id);
CREATE INDEX idx_da_statut ON demandes_adhesion(statut);
CREATE INDEX idx_da_date ON demandes_adhesion(date_demande);
COMMENT ON TABLE workflow_validation_config IS 'Configuration du workflow de validation par organisation (max 3 étapes)';
COMMENT ON TABLE validation_etapes_demande IS 'Historique des validations — tracé BCEAO/OHADA — délégation de véto incluse';
COMMENT ON TABLE demandes_adhesion IS 'Demande d''adhésion d''un utilisateur à une organisation avec paiement Wave intégré';

View File

@@ -1,72 +1,72 @@
-- ============================================================
-- V2.6 — Système de modules activables par type d'organisation
-- Auteur: UnionFlow Team | BCEAO/OHADA compliant
-- ============================================================
CREATE TABLE IF NOT EXISTS modules_disponibles (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
code VARCHAR(50) UNIQUE NOT NULL,
libelle VARCHAR(150) NOT NULL,
description TEXT,
types_org_compatibles TEXT, -- JSON array: ["MUTUELLE_SANTE","ONG",...]
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
);
-- Catalogue initial des modules métier
INSERT INTO modules_disponibles (id, code, libelle, description, types_org_compatibles, actif, ordre_affichage)
VALUES
(gen_random_uuid(), 'COTISATIONS', 'Gestion des cotisations', 'Suivi cotisations, relances, statistiques', '["ALL"]', true, 1),
(gen_random_uuid(), 'EVENEMENTS', 'Gestion des événements', 'Création, inscriptions, présences, paiements', '["ALL"]', true, 2),
(gen_random_uuid(), 'SOLIDARITE', 'Fonds de solidarité', 'Demandes d''aide avec workflow de validation', '["ALL"]', true, 3),
(gen_random_uuid(), 'COMPTABILITE', 'Comptabilité simplifiée', 'Journal, écritures, comptes — conforme OHADA', '["ALL"]', true, 4),
(gen_random_uuid(), 'DOCUMENTS', 'Gestion documentaire', 'Upload, versioning, intégrité hash — 1Go max', '["ALL"]', true, 5),
(gen_random_uuid(), 'NOTIFICATIONS', 'Notifications multi-canal', 'Email, WhatsApp, push mobile', '["ALL"]', true, 6),
(gen_random_uuid(), 'CREDIT_EPARGNE', 'Épargne & crédit MEC', 'Prêts, échéanciers, impayés, multi-caisses', '["MUTUELLE_EPARGNE_CREDIT"]', true, 10),
(gen_random_uuid(), 'AYANTS_DROIT', 'Gestion des ayants droit', 'Couverture santé, plafonds, conventions centres de santé', '["MUTUELLE_SANTE"]', true, 11),
(gen_random_uuid(), 'TONTINE', 'Tontine / épargne rotative', 'Cycles rotatifs, tirage, enchères, pénalités', '["TONTINE"]', true, 12),
(gen_random_uuid(), 'ONG_PROJETS', 'Projets humanitaires', 'Logframe, budget bailleurs, indicateurs d''impact, rapports', '["ONG"]', true, 13),
(gen_random_uuid(), 'COOP_AGRICOLE', 'Coopérative agricole', 'Parcelles, rendements, intrants, vente groupée, ristournes', '["COOPERATIVE_AGRICOLE"]', true, 14),
(gen_random_uuid(), 'VOTE_INTERNE', 'Vote interne électronique', 'Assemblées générales, votes, quorums', '["FEDERATION","ASSOCIATION","SYNDICAT"]', true, 15),
(gen_random_uuid(), 'COLLECTE_FONDS', 'Collecte de fonds', 'Campagnes de don, suivi, rapports', '["ONG","ORGANISATION_RELIGIEUSE","ASSOCIATION"]', true, 16),
(gen_random_uuid(), 'REGISTRE_PROFESSIONNEL','Registre officiel membres', 'Agrément, diplômes, sanctions disciplinaires, annuaire certifié', '["ASSOCIATION_PROFESSIONNELLE"]', true, 17),
(gen_random_uuid(), 'CULTES_RELIGIEUX', 'Gestion cultes & dîmes', 'Dîmes, promesses de don, planification cultes, cellules, offrandes anon.','["ORGANISATION_RELIGIEUSE"]', true, 18),
(gen_random_uuid(), 'GOUVERNANCE_MULTI', 'Gouvernance multi-niveaux', 'Cotisation par section, reporting consolidé, redistribution subventions', '["FEDERATION"]', true, 19)
ON CONFLICT (code) DO NOTHING;
-- ============================================================
-- Modules activés pour chaque organisation
-- ============================================================
CREATE TABLE IF NOT EXISTS modules_organisation_actifs (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
organisation_id UUID NOT NULL,
module_code VARCHAR(50) NOT NULL,
actif BOOLEAN NOT NULL DEFAULT TRUE,
parametres TEXT, -- JSON de configuration spécifique
date_activation TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
-- 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 fk_moa_organisation FOREIGN KEY (organisation_id) REFERENCES organisations(id) ON DELETE CASCADE,
CONSTRAINT uk_moa_org_module UNIQUE (organisation_id, module_code)
);
CREATE INDEX idx_moa_organisation ON modules_organisation_actifs(organisation_id);
CREATE INDEX idx_moa_module ON modules_organisation_actifs(module_code);
COMMENT ON TABLE modules_disponibles IS 'Catalogue des modules métier UnionFlow activables selon le type d''organisation';
COMMENT ON TABLE modules_organisation_actifs IS 'Modules activés pour une organisation donnée avec paramètres spécifiques';
-- ============================================================
-- V2.6 — Système de modules activables par type d'organisation
-- Auteur: UnionFlow Team | BCEAO/OHADA compliant
-- ============================================================
CREATE TABLE IF NOT EXISTS modules_disponibles (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
code VARCHAR(50) UNIQUE NOT NULL,
libelle VARCHAR(150) NOT NULL,
description TEXT,
types_org_compatibles TEXT, -- JSON array: ["MUTUELLE_SANTE","ONG",...]
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
);
-- Catalogue initial des modules métier
INSERT INTO modules_disponibles (id, code, libelle, description, types_org_compatibles, actif, ordre_affichage)
VALUES
(gen_random_uuid(), 'COTISATIONS', 'Gestion des cotisations', 'Suivi cotisations, relances, statistiques', '["ALL"]', true, 1),
(gen_random_uuid(), 'EVENEMENTS', 'Gestion des événements', 'Création, inscriptions, présences, paiements', '["ALL"]', true, 2),
(gen_random_uuid(), 'SOLIDARITE', 'Fonds de solidarité', 'Demandes d''aide avec workflow de validation', '["ALL"]', true, 3),
(gen_random_uuid(), 'COMPTABILITE', 'Comptabilité simplifiée', 'Journal, écritures, comptes — conforme OHADA', '["ALL"]', true, 4),
(gen_random_uuid(), 'DOCUMENTS', 'Gestion documentaire', 'Upload, versioning, intégrité hash — 1Go max', '["ALL"]', true, 5),
(gen_random_uuid(), 'NOTIFICATIONS', 'Notifications multi-canal', 'Email, WhatsApp, push mobile', '["ALL"]', true, 6),
(gen_random_uuid(), 'CREDIT_EPARGNE', 'Épargne & crédit MEC', 'Prêts, échéanciers, impayés, multi-caisses', '["MUTUELLE_EPARGNE_CREDIT"]', true, 10),
(gen_random_uuid(), 'AYANTS_DROIT', 'Gestion des ayants droit', 'Couverture santé, plafonds, conventions centres de santé', '["MUTUELLE_SANTE"]', true, 11),
(gen_random_uuid(), 'TONTINE', 'Tontine / épargne rotative', 'Cycles rotatifs, tirage, enchères, pénalités', '["TONTINE"]', true, 12),
(gen_random_uuid(), 'ONG_PROJETS', 'Projets humanitaires', 'Logframe, budget bailleurs, indicateurs d''impact, rapports', '["ONG"]', true, 13),
(gen_random_uuid(), 'COOP_AGRICOLE', 'Coopérative agricole', 'Parcelles, rendements, intrants, vente groupée, ristournes', '["COOPERATIVE_AGRICOLE"]', true, 14),
(gen_random_uuid(), 'VOTE_INTERNE', 'Vote interne électronique', 'Assemblées générales, votes, quorums', '["FEDERATION","ASSOCIATION","SYNDICAT"]', true, 15),
(gen_random_uuid(), 'COLLECTE_FONDS', 'Collecte de fonds', 'Campagnes de don, suivi, rapports', '["ONG","ORGANISATION_RELIGIEUSE","ASSOCIATION"]', true, 16),
(gen_random_uuid(), 'REGISTRE_PROFESSIONNEL','Registre officiel membres', 'Agrément, diplômes, sanctions disciplinaires, annuaire certifié', '["ASSOCIATION_PROFESSIONNELLE"]', true, 17),
(gen_random_uuid(), 'CULTES_RELIGIEUX', 'Gestion cultes & dîmes', 'Dîmes, promesses de don, planification cultes, cellules, offrandes anon.','["ORGANISATION_RELIGIEUSE"]', true, 18),
(gen_random_uuid(), 'GOUVERNANCE_MULTI', 'Gouvernance multi-niveaux', 'Cotisation par section, reporting consolidé, redistribution subventions', '["FEDERATION"]', true, 19)
ON CONFLICT (code) DO NOTHING;
-- ============================================================
-- Modules activés pour chaque organisation
-- ============================================================
CREATE TABLE IF NOT EXISTS modules_organisation_actifs (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
organisation_id UUID NOT NULL,
module_code VARCHAR(50) NOT NULL,
actif BOOLEAN NOT NULL DEFAULT TRUE,
parametres TEXT, -- JSON de configuration spécifique
date_activation TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
-- 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 fk_moa_organisation FOREIGN KEY (organisation_id) REFERENCES organisations(id) ON DELETE CASCADE,
CONSTRAINT uk_moa_org_module UNIQUE (organisation_id, module_code)
);
CREATE INDEX idx_moa_organisation ON modules_organisation_actifs(organisation_id);
CREATE INDEX idx_moa_module ON modules_organisation_actifs(module_code);
COMMENT ON TABLE modules_disponibles IS 'Catalogue des modules métier UnionFlow activables selon le type d''organisation';
COMMENT ON TABLE modules_organisation_actifs IS 'Modules activés pour une organisation donnée avec paramètres spécifiques';

View File

@@ -1,34 +1,34 @@
-- ============================================================
-- V2.7 — Ayants droit (mutuelles de santé)
-- Auteur: UnionFlow Team | BCEAO/OHADA compliant
-- ============================================================
CREATE TABLE IF NOT EXISTS ayants_droit (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
membre_organisation_id UUID NOT NULL, -- membre dans le contexte org mutuelle
prenom VARCHAR(100) NOT NULL,
nom VARCHAR(100) NOT NULL,
date_naissance DATE,
lien_parente VARCHAR(20) NOT NULL, -- CONJOINT|ENFANT|PARENT|AUTRE
numero_beneficiaire VARCHAR(50), -- numéro pour les conventions santé
date_debut_couverture DATE,
date_fin_couverture 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_ad_membre_org FOREIGN KEY (membre_organisation_id) REFERENCES membres_organisations(id) ON DELETE CASCADE,
CONSTRAINT chk_ad_lien_parente CHECK (lien_parente IN ('CONJOINT','ENFANT','PARENT','AUTRE'))
);
CREATE INDEX idx_ad_membre_org ON ayants_droit(membre_organisation_id);
CREATE INDEX idx_ad_couverture ON ayants_droit(date_debut_couverture, date_fin_couverture);
COMMENT ON TABLE ayants_droit IS 'Bénéficiaires d''un membre dans une mutuelle de santé (conjoint, enfants, parents)';
COMMENT ON COLUMN ayants_droit.numero_beneficiaire IS 'Numéro unique attribué pour les conventions avec les centres de santé partenaires';
-- ============================================================
-- V2.7 — Ayants droit (mutuelles de santé)
-- Auteur: UnionFlow Team | BCEAO/OHADA compliant
-- ============================================================
CREATE TABLE IF NOT EXISTS ayants_droit (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
membre_organisation_id UUID NOT NULL, -- membre dans le contexte org mutuelle
prenom VARCHAR(100) NOT NULL,
nom VARCHAR(100) NOT NULL,
date_naissance DATE,
lien_parente VARCHAR(20) NOT NULL, -- CONJOINT|ENFANT|PARENT|AUTRE
numero_beneficiaire VARCHAR(50), -- numéro pour les conventions santé
date_debut_couverture DATE,
date_fin_couverture 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_ad_membre_org FOREIGN KEY (membre_organisation_id) REFERENCES membres_organisations(id) ON DELETE CASCADE,
CONSTRAINT chk_ad_lien_parente CHECK (lien_parente IN ('CONJOINT','ENFANT','PARENT','AUTRE'))
);
CREATE INDEX idx_ad_membre_org ON ayants_droit(membre_organisation_id);
CREATE INDEX idx_ad_couverture ON ayants_droit(date_debut_couverture, date_fin_couverture);
COMMENT ON TABLE ayants_droit IS 'Bénéficiaires d''un membre dans une mutuelle de santé (conjoint, enfants, parents)';
COMMENT ON COLUMN ayants_droit.numero_beneficiaire IS 'Numéro unique attribué pour les conventions avec les centres de santé partenaires';

View File

@@ -1,31 +1,31 @@
-- ============================================================
-- V2.8 — Rôles par organisation : membres_roles enrichi
-- Un membre peut avoir des rôles différents selon l'organisation
-- Auteur: UnionFlow Team | BCEAO/OHADA compliant
-- ============================================================
-- membres_roles doit référencer membres_organisations (pas uniquement membres)
-- On ajoute organisation_id et membre_organisation_id pour permettre les rôles multi-org
ALTER TABLE membres_roles
ADD COLUMN IF NOT EXISTS membre_organisation_id UUID,
ADD COLUMN IF NOT EXISTS organisation_id UUID;
-- Mettre à jour la FK et la contrainte UNIQUE
ALTER TABLE membres_roles
DROP CONSTRAINT IF EXISTS uk_membre_role;
ALTER TABLE membres_roles
ADD CONSTRAINT fk_mr_membre_org FOREIGN KEY (membre_organisation_id) REFERENCES membres_organisations(id) ON DELETE CASCADE,
ADD CONSTRAINT fk_mr_organisation FOREIGN KEY (organisation_id) REFERENCES organisations(id) ON DELETE CASCADE;
-- Nouvelle contrainte: un utilisateur ne peut avoir le même rôle qu'une fois par organisation
ALTER TABLE membres_roles
ADD CONSTRAINT uk_mr_membre_org_role
UNIQUE (membre_organisation_id, role_id);
CREATE INDEX IF NOT EXISTS idx_mr_membre_org ON membres_roles(membre_organisation_id);
CREATE INDEX IF NOT EXISTS idx_mr_organisation ON membres_roles(organisation_id);
COMMENT ON COLUMN membres_roles.membre_organisation_id IS 'Lien vers le membership de l''utilisateur dans l''organisation — détermine le contexte du rôle';
COMMENT ON COLUMN membres_roles.organisation_id IS 'Organisation dans laquelle ce rôle est actif — dénormalisé pour les requêtes de performance';
-- ============================================================
-- V2.8 — Rôles par organisation : membres_roles enrichi
-- Un membre peut avoir des rôles différents selon l'organisation
-- Auteur: UnionFlow Team | BCEAO/OHADA compliant
-- ============================================================
-- membres_roles doit référencer membres_organisations (pas uniquement membres)
-- On ajoute organisation_id et membre_organisation_id pour permettre les rôles multi-org
ALTER TABLE membres_roles
ADD COLUMN IF NOT EXISTS membre_organisation_id UUID,
ADD COLUMN IF NOT EXISTS organisation_id UUID;
-- Mettre à jour la FK et la contrainte UNIQUE
ALTER TABLE membres_roles
DROP CONSTRAINT IF EXISTS uk_membre_role;
ALTER TABLE membres_roles
ADD CONSTRAINT fk_mr_membre_org FOREIGN KEY (membre_organisation_id) REFERENCES membres_organisations(id) ON DELETE CASCADE,
ADD CONSTRAINT fk_mr_organisation FOREIGN KEY (organisation_id) REFERENCES organisations(id) ON DELETE CASCADE;
-- Nouvelle contrainte: un utilisateur ne peut avoir le même rôle qu'une fois par organisation
ALTER TABLE membres_roles
ADD CONSTRAINT uk_mr_membre_org_role
UNIQUE (membre_organisation_id, role_id);
CREATE INDEX IF NOT EXISTS idx_mr_membre_org ON membres_roles(membre_organisation_id);
CREATE INDEX IF NOT EXISTS idx_mr_organisation ON membres_roles(organisation_id);
COMMENT ON COLUMN membres_roles.membre_organisation_id IS 'Lien vers le membership de l''utilisateur dans l''organisation — détermine le contexte du rôle';
COMMENT ON COLUMN membres_roles.organisation_id IS 'Organisation dans laquelle ce rôle est actif — dénormalisé pour les requêtes de performance';

View File

@@ -1,23 +1,23 @@
-- ============================================================
-- V2.9 — Améliorations audit_logs : portée + organisation
-- Double niveau : ORGANISATION (manager) + PLATEFORME (super admin)
-- Conservation 10 ans — BCEAO/OHADA/Fiscalité ivoirienne
-- Auteur: UnionFlow Team
-- ============================================================
ALTER TABLE audit_logs
ADD COLUMN IF NOT EXISTS organisation_id UUID,
ADD COLUMN IF NOT EXISTS portee VARCHAR(15) NOT NULL DEFAULT 'PLATEFORME';
ALTER TABLE audit_logs
ADD CONSTRAINT fk_audit_organisation FOREIGN KEY (organisation_id) REFERENCES organisations(id) ON DELETE SET NULL,
ADD CONSTRAINT chk_audit_portee CHECK (portee IN ('ORGANISATION','PLATEFORME'));
CREATE INDEX IF NOT EXISTS idx_audit_organisation ON audit_logs(organisation_id);
CREATE INDEX IF NOT EXISTS idx_audit_portee ON audit_logs(portee);
-- Index composite pour les consultations fréquentes
CREATE INDEX IF NOT EXISTS idx_audit_org_portee_date ON audit_logs(organisation_id, portee, date_heure DESC);
COMMENT ON COLUMN audit_logs.organisation_id IS 'Organisation concernée — NULL pour événements plateforme';
COMMENT ON COLUMN audit_logs.portee IS 'ORGANISATION: visible par le manager | PLATEFORME: visible uniquement par Super Admin UnionFlow';
-- ============================================================
-- V2.9 — Améliorations audit_logs : portée + organisation
-- Double niveau : ORGANISATION (manager) + PLATEFORME (super admin)
-- Conservation 10 ans — BCEAO/OHADA/Fiscalité ivoirienne
-- Auteur: UnionFlow Team
-- ============================================================
ALTER TABLE audit_logs
ADD COLUMN IF NOT EXISTS organisation_id UUID,
ADD COLUMN IF NOT EXISTS portee VARCHAR(15) NOT NULL DEFAULT 'PLATEFORME';
ALTER TABLE audit_logs
ADD CONSTRAINT fk_audit_organisation FOREIGN KEY (organisation_id) REFERENCES organisations(id) ON DELETE SET NULL,
ADD CONSTRAINT chk_audit_portee CHECK (portee IN ('ORGANISATION','PLATEFORME'));
CREATE INDEX IF NOT EXISTS idx_audit_organisation ON audit_logs(organisation_id);
CREATE INDEX IF NOT EXISTS idx_audit_portee ON audit_logs(portee);
-- Index composite pour les consultations fréquentes
CREATE INDEX IF NOT EXISTS idx_audit_org_portee_date ON audit_logs(organisation_id, portee, date_heure DESC);
COMMENT ON COLUMN audit_logs.organisation_id IS 'Organisation concernée — NULL pour événements plateforme';
COMMENT ON COLUMN audit_logs.portee IS 'ORGANISATION: visible par le manager | PLATEFORME: visible uniquement par Super Admin UnionFlow';

View File

@@ -1,266 +1,266 @@
-- =====================================================
-- V3.0 — Optimisation de la structure de données
-- =====================================================
-- Cat.1 : Table types_reference
-- Cat.2 : Table paiements_objets + suppression
-- colonnes adresse de organisations
-- Cat.4 : Refonte pieces_jointes (polymorphique)
-- Cat.5 : Colonnes Membre manquantes
-- =====================================================
-- ─────────────────────────────────────────────────────
-- Cat.1 — types_reference
-- ─────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS types_reference (
id UUID PRIMARY KEY,
domaine VARCHAR(100) NOT NULL,
code VARCHAR(100) NOT NULL,
libelle VARCHAR(255) NOT NULL,
description VARCHAR(1000),
ordre INT NOT NULL DEFAULT 0,
valeur_systeme BOOLEAN NOT NULL DEFAULT FALSE,
actif BOOLEAN NOT NULL DEFAULT TRUE,
date_creation TIMESTAMP NOT NULL DEFAULT NOW(),
date_modification TIMESTAMP,
cree_par VARCHAR(255),
modifie_par VARCHAR(255),
version BIGINT NOT NULL DEFAULT 0,
CONSTRAINT uk_type_ref_domaine_code
UNIQUE (domaine, code)
);
CREATE INDEX IF NOT EXISTS idx_tr_domaine
ON types_reference (domaine);
CREATE INDEX IF NOT EXISTS idx_tr_actif
ON types_reference (actif);
-- ─────────────────────────────────────────────────────────────────────────────
-- Bloc d'idempotence : corrige l'écart entre la table créée par Hibernate
-- (sans DEFAULT SQL) et le schéma attendu par cette migration.
-- Hibernate gère les defaults en Java ; ici on les pose au niveau PostgreSQL.
-- ─────────────────────────────────────────────────────────────────────────────
ALTER TABLE types_reference
ADD COLUMN IF NOT EXISTS valeur_systeme BOOLEAN NOT NULL DEFAULT FALSE;
ALTER TABLE types_reference
ADD COLUMN IF NOT EXISTS ordre INT NOT NULL DEFAULT 0,
ADD COLUMN IF NOT EXISTS actif BOOLEAN NOT NULL DEFAULT TRUE,
ADD COLUMN IF NOT EXISTS version BIGINT NOT NULL DEFAULT 0,
ADD COLUMN IF NOT EXISTS date_creation TIMESTAMP NOT NULL DEFAULT NOW(),
ADD COLUMN IF NOT EXISTS est_defaut BOOLEAN NOT NULL DEFAULT FALSE,
ADD COLUMN IF NOT EXISTS est_systeme BOOLEAN NOT NULL DEFAULT FALSE,
ADD COLUMN IF NOT EXISTS ordre_affichage INT NOT NULL DEFAULT 0;
-- Garantit que la contrainte UNIQUE existe (nécessaire pour ON CONFLICT ci-dessous)
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM pg_constraint
WHERE conname = 'uk_type_ref_domaine_code'
AND conrelid = 'types_reference'::regclass
) THEN
ALTER TABLE types_reference
ADD CONSTRAINT uk_type_ref_domaine_code UNIQUE (domaine, code);
END IF;
END $$;
-- Données initiales : domaines référencés par les entités
-- Toutes les colonnes NOT NULL sont fournies (table peut exister sans DEFAULT si créée par Hibernate)
INSERT INTO types_reference (id, domaine, code, libelle, valeur_systeme, cree_par, actif, ordre, version, date_creation, est_defaut, est_systeme, ordre_affichage)
VALUES
-- OBJET_PAIEMENT (Cat.2 — PaiementObjet)
(gen_random_uuid(), 'OBJET_PAIEMENT', 'COTISATION', 'Cotisation', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0),
(gen_random_uuid(), 'OBJET_PAIEMENT', 'ADHESION', 'Adhésion', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0),
(gen_random_uuid(), 'OBJET_PAIEMENT', 'EVENEMENT', 'Événement', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0),
(gen_random_uuid(), 'OBJET_PAIEMENT', 'AIDE', 'Aide', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0),
-- ENTITE_RATTACHEE (Cat.4 — PieceJointe)
(gen_random_uuid(), 'ENTITE_RATTACHEE', 'MEMBRE', 'Membre', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0),
(gen_random_uuid(), 'ENTITE_RATTACHEE', 'ORGANISATION', 'Organisation', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0),
(gen_random_uuid(), 'ENTITE_RATTACHEE', 'COTISATION', 'Cotisation', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0),
(gen_random_uuid(), 'ENTITE_RATTACHEE', 'ADHESION', 'Adhésion', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0),
(gen_random_uuid(), 'ENTITE_RATTACHEE', 'AIDE', 'Aide', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0),
(gen_random_uuid(), 'ENTITE_RATTACHEE', 'TRANSACTION_WAVE', 'Transaction Wave', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0),
-- STATUT_MATRIMONIAL (Cat.5 — Membre)
(gen_random_uuid(), 'STATUT_MATRIMONIAL', 'CELIBATAIRE', 'Célibataire', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0),
(gen_random_uuid(), 'STATUT_MATRIMONIAL', 'MARIE', 'Marié(e)', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0),
(gen_random_uuid(), 'STATUT_MATRIMONIAL', 'DIVORCE', 'Divorcé(e)', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0),
(gen_random_uuid(), 'STATUT_MATRIMONIAL', 'VEUF', 'Veuf/Veuve', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0),
-- TYPE_IDENTITE (Cat.5 — Membre)
(gen_random_uuid(), 'TYPE_IDENTITE', 'CNI', 'Carte Nationale d''Identité', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0),
(gen_random_uuid(), 'TYPE_IDENTITE', 'PASSEPORT', 'Passeport', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0),
(gen_random_uuid(), 'TYPE_IDENTITE', 'PERMIS', 'Permis de conduire', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0),
(gen_random_uuid(), 'TYPE_IDENTITE', 'CARTE_SEJOUR','Carte de séjour', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0)
ON CONFLICT (domaine, code) DO NOTHING;
-- ─────────────────────────────────────────────────────
-- Cat.2 — paiements_objets (remplace 4 tables)
-- ─────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS paiements_objets (
id UUID PRIMARY KEY,
paiement_id UUID NOT NULL
REFERENCES paiements(id),
type_objet_cible VARCHAR(50) NOT NULL,
objet_cible_id UUID NOT NULL,
montant_applique NUMERIC(14,2) NOT NULL,
date_application TIMESTAMP,
commentaire VARCHAR(500),
actif BOOLEAN NOT NULL DEFAULT TRUE,
date_creation TIMESTAMP NOT NULL DEFAULT NOW(),
date_modification TIMESTAMP,
cree_par VARCHAR(255),
modifie_par VARCHAR(255),
version BIGINT NOT NULL DEFAULT 0,
CONSTRAINT uk_paiement_objet
UNIQUE (paiement_id, type_objet_cible, objet_cible_id)
);
CREATE INDEX IF NOT EXISTS idx_po_paiement
ON paiements_objets (paiement_id);
CREATE INDEX IF NOT EXISTS idx_po_objet
ON paiements_objets (type_objet_cible, objet_cible_id);
CREATE INDEX IF NOT EXISTS idx_po_type
ON paiements_objets (type_objet_cible);
-- ─────────────────────────────────────────────────────
-- Cat.2 — Suppression colonnes adresse de organisations
-- ─────────────────────────────────────────────────────
ALTER TABLE organisations
DROP COLUMN IF EXISTS adresse,
DROP COLUMN IF EXISTS ville,
DROP COLUMN IF EXISTS code_postal,
DROP COLUMN IF EXISTS region,
DROP COLUMN IF EXISTS pays;
-- ─────────────────────────────────────────────────────
-- Cat.4 — pieces_jointes → polymorphique
-- ─────────────────────────────────────────────────────
-- Ajout colonnes polymorphiques
ALTER TABLE pieces_jointes
ADD COLUMN IF NOT EXISTS type_entite_rattachee VARCHAR(50),
ADD COLUMN IF NOT EXISTS entite_rattachee_id UUID;
-- Migration des données existantes (colonnes FK explicites ou entite_type/entite_id selon le schéma)
DO $$
BEGIN
IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = 'public' AND table_name = 'pieces_jointes' AND column_name = 'membre_id') THEN
UPDATE pieces_jointes SET type_entite_rattachee = 'MEMBRE', entite_rattachee_id = membre_id WHERE membre_id IS NOT NULL AND (type_entite_rattachee IS NULL OR type_entite_rattachee = '');
END IF;
IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = 'public' AND table_name = 'pieces_jointes' AND column_name = 'organisation_id') THEN
UPDATE pieces_jointes SET type_entite_rattachee = 'ORGANISATION', entite_rattachee_id = organisation_id WHERE organisation_id IS NOT NULL AND (type_entite_rattachee IS NULL OR type_entite_rattachee = '');
END IF;
IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = 'public' AND table_name = 'pieces_jointes' AND column_name = 'cotisation_id') THEN
UPDATE pieces_jointes SET type_entite_rattachee = 'COTISATION', entite_rattachee_id = cotisation_id WHERE cotisation_id IS NOT NULL AND (type_entite_rattachee IS NULL OR type_entite_rattachee = '');
END IF;
IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = 'public' AND table_name = 'pieces_jointes' AND column_name = 'adhesion_id') THEN
UPDATE pieces_jointes SET type_entite_rattachee = 'ADHESION', entite_rattachee_id = adhesion_id WHERE adhesion_id IS NOT NULL AND (type_entite_rattachee IS NULL OR type_entite_rattachee = '');
END IF;
IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = 'public' AND table_name = 'pieces_jointes' AND column_name = 'demande_aide_id') THEN
UPDATE pieces_jointes SET type_entite_rattachee = 'AIDE', entite_rattachee_id = demande_aide_id WHERE demande_aide_id IS NOT NULL AND (type_entite_rattachee IS NULL OR type_entite_rattachee = '');
END IF;
IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = 'public' AND table_name = 'pieces_jointes' AND column_name = 'transaction_wave_id') THEN
UPDATE pieces_jointes SET type_entite_rattachee = 'TRANSACTION_WAVE', entite_rattachee_id = transaction_wave_id WHERE transaction_wave_id IS NOT NULL AND (type_entite_rattachee IS NULL OR type_entite_rattachee = '');
END IF;
-- Schéma V1.7 : entite_type / entite_id
IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = 'public' AND table_name = 'pieces_jointes' AND column_name = 'entite_type') THEN
UPDATE pieces_jointes SET type_entite_rattachee = COALESCE(NULLIF(TRIM(entite_type), ''), 'MEMBRE'), entite_rattachee_id = entite_id WHERE entite_id IS NOT NULL AND (type_entite_rattachee IS NULL OR type_entite_rattachee = '');
END IF;
-- Valeurs par défaut pour lignes restantes (évite échec NOT NULL)
UPDATE pieces_jointes SET type_entite_rattachee = COALESCE(NULLIF(TRIM(type_entite_rattachee), ''), 'MEMBRE'), entite_rattachee_id = COALESCE(entite_rattachee_id, (SELECT id FROM utilisateurs LIMIT 1)) WHERE type_entite_rattachee IS NULL OR type_entite_rattachee = '' OR entite_rattachee_id IS NULL;
END $$;
-- Contrainte NOT NULL après migration (seulement si plus aucune ligne NULL)
DO $$
BEGIN
IF NOT EXISTS (SELECT 1 FROM pieces_jointes WHERE type_entite_rattachee IS NULL OR type_entite_rattachee = '' OR entite_rattachee_id IS NULL) THEN
EXECUTE 'ALTER TABLE pieces_jointes ALTER COLUMN type_entite_rattachee SET NOT NULL';
EXECUTE 'ALTER TABLE pieces_jointes ALTER COLUMN entite_rattachee_id SET NOT NULL';
END IF;
END $$;
-- Suppression anciennes FK ou colonnes polymorphiques V1.7 (entite_type, entite_id)
ALTER TABLE pieces_jointes
DROP COLUMN IF EXISTS membre_id,
DROP COLUMN IF EXISTS organisation_id,
DROP COLUMN IF EXISTS cotisation_id,
DROP COLUMN IF EXISTS adhesion_id,
DROP COLUMN IF EXISTS demande_aide_id,
DROP COLUMN IF EXISTS transaction_wave_id,
DROP COLUMN IF EXISTS entite_type,
DROP COLUMN IF EXISTS entite_id;
-- Suppression anciens index
DROP INDEX IF EXISTS idx_piece_jointe_membre;
DROP INDEX IF EXISTS idx_piece_jointe_organisation;
DROP INDEX IF EXISTS idx_piece_jointe_cotisation;
DROP INDEX IF EXISTS idx_piece_jointe_adhesion;
DROP INDEX IF EXISTS idx_piece_jointe_demande_aide;
DROP INDEX IF EXISTS idx_piece_jointe_transaction_wave;
-- Nouveaux index polymorphiques
CREATE INDEX IF NOT EXISTS idx_pj_entite
ON pieces_jointes (type_entite_rattachee, entite_rattachee_id);
CREATE INDEX IF NOT EXISTS idx_pj_type_entite
ON pieces_jointes (type_entite_rattachee);
-- ─────────────────────────────────────────────────────
-- Cat.5 — Colonnes Membre manquantes (table utilisateurs depuis V2.0)
-- ─────────────────────────────────────────────────────
ALTER TABLE utilisateurs
ADD COLUMN IF NOT EXISTS statut_matrimonial VARCHAR(50),
ADD COLUMN IF NOT EXISTS nationalite VARCHAR(100),
ADD COLUMN IF NOT EXISTS type_identite VARCHAR(50),
ADD COLUMN IF NOT EXISTS numero_identite VARCHAR(100);
-- ─────────────────────────────────────────────────────
-- Cat.8 — Valeurs par défaut dans configurations
-- ─────────────────────────────────────────────────────
INSERT INTO configurations (id, cle, valeur, type, categorie, description, modifiable, visible, actif, date_creation, cree_par, version)
VALUES
(gen_random_uuid(), 'defaut.devise', 'XOF', 'STRING', 'SYSTEME', 'Devise par défaut', TRUE, TRUE, TRUE, NOW(), 'system', 0),
(gen_random_uuid(), 'defaut.statut.organisation', 'ACTIVE', 'STRING', 'SYSTEME', 'Statut initial organisation', TRUE, TRUE, TRUE, NOW(), 'system', 0),
(gen_random_uuid(), 'defaut.type.organisation', 'ASSOCIATION', 'STRING', 'SYSTEME', 'Type initial organisation', TRUE, TRUE, TRUE, NOW(), 'system', 0),
(gen_random_uuid(), 'defaut.utilisateur.systeme', 'system', 'STRING', 'SYSTEME', 'Identifiant utilisateur système', FALSE, FALSE, TRUE, NOW(), 'system', 0),
(gen_random_uuid(), 'defaut.montant.cotisation', '0', 'NUMBER', 'SYSTEME', 'Montant cotisation par défaut', TRUE, TRUE, TRUE, NOW(), 'system', 0)
ON CONFLICT DO NOTHING;
-- ─────────────────────────────────────────────────────
-- Cat.7 — Index composites pour requêtes fréquentes
-- ─────────────────────────────────────────────────────
-- Aligner paiements avec l'entité (statut → statut_paiement si la colonne existe)
DO $$
BEGIN
IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = 'public' AND table_name = 'paiements' AND column_name = 'statut')
AND NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = 'public' AND table_name = 'paiements' AND column_name = 'statut_paiement') THEN
ALTER TABLE paiements RENAME COLUMN statut TO statut_paiement;
END IF;
END $$;
CREATE INDEX IF NOT EXISTS idx_cotisation_org_statut_annee
ON cotisations (organisation_id, statut, annee);
CREATE INDEX IF NOT EXISTS idx_cotisation_membre_statut
ON cotisations (membre_id, statut);
CREATE INDEX IF NOT EXISTS idx_paiement_membre_statut_date
ON paiements (membre_id, statut_paiement,
date_paiement);
CREATE INDEX IF NOT EXISTS idx_notification_membre_statut
ON notifications (membre_id, statut, date_envoi);
CREATE INDEX IF NOT EXISTS idx_adhesion_org_statut
ON demandes_adhesion (organisation_id, statut);
CREATE INDEX IF NOT EXISTS idx_aide_org_statut_urgence
ON demandes_aide (organisation_id, statut, urgence);
CREATE INDEX IF NOT EXISTS idx_membreorg_org_statut
ON membres_organisations
(organisation_id, statut_membre);
CREATE INDEX IF NOT EXISTS idx_evenement_org_date_statut
ON evenements
(organisation_id, date_debut, statut);
-- ─────────────────────────────────────────────────────
-- Cat.7 — Contraintes CHECK métier
-- ─────────────────────────────────────────────────────
ALTER TABLE cotisations
ADD CONSTRAINT chk_montant_paye_le_du
CHECK (montant_paye <= montant_du);
ALTER TABLE souscriptions_organisation
ADD CONSTRAINT chk_quota_utilise_le_max
CHECK (quota_utilise <= quota_max);
-- =====================================================
-- V3.0 — Optimisation de la structure de données
-- =====================================================
-- Cat.1 : Table types_reference
-- Cat.2 : Table paiements_objets + suppression
-- colonnes adresse de organisations
-- Cat.4 : Refonte pieces_jointes (polymorphique)
-- Cat.5 : Colonnes Membre manquantes
-- =====================================================
-- ─────────────────────────────────────────────────────
-- Cat.1 — types_reference
-- ─────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS types_reference (
id UUID PRIMARY KEY,
domaine VARCHAR(100) NOT NULL,
code VARCHAR(100) NOT NULL,
libelle VARCHAR(255) NOT NULL,
description VARCHAR(1000),
ordre INT NOT NULL DEFAULT 0,
valeur_systeme BOOLEAN NOT NULL DEFAULT FALSE,
actif BOOLEAN NOT NULL DEFAULT TRUE,
date_creation TIMESTAMP NOT NULL DEFAULT NOW(),
date_modification TIMESTAMP,
cree_par VARCHAR(255),
modifie_par VARCHAR(255),
version BIGINT NOT NULL DEFAULT 0,
CONSTRAINT uk_type_ref_domaine_code
UNIQUE (domaine, code)
);
CREATE INDEX IF NOT EXISTS idx_tr_domaine
ON types_reference (domaine);
CREATE INDEX IF NOT EXISTS idx_tr_actif
ON types_reference (actif);
-- ─────────────────────────────────────────────────────────────────────────────
-- Bloc d'idempotence : corrige l'écart entre la table créée par Hibernate
-- (sans DEFAULT SQL) et le schéma attendu par cette migration.
-- Hibernate gère les defaults en Java ; ici on les pose au niveau PostgreSQL.
-- ─────────────────────────────────────────────────────────────────────────────
ALTER TABLE types_reference
ADD COLUMN IF NOT EXISTS valeur_systeme BOOLEAN NOT NULL DEFAULT FALSE;
ALTER TABLE types_reference
ADD COLUMN IF NOT EXISTS ordre INT NOT NULL DEFAULT 0,
ADD COLUMN IF NOT EXISTS actif BOOLEAN NOT NULL DEFAULT TRUE,
ADD COLUMN IF NOT EXISTS version BIGINT NOT NULL DEFAULT 0,
ADD COLUMN IF NOT EXISTS date_creation TIMESTAMP NOT NULL DEFAULT NOW(),
ADD COLUMN IF NOT EXISTS est_defaut BOOLEAN NOT NULL DEFAULT FALSE,
ADD COLUMN IF NOT EXISTS est_systeme BOOLEAN NOT NULL DEFAULT FALSE,
ADD COLUMN IF NOT EXISTS ordre_affichage INT NOT NULL DEFAULT 0;
-- Garantit que la contrainte UNIQUE existe (nécessaire pour ON CONFLICT ci-dessous)
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM pg_constraint
WHERE conname = 'uk_type_ref_domaine_code'
AND conrelid = 'types_reference'::regclass
) THEN
ALTER TABLE types_reference
ADD CONSTRAINT uk_type_ref_domaine_code UNIQUE (domaine, code);
END IF;
END $$;
-- Données initiales : domaines référencés par les entités
-- Toutes les colonnes NOT NULL sont fournies (table peut exister sans DEFAULT si créée par Hibernate)
INSERT INTO types_reference (id, domaine, code, libelle, valeur_systeme, cree_par, actif, ordre, version, date_creation, est_defaut, est_systeme, ordre_affichage)
VALUES
-- OBJET_PAIEMENT (Cat.2 — PaiementObjet)
(gen_random_uuid(), 'OBJET_PAIEMENT', 'COTISATION', 'Cotisation', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0),
(gen_random_uuid(), 'OBJET_PAIEMENT', 'ADHESION', 'Adhésion', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0),
(gen_random_uuid(), 'OBJET_PAIEMENT', 'EVENEMENT', 'Événement', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0),
(gen_random_uuid(), 'OBJET_PAIEMENT', 'AIDE', 'Aide', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0),
-- ENTITE_RATTACHEE (Cat.4 — PieceJointe)
(gen_random_uuid(), 'ENTITE_RATTACHEE', 'MEMBRE', 'Membre', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0),
(gen_random_uuid(), 'ENTITE_RATTACHEE', 'ORGANISATION', 'Organisation', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0),
(gen_random_uuid(), 'ENTITE_RATTACHEE', 'COTISATION', 'Cotisation', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0),
(gen_random_uuid(), 'ENTITE_RATTACHEE', 'ADHESION', 'Adhésion', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0),
(gen_random_uuid(), 'ENTITE_RATTACHEE', 'AIDE', 'Aide', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0),
(gen_random_uuid(), 'ENTITE_RATTACHEE', 'TRANSACTION_WAVE', 'Transaction Wave', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0),
-- STATUT_MATRIMONIAL (Cat.5 — Membre)
(gen_random_uuid(), 'STATUT_MATRIMONIAL', 'CELIBATAIRE', 'Célibataire', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0),
(gen_random_uuid(), 'STATUT_MATRIMONIAL', 'MARIE', 'Marié(e)', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0),
(gen_random_uuid(), 'STATUT_MATRIMONIAL', 'DIVORCE', 'Divorcé(e)', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0),
(gen_random_uuid(), 'STATUT_MATRIMONIAL', 'VEUF', 'Veuf/Veuve', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0),
-- TYPE_IDENTITE (Cat.5 — Membre)
(gen_random_uuid(), 'TYPE_IDENTITE', 'CNI', 'Carte Nationale d''Identité', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0),
(gen_random_uuid(), 'TYPE_IDENTITE', 'PASSEPORT', 'Passeport', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0),
(gen_random_uuid(), 'TYPE_IDENTITE', 'PERMIS', 'Permis de conduire', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0),
(gen_random_uuid(), 'TYPE_IDENTITE', 'CARTE_SEJOUR','Carte de séjour', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0)
ON CONFLICT (domaine, code) DO NOTHING;
-- ─────────────────────────────────────────────────────
-- Cat.2 — paiements_objets (remplace 4 tables)
-- ─────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS paiements_objets (
id UUID PRIMARY KEY,
paiement_id UUID NOT NULL
REFERENCES paiements(id),
type_objet_cible VARCHAR(50) NOT NULL,
objet_cible_id UUID NOT NULL,
montant_applique NUMERIC(14,2) NOT NULL,
date_application TIMESTAMP,
commentaire VARCHAR(500),
actif BOOLEAN NOT NULL DEFAULT TRUE,
date_creation TIMESTAMP NOT NULL DEFAULT NOW(),
date_modification TIMESTAMP,
cree_par VARCHAR(255),
modifie_par VARCHAR(255),
version BIGINT NOT NULL DEFAULT 0,
CONSTRAINT uk_paiement_objet
UNIQUE (paiement_id, type_objet_cible, objet_cible_id)
);
CREATE INDEX IF NOT EXISTS idx_po_paiement
ON paiements_objets (paiement_id);
CREATE INDEX IF NOT EXISTS idx_po_objet
ON paiements_objets (type_objet_cible, objet_cible_id);
CREATE INDEX IF NOT EXISTS idx_po_type
ON paiements_objets (type_objet_cible);
-- ─────────────────────────────────────────────────────
-- Cat.2 — Suppression colonnes adresse de organisations
-- ─────────────────────────────────────────────────────
ALTER TABLE organisations
DROP COLUMN IF EXISTS adresse,
DROP COLUMN IF EXISTS ville,
DROP COLUMN IF EXISTS code_postal,
DROP COLUMN IF EXISTS region,
DROP COLUMN IF EXISTS pays;
-- ─────────────────────────────────────────────────────
-- Cat.4 — pieces_jointes → polymorphique
-- ─────────────────────────────────────────────────────
-- Ajout colonnes polymorphiques
ALTER TABLE pieces_jointes
ADD COLUMN IF NOT EXISTS type_entite_rattachee VARCHAR(50),
ADD COLUMN IF NOT EXISTS entite_rattachee_id UUID;
-- Migration des données existantes (colonnes FK explicites ou entite_type/entite_id selon le schéma)
DO $$
BEGIN
IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = 'public' AND table_name = 'pieces_jointes' AND column_name = 'membre_id') THEN
UPDATE pieces_jointes SET type_entite_rattachee = 'MEMBRE', entite_rattachee_id = membre_id WHERE membre_id IS NOT NULL AND (type_entite_rattachee IS NULL OR type_entite_rattachee = '');
END IF;
IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = 'public' AND table_name = 'pieces_jointes' AND column_name = 'organisation_id') THEN
UPDATE pieces_jointes SET type_entite_rattachee = 'ORGANISATION', entite_rattachee_id = organisation_id WHERE organisation_id IS NOT NULL AND (type_entite_rattachee IS NULL OR type_entite_rattachee = '');
END IF;
IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = 'public' AND table_name = 'pieces_jointes' AND column_name = 'cotisation_id') THEN
UPDATE pieces_jointes SET type_entite_rattachee = 'COTISATION', entite_rattachee_id = cotisation_id WHERE cotisation_id IS NOT NULL AND (type_entite_rattachee IS NULL OR type_entite_rattachee = '');
END IF;
IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = 'public' AND table_name = 'pieces_jointes' AND column_name = 'adhesion_id') THEN
UPDATE pieces_jointes SET type_entite_rattachee = 'ADHESION', entite_rattachee_id = adhesion_id WHERE adhesion_id IS NOT NULL AND (type_entite_rattachee IS NULL OR type_entite_rattachee = '');
END IF;
IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = 'public' AND table_name = 'pieces_jointes' AND column_name = 'demande_aide_id') THEN
UPDATE pieces_jointes SET type_entite_rattachee = 'AIDE', entite_rattachee_id = demande_aide_id WHERE demande_aide_id IS NOT NULL AND (type_entite_rattachee IS NULL OR type_entite_rattachee = '');
END IF;
IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = 'public' AND table_name = 'pieces_jointes' AND column_name = 'transaction_wave_id') THEN
UPDATE pieces_jointes SET type_entite_rattachee = 'TRANSACTION_WAVE', entite_rattachee_id = transaction_wave_id WHERE transaction_wave_id IS NOT NULL AND (type_entite_rattachee IS NULL OR type_entite_rattachee = '');
END IF;
-- Schéma V1.7 : entite_type / entite_id
IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = 'public' AND table_name = 'pieces_jointes' AND column_name = 'entite_type') THEN
UPDATE pieces_jointes SET type_entite_rattachee = COALESCE(NULLIF(TRIM(entite_type), ''), 'MEMBRE'), entite_rattachee_id = entite_id WHERE entite_id IS NOT NULL AND (type_entite_rattachee IS NULL OR type_entite_rattachee = '');
END IF;
-- Valeurs par défaut pour lignes restantes (évite échec NOT NULL)
UPDATE pieces_jointes SET type_entite_rattachee = COALESCE(NULLIF(TRIM(type_entite_rattachee), ''), 'MEMBRE'), entite_rattachee_id = COALESCE(entite_rattachee_id, (SELECT id FROM utilisateurs LIMIT 1)) WHERE type_entite_rattachee IS NULL OR type_entite_rattachee = '' OR entite_rattachee_id IS NULL;
END $$;
-- Contrainte NOT NULL après migration (seulement si plus aucune ligne NULL)
DO $$
BEGIN
IF NOT EXISTS (SELECT 1 FROM pieces_jointes WHERE type_entite_rattachee IS NULL OR type_entite_rattachee = '' OR entite_rattachee_id IS NULL) THEN
EXECUTE 'ALTER TABLE pieces_jointes ALTER COLUMN type_entite_rattachee SET NOT NULL';
EXECUTE 'ALTER TABLE pieces_jointes ALTER COLUMN entite_rattachee_id SET NOT NULL';
END IF;
END $$;
-- Suppression anciennes FK ou colonnes polymorphiques V1.7 (entite_type, entite_id)
ALTER TABLE pieces_jointes
DROP COLUMN IF EXISTS membre_id,
DROP COLUMN IF EXISTS organisation_id,
DROP COLUMN IF EXISTS cotisation_id,
DROP COLUMN IF EXISTS adhesion_id,
DROP COLUMN IF EXISTS demande_aide_id,
DROP COLUMN IF EXISTS transaction_wave_id,
DROP COLUMN IF EXISTS entite_type,
DROP COLUMN IF EXISTS entite_id;
-- Suppression anciens index
DROP INDEX IF EXISTS idx_piece_jointe_membre;
DROP INDEX IF EXISTS idx_piece_jointe_organisation;
DROP INDEX IF EXISTS idx_piece_jointe_cotisation;
DROP INDEX IF EXISTS idx_piece_jointe_adhesion;
DROP INDEX IF EXISTS idx_piece_jointe_demande_aide;
DROP INDEX IF EXISTS idx_piece_jointe_transaction_wave;
-- Nouveaux index polymorphiques
CREATE INDEX IF NOT EXISTS idx_pj_entite
ON pieces_jointes (type_entite_rattachee, entite_rattachee_id);
CREATE INDEX IF NOT EXISTS idx_pj_type_entite
ON pieces_jointes (type_entite_rattachee);
-- ─────────────────────────────────────────────────────
-- Cat.5 — Colonnes Membre manquantes (table utilisateurs depuis V2.0)
-- ─────────────────────────────────────────────────────
ALTER TABLE utilisateurs
ADD COLUMN IF NOT EXISTS statut_matrimonial VARCHAR(50),
ADD COLUMN IF NOT EXISTS nationalite VARCHAR(100),
ADD COLUMN IF NOT EXISTS type_identite VARCHAR(50),
ADD COLUMN IF NOT EXISTS numero_identite VARCHAR(100);
-- ─────────────────────────────────────────────────────
-- Cat.8 — Valeurs par défaut dans configurations
-- ─────────────────────────────────────────────────────
INSERT INTO configurations (id, cle, valeur, type, categorie, description, modifiable, visible, actif, date_creation, cree_par, version)
VALUES
(gen_random_uuid(), 'defaut.devise', 'XOF', 'STRING', 'SYSTEME', 'Devise par défaut', TRUE, TRUE, TRUE, NOW(), 'system', 0),
(gen_random_uuid(), 'defaut.statut.organisation', 'ACTIVE', 'STRING', 'SYSTEME', 'Statut initial organisation', TRUE, TRUE, TRUE, NOW(), 'system', 0),
(gen_random_uuid(), 'defaut.type.organisation', 'ASSOCIATION', 'STRING', 'SYSTEME', 'Type initial organisation', TRUE, TRUE, TRUE, NOW(), 'system', 0),
(gen_random_uuid(), 'defaut.utilisateur.systeme', 'system', 'STRING', 'SYSTEME', 'Identifiant utilisateur système', FALSE, FALSE, TRUE, NOW(), 'system', 0),
(gen_random_uuid(), 'defaut.montant.cotisation', '0', 'NUMBER', 'SYSTEME', 'Montant cotisation par défaut', TRUE, TRUE, TRUE, NOW(), 'system', 0)
ON CONFLICT DO NOTHING;
-- ─────────────────────────────────────────────────────
-- Cat.7 — Index composites pour requêtes fréquentes
-- ─────────────────────────────────────────────────────
-- Aligner paiements avec l'entité (statut → statut_paiement si la colonne existe)
DO $$
BEGIN
IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = 'public' AND table_name = 'paiements' AND column_name = 'statut')
AND NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = 'public' AND table_name = 'paiements' AND column_name = 'statut_paiement') THEN
ALTER TABLE paiements RENAME COLUMN statut TO statut_paiement;
END IF;
END $$;
CREATE INDEX IF NOT EXISTS idx_cotisation_org_statut_annee
ON cotisations (organisation_id, statut, annee);
CREATE INDEX IF NOT EXISTS idx_cotisation_membre_statut
ON cotisations (membre_id, statut);
CREATE INDEX IF NOT EXISTS idx_paiement_membre_statut_date
ON paiements (membre_id, statut_paiement,
date_paiement);
CREATE INDEX IF NOT EXISTS idx_notification_membre_statut
ON notifications (membre_id, statut, date_envoi);
CREATE INDEX IF NOT EXISTS idx_adhesion_org_statut
ON demandes_adhesion (organisation_id, statut);
CREATE INDEX IF NOT EXISTS idx_aide_org_statut_urgence
ON demandes_aide (organisation_id, statut, urgence);
CREATE INDEX IF NOT EXISTS idx_membreorg_org_statut
ON membres_organisations
(organisation_id, statut_membre);
CREATE INDEX IF NOT EXISTS idx_evenement_org_date_statut
ON evenements
(organisation_id, date_debut, statut);
-- ─────────────────────────────────────────────────────
-- Cat.7 — Contraintes CHECK métier
-- ─────────────────────────────────────────────────────
ALTER TABLE cotisations
ADD CONSTRAINT chk_montant_paye_le_du
CHECK (montant_paye <= montant_du);
ALTER TABLE souscriptions_organisation
ADD CONSTRAINT chk_quota_utilise_le_max
CHECK (quota_utilise <= quota_max);

View File

@@ -1,24 +1,24 @@
-- =====================================================
-- V3.1 — Correction Intégrité Référentielle Modules
-- Cat.2 — ModuleOrganisationActif -> ModuleDisponible
-- =====================================================
-- 1. Ajout de la colonne FK
ALTER TABLE modules_organisation_actifs
ADD COLUMN IF NOT EXISTS module_disponible_id UUID;
-- 2. Migration des données basées sur module_code
UPDATE modules_organisation_actifs moa
SET module_disponible_id = (SELECT id FROM modules_disponibles md WHERE md.code = moa.module_code);
-- 3. Ajout de la contrainte FK
ALTER TABLE modules_organisation_actifs
ADD CONSTRAINT fk_moa_module_disponible
FOREIGN KEY (module_disponible_id) REFERENCES modules_disponibles(id)
ON DELETE RESTRICT;
-- 4. Nettoyage (Optionnel : on garde module_code pour compatibilité DTO existante si nécessaire,
-- mais on force la cohérence via un index unique si possible)
CREATE INDEX IF NOT EXISTS idx_moa_module_id ON modules_organisation_actifs(module_disponible_id);
-- Note: L'audit demandait l'intégrité, c'est fait.
-- =====================================================
-- V3.1 — Correction Intégrité Référentielle Modules
-- Cat.2 — ModuleOrganisationActif -> ModuleDisponible
-- =====================================================
-- 1. Ajout de la colonne FK
ALTER TABLE modules_organisation_actifs
ADD COLUMN IF NOT EXISTS module_disponible_id UUID;
-- 2. Migration des données basées sur module_code
UPDATE modules_organisation_actifs moa
SET module_disponible_id = (SELECT id FROM modules_disponibles md WHERE md.code = moa.module_code);
-- 3. Ajout de la contrainte FK
ALTER TABLE modules_organisation_actifs
ADD CONSTRAINT fk_moa_module_disponible
FOREIGN KEY (module_disponible_id) REFERENCES modules_disponibles(id)
ON DELETE RESTRICT;
-- 4. Nettoyage (Optionnel : on garde module_code pour compatibilité DTO existante si nécessaire,
-- mais on force la cohérence via un index unique si possible)
CREATE INDEX IF NOT EXISTS idx_moa_module_id ON modules_organisation_actifs(module_disponible_id);
-- Note: L'audit demandait l'intégrité, c'est fait.

View File

@@ -1,58 +1,58 @@
-- =====================================================
-- V3.2 — Initialisation des Types de Référence
-- Cat.1 — Centralisation des domaines de valeurs
-- Colonnes alignées sur l'entité TypeReference (domaine, code, etc.)
-- =====================================================
-- 2. Statut Matrimonial (complément éventuel à V3.0)
INSERT INTO types_reference (id, domaine, code, libelle, description, valeur_systeme, cree_par, actif, ordre, version, date_creation, est_defaut, est_systeme, ordre_affichage)
VALUES
(gen_random_uuid(), 'STATUT_MATRIMONIAL', 'CELIBATAIRE', 'Célibataire', 'Membre non marié', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0),
(gen_random_uuid(), 'STATUT_MATRIMONIAL', 'MARIE', 'Marié(e)', 'Membre marié', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0),
(gen_random_uuid(), 'STATUT_MATRIMONIAL', 'VEUF', 'Veuf/Veuve', 'Membre ayant perdu son conjoint', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0),
(gen_random_uuid(), 'STATUT_MATRIMONIAL', 'DIVORCE', 'Divorcé(e)', 'Membre divorcé', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0)
ON CONFLICT (domaine, code) DO NOTHING;
-- 3. Type d'Identité
INSERT INTO types_reference (id, domaine, code, libelle, description, valeur_systeme, cree_par, actif, ordre, version, date_creation, est_defaut, est_systeme, ordre_affichage)
VALUES
(gen_random_uuid(), 'TYPE_IDENTITE', 'CNI', 'Carte Nationale d''Identité', 'Pièce d''identité nationale', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0),
(gen_random_uuid(), 'TYPE_IDENTITE', 'PASSEPORT', 'Passeport', 'Passeport international', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0),
(gen_random_uuid(), 'TYPE_IDENTITE', 'PERMIS_CONDUIRE', 'Permis de conduire', 'Permis de conduire officiel', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0),
(gen_random_uuid(), 'TYPE_IDENTITE', 'CARTE_CONSULAIRE', 'Carte Consulaire', 'Carte délivrée par un consulat', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0)
ON CONFLICT (domaine, code) DO NOTHING;
-- 4. Objet de Paiement (compléments à V3.0)
INSERT INTO types_reference (id, domaine, code, libelle, description, valeur_systeme, cree_par, actif, ordre, version, date_creation, est_defaut, est_systeme, ordre_affichage)
VALUES
(gen_random_uuid(), 'OBJET_PAIEMENT', 'COTISATION', 'Cotisation annuelle', 'Paiement de la cotisation de membre', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0),
(gen_random_uuid(), 'OBJET_PAIEMENT', 'DON', 'Don gracieux', 'Don volontaire pour l''association', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0),
(gen_random_uuid(), 'OBJET_PAIEMENT', 'INSCRIPTION_EVENEMENT', 'Inscription à un événement', 'Paiement pour participer à un événement', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0),
(gen_random_uuid(), 'OBJET_PAIEMENT', 'AMENDE', 'Amende / Sanction', 'Paiement suite à une sanction', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0)
ON CONFLICT (domaine, code) DO NOTHING;
-- 5. Type d'Organisation
INSERT INTO types_reference (id, domaine, code, libelle, description, valeur_systeme, cree_par, actif, ordre, version, date_creation, est_defaut, est_systeme, ordre_affichage)
VALUES
(gen_random_uuid(), 'TYPE_ORGANISATION', 'ASSOCIATION', 'Association', 'Organisation type association', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0),
(gen_random_uuid(), 'TYPE_ORGANISATION', 'COOPERATIVE', 'Coopérative', 'Organisation type coopérative', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0),
(gen_random_uuid(), 'TYPE_ORGANISATION', 'FEDERATION', 'Fédération', 'Regroupement d''associations', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0),
(gen_random_uuid(), 'TYPE_ORGANISATION', 'CELLULE', 'Cellule de base', 'Unité locale d''une organisation', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0)
ON CONFLICT (domaine, code) DO NOTHING;
-- 6. Type de Rôle
INSERT INTO types_reference (id, domaine, code, libelle, description, valeur_systeme, cree_par, actif, ordre, version, date_creation, est_defaut, est_systeme, ordre_affichage)
VALUES
(gen_random_uuid(), 'TYPE_ROLE', 'SYSTEME', 'Système', 'Rôle global non modifiable', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0),
(gen_random_uuid(), 'TYPE_ROLE', 'ORGANISATION', 'Organisation', 'Rôle spécifique à une organisation', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0),
(gen_random_uuid(), 'TYPE_ROLE', 'PERSONNALISE', 'Personnalisé', 'Rôle créé manuellement', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0)
ON CONFLICT (domaine, code) DO NOTHING;
-- 7. Statut d'Inscription
INSERT INTO types_reference (id, domaine, code, libelle, description, valeur_systeme, cree_par, actif, ordre, version, date_creation, est_defaut, est_systeme, ordre_affichage)
VALUES
(gen_random_uuid(), 'STATUT_INSCRIPTION', 'CONFIRMEE', 'Confirmée', 'Inscription validée', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0),
(gen_random_uuid(), 'STATUT_INSCRIPTION', 'EN_ATTENTE', 'En attente', 'En attente de validation', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0),
(gen_random_uuid(), 'STATUT_INSCRIPTION', 'ANNULEE', 'Annulée', 'Inscription annulée par l''utilisateur', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0),
(gen_random_uuid(), 'STATUT_INSCRIPTION', 'REFUSEE', 'Refusée', 'Inscription rejetée par l''organisateur', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0)
ON CONFLICT (domaine, code) DO NOTHING;
-- =====================================================
-- V3.2 — Initialisation des Types de Référence
-- Cat.1 — Centralisation des domaines de valeurs
-- Colonnes alignées sur l'entité TypeReference (domaine, code, etc.)
-- =====================================================
-- 2. Statut Matrimonial (complément éventuel à V3.0)
INSERT INTO types_reference (id, domaine, code, libelle, description, valeur_systeme, cree_par, actif, ordre, version, date_creation, est_defaut, est_systeme, ordre_affichage)
VALUES
(gen_random_uuid(), 'STATUT_MATRIMONIAL', 'CELIBATAIRE', 'Célibataire', 'Membre non marié', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0),
(gen_random_uuid(), 'STATUT_MATRIMONIAL', 'MARIE', 'Marié(e)', 'Membre marié', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0),
(gen_random_uuid(), 'STATUT_MATRIMONIAL', 'VEUF', 'Veuf/Veuve', 'Membre ayant perdu son conjoint', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0),
(gen_random_uuid(), 'STATUT_MATRIMONIAL', 'DIVORCE', 'Divorcé(e)', 'Membre divorcé', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0)
ON CONFLICT (domaine, code) DO NOTHING;
-- 3. Type d'Identité
INSERT INTO types_reference (id, domaine, code, libelle, description, valeur_systeme, cree_par, actif, ordre, version, date_creation, est_defaut, est_systeme, ordre_affichage)
VALUES
(gen_random_uuid(), 'TYPE_IDENTITE', 'CNI', 'Carte Nationale d''Identité', 'Pièce d''identité nationale', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0),
(gen_random_uuid(), 'TYPE_IDENTITE', 'PASSEPORT', 'Passeport', 'Passeport international', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0),
(gen_random_uuid(), 'TYPE_IDENTITE', 'PERMIS_CONDUIRE', 'Permis de conduire', 'Permis de conduire officiel', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0),
(gen_random_uuid(), 'TYPE_IDENTITE', 'CARTE_CONSULAIRE', 'Carte Consulaire', 'Carte délivrée par un consulat', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0)
ON CONFLICT (domaine, code) DO NOTHING;
-- 4. Objet de Paiement (compléments à V3.0)
INSERT INTO types_reference (id, domaine, code, libelle, description, valeur_systeme, cree_par, actif, ordre, version, date_creation, est_defaut, est_systeme, ordre_affichage)
VALUES
(gen_random_uuid(), 'OBJET_PAIEMENT', 'COTISATION', 'Cotisation annuelle', 'Paiement de la cotisation de membre', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0),
(gen_random_uuid(), 'OBJET_PAIEMENT', 'DON', 'Don gracieux', 'Don volontaire pour l''association', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0),
(gen_random_uuid(), 'OBJET_PAIEMENT', 'INSCRIPTION_EVENEMENT', 'Inscription à un événement', 'Paiement pour participer à un événement', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0),
(gen_random_uuid(), 'OBJET_PAIEMENT', 'AMENDE', 'Amende / Sanction', 'Paiement suite à une sanction', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0)
ON CONFLICT (domaine, code) DO NOTHING;
-- 5. Type d'Organisation
INSERT INTO types_reference (id, domaine, code, libelle, description, valeur_systeme, cree_par, actif, ordre, version, date_creation, est_defaut, est_systeme, ordre_affichage)
VALUES
(gen_random_uuid(), 'TYPE_ORGANISATION', 'ASSOCIATION', 'Association', 'Organisation type association', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0),
(gen_random_uuid(), 'TYPE_ORGANISATION', 'COOPERATIVE', 'Coopérative', 'Organisation type coopérative', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0),
(gen_random_uuid(), 'TYPE_ORGANISATION', 'FEDERATION', 'Fédération', 'Regroupement d''associations', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0),
(gen_random_uuid(), 'TYPE_ORGANISATION', 'CELLULE', 'Cellule de base', 'Unité locale d''une organisation', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0)
ON CONFLICT (domaine, code) DO NOTHING;
-- 6. Type de Rôle
INSERT INTO types_reference (id, domaine, code, libelle, description, valeur_systeme, cree_par, actif, ordre, version, date_creation, est_defaut, est_systeme, ordre_affichage)
VALUES
(gen_random_uuid(), 'TYPE_ROLE', 'SYSTEME', 'Système', 'Rôle global non modifiable', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0),
(gen_random_uuid(), 'TYPE_ROLE', 'ORGANISATION', 'Organisation', 'Rôle spécifique à une organisation', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0),
(gen_random_uuid(), 'TYPE_ROLE', 'PERSONNALISE', 'Personnalisé', 'Rôle créé manuellement', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0)
ON CONFLICT (domaine, code) DO NOTHING;
-- 7. Statut d'Inscription
INSERT INTO types_reference (id, domaine, code, libelle, description, valeur_systeme, cree_par, actif, ordre, version, date_creation, est_defaut, est_systeme, ordre_affichage)
VALUES
(gen_random_uuid(), 'STATUT_INSCRIPTION', 'CONFIRMEE', 'Confirmée', 'Inscription validée', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0),
(gen_random_uuid(), 'STATUT_INSCRIPTION', 'EN_ATTENTE', 'En attente', 'En attente de validation', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0),
(gen_random_uuid(), 'STATUT_INSCRIPTION', 'ANNULEE', 'Annulée', 'Inscription annulée par l''utilisateur', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0),
(gen_random_uuid(), 'STATUT_INSCRIPTION', 'REFUSEE', 'Refusée', 'Inscription rejetée par l''organisateur', TRUE, 'system', TRUE, 0, 0, NOW(), FALSE, TRUE, 0)
ON CONFLICT (domaine, code) DO NOTHING;

View File

@@ -1,20 +1,20 @@
-- =====================================================
-- V3.3 — Optimisation des Index de Performance
-- Cat.7 — Index composites pour recherches fréquentes
-- =====================================================
-- 1. Index composite sur les membres (Recherche par nom complet)
CREATE INDEX IF NOT EXISTS idx_membre_nom_prenom ON utilisateurs(nom, prenom);
-- 2. Index composite sur les cotisations (Recherche par membre et année)
CREATE INDEX IF NOT EXISTS idx_cotisation_membre_annee ON cotisations(membre_id, annee);
-- 3. Index sur le Keycloak ID pour synchronisation rapide
CREATE INDEX IF NOT EXISTS idx_membre_keycloak_id ON utilisateurs(keycloak_id);
-- 4. Index sur le statut des paiements
CREATE INDEX IF NOT EXISTS idx_paiement_statut_paiement ON paiements(statut_paiement);
-- 5. Index sur les dates de création pour tris par défaut
CREATE INDEX IF NOT EXISTS idx_membre_date_creation ON utilisateurs(date_creation DESC);
CREATE INDEX IF NOT EXISTS idx_organisation_date_creation ON organisations(date_creation DESC);
-- =====================================================
-- V3.3 — Optimisation des Index de Performance
-- Cat.7 — Index composites pour recherches fréquentes
-- =====================================================
-- 1. Index composite sur les membres (Recherche par nom complet)
CREATE INDEX IF NOT EXISTS idx_membre_nom_prenom ON utilisateurs(nom, prenom);
-- 2. Index composite sur les cotisations (Recherche par membre et année)
CREATE INDEX IF NOT EXISTS idx_cotisation_membre_annee ON cotisations(membre_id, annee);
-- 3. Index sur le Keycloak ID pour synchronisation rapide
CREATE INDEX IF NOT EXISTS idx_membre_keycloak_id ON utilisateurs(keycloak_id);
-- 4. Index sur le statut des paiements
CREATE INDEX IF NOT EXISTS idx_paiement_statut_paiement ON paiements(statut_paiement);
-- 5. Index sur les dates de création pour tris par défaut
CREATE INDEX IF NOT EXISTS idx_membre_date_creation ON utilisateurs(date_creation DESC);
CREATE INDEX IF NOT EXISTS idx_organisation_date_creation ON organisations(date_creation DESC);

View File

@@ -1,73 +1,73 @@
-- ============================================================
-- V3.4 — LCB-FT / Anti-blanchiment (mutuelles)
-- Spec: specs/001-mutuelles-anti-blanchiment/spec.md
-- Traçabilité origine des fonds, KYC, seuils
-- ============================================================
-- 1. Utilisateurs (identité) — vigilance KYC
ALTER TABLE utilisateurs
ADD COLUMN IF NOT EXISTS niveau_vigilance_kyc VARCHAR(20) DEFAULT 'SIMPLIFIE',
ADD COLUMN IF NOT EXISTS statut_kyc VARCHAR(20) DEFAULT 'NON_VERIFIE',
ADD COLUMN IF NOT EXISTS date_verification_identite DATE;
ALTER TABLE utilisateurs
ADD CONSTRAINT chk_utilisateur_niveau_kyc
CHECK (niveau_vigilance_kyc IS NULL OR niveau_vigilance_kyc IN ('SIMPLIFIE', 'RENFORCE'));
ALTER TABLE utilisateurs
ADD CONSTRAINT chk_utilisateur_statut_kyc
CHECK (statut_kyc IS NULL OR statut_kyc IN ('NON_VERIFIE', 'EN_COURS', 'VERIFIE', 'REFUSE'));
CREATE INDEX IF NOT EXISTS idx_utilisateur_statut_kyc ON utilisateurs(statut_kyc);
COMMENT ON COLUMN utilisateurs.niveau_vigilance_kyc IS 'Niveau de vigilance KYC LCB-FT';
COMMENT ON COLUMN utilisateurs.statut_kyc IS 'Statut vérification identité';
COMMENT ON COLUMN utilisateurs.date_verification_identite IS 'Date de dernière vérification d''identité';
-- 2. Intentions de paiement — origine des fonds / justification LCB-FT
ALTER TABLE intentions_paiement
ADD COLUMN IF NOT EXISTS origine_fonds VARCHAR(200),
ADD COLUMN IF NOT EXISTS justification_lcb_ft TEXT;
COMMENT ON COLUMN intentions_paiement.origine_fonds IS 'Origine des fonds déclarée (obligatoire au-dessus du seuil)';
COMMENT ON COLUMN intentions_paiement.justification_lcb_ft IS 'Justification LCB-FT optionnelle';
-- 3. Transactions épargne — origine des fonds, pièce justificative (si la table existe)
DO $$
BEGIN
IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'transactions_epargne') THEN
ALTER TABLE transactions_epargne
ADD COLUMN IF NOT EXISTS origine_fonds VARCHAR(200),
ADD COLUMN IF NOT EXISTS piece_justificative_id UUID;
EXECUTE 'COMMENT ON COLUMN transactions_epargne.origine_fonds IS ''Origine des fonds (obligatoire au-dessus du seuil LCB-FT)''';
EXECUTE 'COMMENT ON COLUMN transactions_epargne.piece_justificative_id IS ''Référence pièce jointe justificative''';
END IF;
END $$;
-- 4. Paramètres LCB-FT (seuils par organisation ou globaux)
CREATE TABLE IF NOT EXISTS parametres_lcb_ft (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
organisation_id UUID,
code_devise VARCHAR(3) NOT NULL DEFAULT 'XOF',
montant_seuil_justification DECIMAL(18,4) NOT NULL,
montant_seuil_validation_manuelle DECIMAL(18,4),
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_param_lcb_ft_org FOREIGN KEY (organisation_id) REFERENCES organisations(id) ON DELETE CASCADE,
CONSTRAINT chk_param_devise CHECK (code_devise ~ '^[A-Z]{3}$')
);
CREATE UNIQUE INDEX IF NOT EXISTS idx_param_lcb_ft_org_devise
ON parametres_lcb_ft(COALESCE(organisation_id, '00000000-0000-0000-0000-000000000000'::uuid), code_devise);
CREATE INDEX IF NOT EXISTS idx_param_lcb_ft_org ON parametres_lcb_ft(organisation_id);
COMMENT ON TABLE parametres_lcb_ft IS 'Seuils LCB-FT : au-dessus de montant_seuil_justification, origine des fonds obligatoire';
COMMENT ON COLUMN parametres_lcb_ft.organisation_id IS 'NULL = paramètres plateforme par défaut';
-- Valeur par défaut plateforme (XOF) — une seule ligne org NULL + XOF (toutes colonnes NOT NULL fournies)
INSERT INTO parametres_lcb_ft (id, organisation_id, code_devise, montant_seuil_justification, montant_seuil_validation_manuelle, cree_par, actif, date_creation, version)
SELECT gen_random_uuid(), NULL, 'XOF', 500000, 1000000, 'system', TRUE, NOW(), 0
WHERE NOT EXISTS (SELECT 1 FROM parametres_lcb_ft WHERE organisation_id IS NULL AND code_devise = 'XOF');
-- ============================================================
-- V3.4 — LCB-FT / Anti-blanchiment (mutuelles)
-- Spec: specs/001-mutuelles-anti-blanchiment/spec.md
-- Traçabilité origine des fonds, KYC, seuils
-- ============================================================
-- 1. Utilisateurs (identité) — vigilance KYC
ALTER TABLE utilisateurs
ADD COLUMN IF NOT EXISTS niveau_vigilance_kyc VARCHAR(20) DEFAULT 'SIMPLIFIE',
ADD COLUMN IF NOT EXISTS statut_kyc VARCHAR(20) DEFAULT 'NON_VERIFIE',
ADD COLUMN IF NOT EXISTS date_verification_identite DATE;
ALTER TABLE utilisateurs
ADD CONSTRAINT chk_utilisateur_niveau_kyc
CHECK (niveau_vigilance_kyc IS NULL OR niveau_vigilance_kyc IN ('SIMPLIFIE', 'RENFORCE'));
ALTER TABLE utilisateurs
ADD CONSTRAINT chk_utilisateur_statut_kyc
CHECK (statut_kyc IS NULL OR statut_kyc IN ('NON_VERIFIE', 'EN_COURS', 'VERIFIE', 'REFUSE'));
CREATE INDEX IF NOT EXISTS idx_utilisateur_statut_kyc ON utilisateurs(statut_kyc);
COMMENT ON COLUMN utilisateurs.niveau_vigilance_kyc IS 'Niveau de vigilance KYC LCB-FT';
COMMENT ON COLUMN utilisateurs.statut_kyc IS 'Statut vérification identité';
COMMENT ON COLUMN utilisateurs.date_verification_identite IS 'Date de dernière vérification d''identité';
-- 2. Intentions de paiement — origine des fonds / justification LCB-FT
ALTER TABLE intentions_paiement
ADD COLUMN IF NOT EXISTS origine_fonds VARCHAR(200),
ADD COLUMN IF NOT EXISTS justification_lcb_ft TEXT;
COMMENT ON COLUMN intentions_paiement.origine_fonds IS 'Origine des fonds déclarée (obligatoire au-dessus du seuil)';
COMMENT ON COLUMN intentions_paiement.justification_lcb_ft IS 'Justification LCB-FT optionnelle';
-- 3. Transactions épargne — origine des fonds, pièce justificative (si la table existe)
DO $$
BEGIN
IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'transactions_epargne') THEN
ALTER TABLE transactions_epargne
ADD COLUMN IF NOT EXISTS origine_fonds VARCHAR(200),
ADD COLUMN IF NOT EXISTS piece_justificative_id UUID;
EXECUTE 'COMMENT ON COLUMN transactions_epargne.origine_fonds IS ''Origine des fonds (obligatoire au-dessus du seuil LCB-FT)''';
EXECUTE 'COMMENT ON COLUMN transactions_epargne.piece_justificative_id IS ''Référence pièce jointe justificative''';
END IF;
END $$;
-- 4. Paramètres LCB-FT (seuils par organisation ou globaux)
CREATE TABLE IF NOT EXISTS parametres_lcb_ft (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
organisation_id UUID,
code_devise VARCHAR(3) NOT NULL DEFAULT 'XOF',
montant_seuil_justification DECIMAL(18,4) NOT NULL,
montant_seuil_validation_manuelle DECIMAL(18,4),
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_param_lcb_ft_org FOREIGN KEY (organisation_id) REFERENCES organisations(id) ON DELETE CASCADE,
CONSTRAINT chk_param_devise CHECK (code_devise ~ '^[A-Z]{3}$')
);
CREATE UNIQUE INDEX IF NOT EXISTS idx_param_lcb_ft_org_devise
ON parametres_lcb_ft(COALESCE(organisation_id, '00000000-0000-0000-0000-000000000000'::uuid), code_devise);
CREATE INDEX IF NOT EXISTS idx_param_lcb_ft_org ON parametres_lcb_ft(organisation_id);
COMMENT ON TABLE parametres_lcb_ft IS 'Seuils LCB-FT : au-dessus de montant_seuil_justification, origine des fonds obligatoire';
COMMENT ON COLUMN parametres_lcb_ft.organisation_id IS 'NULL = paramètres plateforme par défaut';
-- Valeur par défaut plateforme (XOF) — une seule ligne org NULL + XOF (toutes colonnes NOT NULL fournies)
INSERT INTO parametres_lcb_ft (id, organisation_id, code_devise, montant_seuil_justification, montant_seuil_validation_manuelle, cree_par, actif, date_creation, version)
SELECT gen_random_uuid(), NULL, 'XOF', 500000, 1000000, 'system', TRUE, NOW(), 0
WHERE NOT EXISTS (SELECT 1 FROM parametres_lcb_ft WHERE organisation_id IS NULL AND code_devise = 'XOF');

View File

@@ -1,23 +1,23 @@
-- Migration V3.5 : Ajout des champs d'adresse dans la table organisations
-- Date : 2026-02-28
-- Description : Ajoute les champs adresse, ville, région, pays et code postal
-- pour stocker l'adresse principale directement dans organisations
-- Ajout des colonnes d'adresse
ALTER TABLE organisations ADD COLUMN IF NOT EXISTS adresse VARCHAR(500);
ALTER TABLE organisations ADD COLUMN IF NOT EXISTS ville VARCHAR(100);
ALTER TABLE organisations ADD COLUMN IF NOT EXISTS region VARCHAR(100);
ALTER TABLE organisations ADD COLUMN IF NOT EXISTS pays VARCHAR(100);
ALTER TABLE organisations ADD COLUMN IF NOT EXISTS code_postal VARCHAR(20);
-- Ajout d'index pour optimiser les recherches par localisation
CREATE INDEX IF NOT EXISTS idx_organisation_ville ON organisations(ville);
CREATE INDEX IF NOT EXISTS idx_organisation_region ON organisations(region);
CREATE INDEX IF NOT EXISTS idx_organisation_pays ON organisations(pays);
-- Commentaires sur les colonnes
COMMENT ON COLUMN organisations.adresse IS 'Adresse principale de l''organisation (dénormalisée pour performance)';
COMMENT ON COLUMN organisations.ville IS 'Ville de l''adresse principale';
COMMENT ON COLUMN organisations.region IS 'Région/Province/État de l''adresse principale';
COMMENT ON COLUMN organisations.pays IS 'Pays de l''adresse principale';
COMMENT ON COLUMN organisations.code_postal IS 'Code postal de l''adresse principale';
-- Migration V3.5 : Ajout des champs d'adresse dans la table organisations
-- Date : 2026-02-28
-- Description : Ajoute les champs adresse, ville, région, pays et code postal
-- pour stocker l'adresse principale directement dans organisations
-- Ajout des colonnes d'adresse
ALTER TABLE organisations ADD COLUMN IF NOT EXISTS adresse VARCHAR(500);
ALTER TABLE organisations ADD COLUMN IF NOT EXISTS ville VARCHAR(100);
ALTER TABLE organisations ADD COLUMN IF NOT EXISTS region VARCHAR(100);
ALTER TABLE organisations ADD COLUMN IF NOT EXISTS pays VARCHAR(100);
ALTER TABLE organisations ADD COLUMN IF NOT EXISTS code_postal VARCHAR(20);
-- Ajout d'index pour optimiser les recherches par localisation
CREATE INDEX IF NOT EXISTS idx_organisation_ville ON organisations(ville);
CREATE INDEX IF NOT EXISTS idx_organisation_region ON organisations(region);
CREATE INDEX IF NOT EXISTS idx_organisation_pays ON organisations(pays);
-- Commentaires sur les colonnes
COMMENT ON COLUMN organisations.adresse IS 'Adresse principale de l''organisation (dénormalisée pour performance)';
COMMENT ON COLUMN organisations.ville IS 'Ville de l''adresse principale';
COMMENT ON COLUMN organisations.region IS 'Région/Province/État de l''adresse principale';
COMMENT ON COLUMN organisations.pays IS 'Pays de l''adresse principale';
COMMENT ON COLUMN organisations.code_postal IS 'Code postal de l''adresse principale';

View File

@@ -1,152 +1,152 @@
-- Migration V3.6 - Création des organisations de test MUKEFI et MESKA
-- UnionFlow - Configuration initiale pour tests
-- ⚠ Correction : INSERT dans "organisations" (pluriel, table JPA gérée par Hibernate,
-- définie en V1.2), et non "organisation" (singulier, ancienne table isolée).
-- ============================================================================
-- 1. ORGANISATION MUKEFI (Mutuelle d'épargne et de crédit)
-- ============================================================================
DELETE FROM organisations WHERE nom_court = 'MUKEFI';
INSERT INTO organisations (
id,
nom,
nom_court,
description,
email,
telephone,
site_web,
type_organisation,
statut,
date_fondation,
numero_enregistrement,
devise,
budget_annuel,
cotisation_obligatoire,
montant_cotisation_annuelle,
objectifs,
activites_principales,
partenaires,
latitude,
longitude,
date_creation,
date_modification,
cree_par,
modifie_par,
version,
actif,
accepte_nouveaux_membres,
est_organisation_racine,
niveau_hierarchique,
nombre_membres,
nombre_administrateurs,
organisation_publique
) VALUES (
gen_random_uuid(),
'Mutuelle d''Épargne et de Crédit des Fonctionnaires et Indépendants',
'MUKEFI',
'Mutuelle d''épargne et de crédit dédiée aux fonctionnaires et travailleurs indépendants de Côte d''Ivoire',
'contact@mukefi.org',
'+225 07 00 00 00 01',
'https://mukefi.org',
'ASSOCIATION',
'ACTIVE',
'2020-01-15',
'MUT-CI-2020-001',
'XOF',
500000000,
true,
50000,
'Favoriser l''épargne et l''accès au crédit pour les membres',
'Épargne, crédit, micro-crédit, formation financière',
'Banque Centrale des États de l''Afrique de l''Ouest (BCEAO)',
5.3364,
-4.0267,
CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP,
'superadmin@unionflow.test',
'superadmin@unionflow.test',
0,
true,
true,
true,
0,
0,
0,
true
);
-- ============================================================================
-- 2. ORGANISATION MESKA (Association)
-- ============================================================================
DELETE FROM organisations WHERE nom_court = 'MESKA';
INSERT INTO organisations (
id,
nom,
nom_court,
description,
email,
telephone,
site_web,
type_organisation,
statut,
date_fondation,
numero_enregistrement,
devise,
budget_annuel,
cotisation_obligatoire,
montant_cotisation_annuelle,
objectifs,
activites_principales,
partenaires,
latitude,
longitude,
date_creation,
date_modification,
cree_par,
modifie_par,
version,
actif,
accepte_nouveaux_membres,
est_organisation_racine,
niveau_hierarchique,
nombre_membres,
nombre_administrateurs,
organisation_publique
) VALUES (
gen_random_uuid(),
'Mouvement d''Entraide et de Solidarité de Koumassi et Adjamé',
'MESKA',
'Association communautaire d''entraide et de solidarité basée à Abidjan',
'contact@meska.org',
'+225 07 00 00 00 02',
'https://meska.org',
'ASSOCIATION',
'ACTIVE',
'2018-06-20',
'ASSO-CI-2018-045',
'XOF',
25000000,
true,
25000,
'Promouvoir la solidarité et l''entraide entre les membres des communes de Koumassi et Adjamé',
'Aide sociale, événements communautaires, formations, projets collectifs',
'Mairie de Koumassi, Mairie d''Adjamé',
5.2931,
-3.9468,
CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP,
'superadmin@unionflow.test',
'superadmin@unionflow.test',
0,
true,
true,
true,
0,
0,
0,
true
);
-- Migration V3.6 - Création des organisations de test MUKEFI et MESKA
-- UnionFlow - Configuration initiale pour tests
-- ⚠ Correction : INSERT dans "organisations" (pluriel, table JPA gérée par Hibernate,
-- définie en V1.2), et non "organisation" (singulier, ancienne table isolée).
-- ============================================================================
-- 1. ORGANISATION MUKEFI (Mutuelle d'épargne et de crédit)
-- ============================================================================
DELETE FROM organisations WHERE nom_court = 'MUKEFI';
INSERT INTO organisations (
id,
nom,
nom_court,
description,
email,
telephone,
site_web,
type_organisation,
statut,
date_fondation,
numero_enregistrement,
devise,
budget_annuel,
cotisation_obligatoire,
montant_cotisation_annuelle,
objectifs,
activites_principales,
partenaires,
latitude,
longitude,
date_creation,
date_modification,
cree_par,
modifie_par,
version,
actif,
accepte_nouveaux_membres,
est_organisation_racine,
niveau_hierarchique,
nombre_membres,
nombre_administrateurs,
organisation_publique
) VALUES (
gen_random_uuid(),
'Mutuelle d''Épargne et de Crédit des Fonctionnaires et Indépendants',
'MUKEFI',
'Mutuelle d''épargne et de crédit dédiée aux fonctionnaires et travailleurs indépendants de Côte d''Ivoire',
'contact@mukefi.org',
'+225 07 00 00 00 01',
'https://mukefi.org',
'ASSOCIATION',
'ACTIVE',
'2020-01-15',
'MUT-CI-2020-001',
'XOF',
500000000,
true,
50000,
'Favoriser l''épargne et l''accès au crédit pour les membres',
'Épargne, crédit, micro-crédit, formation financière',
'Banque Centrale des États de l''Afrique de l''Ouest (BCEAO)',
5.3364,
-4.0267,
CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP,
'superadmin@unionflow.test',
'superadmin@unionflow.test',
0,
true,
true,
true,
0,
0,
0,
true
);
-- ============================================================================
-- 2. ORGANISATION MESKA (Association)
-- ============================================================================
DELETE FROM organisations WHERE nom_court = 'MESKA';
INSERT INTO organisations (
id,
nom,
nom_court,
description,
email,
telephone,
site_web,
type_organisation,
statut,
date_fondation,
numero_enregistrement,
devise,
budget_annuel,
cotisation_obligatoire,
montant_cotisation_annuelle,
objectifs,
activites_principales,
partenaires,
latitude,
longitude,
date_creation,
date_modification,
cree_par,
modifie_par,
version,
actif,
accepte_nouveaux_membres,
est_organisation_racine,
niveau_hierarchique,
nombre_membres,
nombre_administrateurs,
organisation_publique
) VALUES (
gen_random_uuid(),
'Mouvement d''Entraide et de Solidarité de Koumassi et Adjamé',
'MESKA',
'Association communautaire d''entraide et de solidarité basée à Abidjan',
'contact@meska.org',
'+225 07 00 00 00 02',
'https://meska.org',
'ASSOCIATION',
'ACTIVE',
'2018-06-20',
'ASSO-CI-2018-045',
'XOF',
25000000,
true,
25000,
'Promouvoir la solidarité et l''entraide entre les membres des communes de Koumassi et Adjamé',
'Aide sociale, événements communautaires, formations, projets collectifs',
'Mairie de Koumassi, Mairie d''Adjamé',
5.2931,
-3.9468,
CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP,
'superadmin@unionflow.test',
'superadmin@unionflow.test',
0,
true,
true,
true,
0,
0,
0,
true
);

View File

@@ -1,237 +1,237 @@
-- ============================================================================
-- V3.7 — Données de test : Membres et Cotisations
-- Tables cibles :
-- utilisateurs -> entité JPA Membre
-- organisations -> entité JPA Organisation (V1.2)
-- membres_organisations -> jointure membre <> organisation
-- cotisations -> entité JPA Cotisation
-- ============================================================================
-- ─────────────────────────────────────────────────────────────────────────────
-- 0. Nettoyage (idempotent)
-- ─────────────────────────────────────────────────────────────────────────────
DELETE FROM cotisations
WHERE membre_id IN (
SELECT id FROM utilisateurs
WHERE email IN (
'membre.mukefi@unionflow.test',
'admin.mukefi@unionflow.test',
'membre.meska@unionflow.test'
)
);
DELETE FROM membres_organisations
WHERE utilisateur_id IN (
SELECT id FROM utilisateurs
WHERE email IN (
'membre.mukefi@unionflow.test',
'admin.mukefi@unionflow.test',
'membre.meska@unionflow.test'
)
);
DELETE FROM utilisateurs
WHERE email IN (
'membre.mukefi@unionflow.test',
'admin.mukefi@unionflow.test',
'membre.meska@unionflow.test'
);
-- ─────────────────────────────────────────────────────────────────────────────
-- 0b. S'assurer que MUKEFI et MESKA existent dans "organisations" (table JPA).
-- Si V3.6 les a déjà insérées, ON CONFLICT (email) DO NOTHING évite le doublon.
-- ─────────────────────────────────────────────────────────────────────────────
INSERT INTO organisations (
id, nom, nom_court, type_organisation, statut, email, telephone,
site_web, date_fondation, numero_enregistrement, devise,
budget_annuel, cotisation_obligatoire, montant_cotisation_annuelle,
objectifs, activites_principales, partenaires, latitude, longitude,
date_creation, date_modification, cree_par, modifie_par, version, actif,
accepte_nouveaux_membres, est_organisation_racine, niveau_hierarchique,
nombre_membres, nombre_administrateurs, organisation_publique
) VALUES (
gen_random_uuid(),
'Mutuelle d''Épargne et de Crédit des Fonctionnaires et Indépendants',
'MUKEFI', 'ASSOCIATION', 'ACTIVE',
'contact@mukefi.org', '+225 07 00 00 00 01', 'https://mukefi.org',
'2020-01-15', 'MUT-CI-2020-001', 'XOF',
500000000, true, 50000,
'Favoriser l''épargne et l''accès au crédit pour les membres',
'Épargne, crédit, micro-crédit, formation financière',
'BCEAO', 5.3364, -4.0267,
CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'system', 'system', 0, true,
true, true, 0, 0, 0, true
) ON CONFLICT (email) DO NOTHING;
INSERT INTO organisations (
id, nom, nom_court, type_organisation, statut, email, telephone,
site_web, date_fondation, numero_enregistrement, devise,
budget_annuel, cotisation_obligatoire, montant_cotisation_annuelle,
objectifs, activites_principales, partenaires, latitude, longitude,
date_creation, date_modification, cree_par, modifie_par, version, actif,
accepte_nouveaux_membres, est_organisation_racine, niveau_hierarchique,
nombre_membres, nombre_administrateurs, organisation_publique
) VALUES (
gen_random_uuid(),
'Mouvement d''Entraide et de Solidarité de Koumassi et Adjamé',
'MESKA', 'ASSOCIATION', 'ACTIVE',
'contact@meska.org', '+225 07 00 00 00 02', 'https://meska.org',
'2018-06-20', 'ASSO-CI-2018-045', 'XOF',
25000000, true, 25000,
'Promouvoir la solidarité et l''entraide entre les membres des communes de Koumassi et Adjamé',
'Aide sociale, événements communautaires, formations, projets collectifs',
'Mairie de Koumassi, Mairie d''Adjamé', 5.2931, -3.9468,
CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'system', 'system', 0, true,
true, true, 0, 0, 0, true
) ON CONFLICT (email) DO NOTHING;
-- ─────────────────────────────────────────────────────────────────────────────
-- 1. MEMBRE : membre.mukefi@unionflow.test (MUKEFI)
-- ─────────────────────────────────────────────────────────────────────────────
INSERT INTO utilisateurs (
id, numero_membre, prenom, nom, email, telephone, date_naissance,
nationalite, profession, statut_compte,
date_creation, date_modification, cree_par, modifie_par, version, actif
) VALUES (
gen_random_uuid(), 'MBR-MUKEFI-001', 'Membre', 'MUKEFI',
'membre.mukefi@unionflow.test', '+22507000101', '1985-06-15',
'Ivoirien', 'Fonctionnaire', 'ACTIF',
CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'system', 'system', 0, true
);
-- ─────────────────────────────────────────────────────────────────────────────
-- 2. MEMBRE : admin.mukefi@unionflow.test (admin MUKEFI)
-- ─────────────────────────────────────────────────────────────────────────────
INSERT INTO utilisateurs (
id, numero_membre, prenom, nom, email, telephone, date_naissance,
nationalite, profession, statut_compte,
date_creation, date_modification, cree_par, modifie_par, version, actif
) VALUES (
gen_random_uuid(), 'MBR-MUKEFI-ADMIN', 'Admin', 'MUKEFI',
'admin.mukefi@unionflow.test', '+22507000102', '1978-04-22',
'Ivoirien', 'Administrateur', 'ACTIF',
CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'system', 'system', 0, true
);
-- ─────────────────────────────────────────────────────────────────────────────
-- 3. MEMBRE : membre.meska@unionflow.test (MESKA)
-- ─────────────────────────────────────────────────────────────────────────────
INSERT INTO utilisateurs (
id, numero_membre, prenom, nom, email, telephone, date_naissance,
nationalite, profession, statut_compte,
date_creation, date_modification, cree_par, modifie_par, version, actif
) VALUES (
gen_random_uuid(), 'MBR-MESKA-001', 'Membre', 'MESKA',
'membre.meska@unionflow.test', '+22507000201', '1990-11-30',
'Ivoirienne', 'Commercante', 'ACTIF',
CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'system', 'system', 0, true
);
-- ─────────────────────────────────────────────────────────────────────────────
-- 4. RATTACHEMENTS membres_organisations
-- ─────────────────────────────────────────────────────────────────────────────
INSERT INTO membres_organisations (
id, utilisateur_id, organisation_id, statut_membre, date_adhesion,
date_creation, date_modification, cree_par, modifie_par, version, actif
) VALUES (
gen_random_uuid(),
(SELECT id FROM utilisateurs WHERE email = 'membre.mukefi@unionflow.test'),
(SELECT id FROM organisations WHERE nom_court = 'MUKEFI' LIMIT 1),
'ACTIF', '2020-03-01',
CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'system', 'system', 0, true
);
INSERT INTO membres_organisations (
id, utilisateur_id, organisation_id, statut_membre, date_adhesion,
date_creation, date_modification, cree_par, modifie_par, version, actif
) VALUES (
gen_random_uuid(),
(SELECT id FROM utilisateurs WHERE email = 'admin.mukefi@unionflow.test'),
(SELECT id FROM organisations WHERE nom_court = 'MUKEFI' LIMIT 1),
'ACTIF', '2020-01-15',
CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'system', 'system', 0, true
);
INSERT INTO membres_organisations (
id, utilisateur_id, organisation_id, statut_membre, date_adhesion,
date_creation, date_modification, cree_par, modifie_par, version, actif
) VALUES (
gen_random_uuid(),
(SELECT id FROM utilisateurs WHERE email = 'membre.meska@unionflow.test'),
(SELECT id FROM organisations WHERE nom_court = 'MESKA' LIMIT 1),
'ACTIF', '2018-09-01',
CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'system', 'system', 0, true
);
-- ─────────────────────────────────────────────────────────────────────────────
-- 5. COTISATIONS pour membre.mukefi@unionflow.test
-- ─────────────────────────────────────────────────────────────────────────────
-- 2023 PAYÉE
INSERT INTO cotisations (
id, numero_reference, membre_id, organisation_id,
type_cotisation, libelle, montant_du, montant_paye, code_devise,
statut, date_echeance, date_paiement, annee, periode,
date_creation, date_modification, cree_par, modifie_par, version, actif, nombre_rappels, recurrente
) VALUES (
gen_random_uuid(), 'COT-MUKEFI-2023-001',
(SELECT id FROM utilisateurs WHERE email = 'membre.mukefi@unionflow.test'),
(SELECT id FROM organisations WHERE nom_court = 'MUKEFI' LIMIT 1),
'ANNUELLE', 'Cotisation annuelle 2023', 50000, 50000, 'XOF',
'PAYEE', '2023-12-31', '2023-03-15 10:00:00', 2023, '2023',
CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'system', 'system', 0, true, 0, true
);
-- 2024 PAYÉE
INSERT INTO cotisations (
id, numero_reference, membre_id, organisation_id,
type_cotisation, libelle, montant_du, montant_paye, code_devise,
statut, date_echeance, date_paiement, annee, periode,
date_creation, date_modification, cree_par, modifie_par, version, actif, nombre_rappels, recurrente
) VALUES (
gen_random_uuid(), 'COT-MUKEFI-2024-001',
(SELECT id FROM utilisateurs WHERE email = 'membre.mukefi@unionflow.test'),
(SELECT id FROM organisations WHERE nom_court = 'MUKEFI' LIMIT 1),
'ANNUELLE', 'Cotisation annuelle 2024', 50000, 50000, 'XOF',
'PAYEE', '2024-12-31', '2024-02-20 09:30:00', 2024, '2024',
CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'system', 'system', 0, true, 0, true
);
-- 2025 EN ATTENTE
INSERT INTO cotisations (
id, numero_reference, membre_id, organisation_id,
type_cotisation, libelle, montant_du, montant_paye, code_devise,
statut, date_echeance, annee, periode,
date_creation, date_modification, cree_par, modifie_par, version, actif, nombre_rappels, recurrente
) VALUES (
gen_random_uuid(), 'COT-MUKEFI-2025-001',
(SELECT id FROM utilisateurs WHERE email = 'membre.mukefi@unionflow.test'),
(SELECT id FROM organisations WHERE nom_court = 'MUKEFI' LIMIT 1),
'ANNUELLE', 'Cotisation annuelle 2025', 50000, 0, 'XOF',
'EN_ATTENTE', '2025-12-31', 2025, '2025',
CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'system', 'system', 0, true, 0, true
);
-- ─────────────────────────────────────────────────────────────────────────────
-- 6. COTISATION pour membre.meska@unionflow.test
-- ─────────────────────────────────────────────────────────────────────────────
INSERT INTO cotisations (
id, numero_reference, membre_id, organisation_id,
type_cotisation, libelle, montant_du, montant_paye, code_devise,
statut, date_echeance, date_paiement, annee, periode,
date_creation, date_modification, cree_par, modifie_par, version, actif, nombre_rappels, recurrente
) VALUES (
gen_random_uuid(), 'COT-MESKA-2024-001',
(SELECT id FROM utilisateurs WHERE email = 'membre.meska@unionflow.test'),
(SELECT id FROM organisations WHERE nom_court = 'MESKA' LIMIT 1),
'ANNUELLE', 'Cotisation annuelle 2024', 25000, 25000, 'XOF',
'PAYEE', '2024-12-31', '2024-01-10 14:00:00', 2024, '2024',
CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'system', 'system', 0, true, 0, true
);
-- ============================================================================
-- V3.7 — Données de test : Membres et Cotisations
-- Tables cibles :
-- utilisateurs -> entité JPA Membre
-- organisations -> entité JPA Organisation (V1.2)
-- membres_organisations -> jointure membre <> organisation
-- cotisations -> entité JPA Cotisation
-- ============================================================================
-- ─────────────────────────────────────────────────────────────────────────────
-- 0. Nettoyage (idempotent)
-- ─────────────────────────────────────────────────────────────────────────────
DELETE FROM cotisations
WHERE membre_id IN (
SELECT id FROM utilisateurs
WHERE email IN (
'membre.mukefi@unionflow.test',
'admin.mukefi@unionflow.test',
'membre.meska@unionflow.test'
)
);
DELETE FROM membres_organisations
WHERE utilisateur_id IN (
SELECT id FROM utilisateurs
WHERE email IN (
'membre.mukefi@unionflow.test',
'admin.mukefi@unionflow.test',
'membre.meska@unionflow.test'
)
);
DELETE FROM utilisateurs
WHERE email IN (
'membre.mukefi@unionflow.test',
'admin.mukefi@unionflow.test',
'membre.meska@unionflow.test'
);
-- ─────────────────────────────────────────────────────────────────────────────
-- 0b. S'assurer que MUKEFI et MESKA existent dans "organisations" (table JPA).
-- Si V3.6 les a déjà insérées, ON CONFLICT (email) DO NOTHING évite le doublon.
-- ─────────────────────────────────────────────────────────────────────────────
INSERT INTO organisations (
id, nom, nom_court, type_organisation, statut, email, telephone,
site_web, date_fondation, numero_enregistrement, devise,
budget_annuel, cotisation_obligatoire, montant_cotisation_annuelle,
objectifs, activites_principales, partenaires, latitude, longitude,
date_creation, date_modification, cree_par, modifie_par, version, actif,
accepte_nouveaux_membres, est_organisation_racine, niveau_hierarchique,
nombre_membres, nombre_administrateurs, organisation_publique
) VALUES (
gen_random_uuid(),
'Mutuelle d''Épargne et de Crédit des Fonctionnaires et Indépendants',
'MUKEFI', 'ASSOCIATION', 'ACTIVE',
'contact@mukefi.org', '+225 07 00 00 00 01', 'https://mukefi.org',
'2020-01-15', 'MUT-CI-2020-001', 'XOF',
500000000, true, 50000,
'Favoriser l''épargne et l''accès au crédit pour les membres',
'Épargne, crédit, micro-crédit, formation financière',
'BCEAO', 5.3364, -4.0267,
CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'system', 'system', 0, true,
true, true, 0, 0, 0, true
) ON CONFLICT (email) DO NOTHING;
INSERT INTO organisations (
id, nom, nom_court, type_organisation, statut, email, telephone,
site_web, date_fondation, numero_enregistrement, devise,
budget_annuel, cotisation_obligatoire, montant_cotisation_annuelle,
objectifs, activites_principales, partenaires, latitude, longitude,
date_creation, date_modification, cree_par, modifie_par, version, actif,
accepte_nouveaux_membres, est_organisation_racine, niveau_hierarchique,
nombre_membres, nombre_administrateurs, organisation_publique
) VALUES (
gen_random_uuid(),
'Mouvement d''Entraide et de Solidarité de Koumassi et Adjamé',
'MESKA', 'ASSOCIATION', 'ACTIVE',
'contact@meska.org', '+225 07 00 00 00 02', 'https://meska.org',
'2018-06-20', 'ASSO-CI-2018-045', 'XOF',
25000000, true, 25000,
'Promouvoir la solidarité et l''entraide entre les membres des communes de Koumassi et Adjamé',
'Aide sociale, événements communautaires, formations, projets collectifs',
'Mairie de Koumassi, Mairie d''Adjamé', 5.2931, -3.9468,
CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'system', 'system', 0, true,
true, true, 0, 0, 0, true
) ON CONFLICT (email) DO NOTHING;
-- ─────────────────────────────────────────────────────────────────────────────
-- 1. MEMBRE : membre.mukefi@unionflow.test (MUKEFI)
-- ─────────────────────────────────────────────────────────────────────────────
INSERT INTO utilisateurs (
id, numero_membre, prenom, nom, email, telephone, date_naissance,
nationalite, profession, statut_compte,
date_creation, date_modification, cree_par, modifie_par, version, actif
) VALUES (
gen_random_uuid(), 'MBR-MUKEFI-001', 'Membre', 'MUKEFI',
'membre.mukefi@unionflow.test', '+22507000101', '1985-06-15',
'Ivoirien', 'Fonctionnaire', 'ACTIF',
CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'system', 'system', 0, true
);
-- ─────────────────────────────────────────────────────────────────────────────
-- 2. MEMBRE : admin.mukefi@unionflow.test (admin MUKEFI)
-- ─────────────────────────────────────────────────────────────────────────────
INSERT INTO utilisateurs (
id, numero_membre, prenom, nom, email, telephone, date_naissance,
nationalite, profession, statut_compte,
date_creation, date_modification, cree_par, modifie_par, version, actif
) VALUES (
gen_random_uuid(), 'MBR-MUKEFI-ADMIN', 'Admin', 'MUKEFI',
'admin.mukefi@unionflow.test', '+22507000102', '1978-04-22',
'Ivoirien', 'Administrateur', 'ACTIF',
CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'system', 'system', 0, true
);
-- ─────────────────────────────────────────────────────────────────────────────
-- 3. MEMBRE : membre.meska@unionflow.test (MESKA)
-- ─────────────────────────────────────────────────────────────────────────────
INSERT INTO utilisateurs (
id, numero_membre, prenom, nom, email, telephone, date_naissance,
nationalite, profession, statut_compte,
date_creation, date_modification, cree_par, modifie_par, version, actif
) VALUES (
gen_random_uuid(), 'MBR-MESKA-001', 'Membre', 'MESKA',
'membre.meska@unionflow.test', '+22507000201', '1990-11-30',
'Ivoirienne', 'Commercante', 'ACTIF',
CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'system', 'system', 0, true
);
-- ─────────────────────────────────────────────────────────────────────────────
-- 4. RATTACHEMENTS membres_organisations
-- ─────────────────────────────────────────────────────────────────────────────
INSERT INTO membres_organisations (
id, utilisateur_id, organisation_id, statut_membre, date_adhesion,
date_creation, date_modification, cree_par, modifie_par, version, actif
) VALUES (
gen_random_uuid(),
(SELECT id FROM utilisateurs WHERE email = 'membre.mukefi@unionflow.test'),
(SELECT id FROM organisations WHERE nom_court = 'MUKEFI' LIMIT 1),
'ACTIF', '2020-03-01',
CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'system', 'system', 0, true
);
INSERT INTO membres_organisations (
id, utilisateur_id, organisation_id, statut_membre, date_adhesion,
date_creation, date_modification, cree_par, modifie_par, version, actif
) VALUES (
gen_random_uuid(),
(SELECT id FROM utilisateurs WHERE email = 'admin.mukefi@unionflow.test'),
(SELECT id FROM organisations WHERE nom_court = 'MUKEFI' LIMIT 1),
'ACTIF', '2020-01-15',
CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'system', 'system', 0, true
);
INSERT INTO membres_organisations (
id, utilisateur_id, organisation_id, statut_membre, date_adhesion,
date_creation, date_modification, cree_par, modifie_par, version, actif
) VALUES (
gen_random_uuid(),
(SELECT id FROM utilisateurs WHERE email = 'membre.meska@unionflow.test'),
(SELECT id FROM organisations WHERE nom_court = 'MESKA' LIMIT 1),
'ACTIF', '2018-09-01',
CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'system', 'system', 0, true
);
-- ─────────────────────────────────────────────────────────────────────────────
-- 5. COTISATIONS pour membre.mukefi@unionflow.test
-- ─────────────────────────────────────────────────────────────────────────────
-- 2023 PAYÉE
INSERT INTO cotisations (
id, numero_reference, membre_id, organisation_id,
type_cotisation, libelle, montant_du, montant_paye, code_devise,
statut, date_echeance, date_paiement, annee, periode,
date_creation, date_modification, cree_par, modifie_par, version, actif, nombre_rappels, recurrente
) VALUES (
gen_random_uuid(), 'COT-MUKEFI-2023-001',
(SELECT id FROM utilisateurs WHERE email = 'membre.mukefi@unionflow.test'),
(SELECT id FROM organisations WHERE nom_court = 'MUKEFI' LIMIT 1),
'ANNUELLE', 'Cotisation annuelle 2023', 50000, 50000, 'XOF',
'PAYEE', '2023-12-31', '2023-03-15 10:00:00', 2023, '2023',
CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'system', 'system', 0, true, 0, true
);
-- 2024 PAYÉE
INSERT INTO cotisations (
id, numero_reference, membre_id, organisation_id,
type_cotisation, libelle, montant_du, montant_paye, code_devise,
statut, date_echeance, date_paiement, annee, periode,
date_creation, date_modification, cree_par, modifie_par, version, actif, nombre_rappels, recurrente
) VALUES (
gen_random_uuid(), 'COT-MUKEFI-2024-001',
(SELECT id FROM utilisateurs WHERE email = 'membre.mukefi@unionflow.test'),
(SELECT id FROM organisations WHERE nom_court = 'MUKEFI' LIMIT 1),
'ANNUELLE', 'Cotisation annuelle 2024', 50000, 50000, 'XOF',
'PAYEE', '2024-12-31', '2024-02-20 09:30:00', 2024, '2024',
CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'system', 'system', 0, true, 0, true
);
-- 2025 EN ATTENTE
INSERT INTO cotisations (
id, numero_reference, membre_id, organisation_id,
type_cotisation, libelle, montant_du, montant_paye, code_devise,
statut, date_echeance, annee, periode,
date_creation, date_modification, cree_par, modifie_par, version, actif, nombre_rappels, recurrente
) VALUES (
gen_random_uuid(), 'COT-MUKEFI-2025-001',
(SELECT id FROM utilisateurs WHERE email = 'membre.mukefi@unionflow.test'),
(SELECT id FROM organisations WHERE nom_court = 'MUKEFI' LIMIT 1),
'ANNUELLE', 'Cotisation annuelle 2025', 50000, 0, 'XOF',
'EN_ATTENTE', '2025-12-31', 2025, '2025',
CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'system', 'system', 0, true, 0, true
);
-- ─────────────────────────────────────────────────────────────────────────────
-- 6. COTISATION pour membre.meska@unionflow.test
-- ─────────────────────────────────────────────────────────────────────────────
INSERT INTO cotisations (
id, numero_reference, membre_id, organisation_id,
type_cotisation, libelle, montant_du, montant_paye, code_devise,
statut, date_echeance, date_paiement, annee, periode,
date_creation, date_modification, cree_par, modifie_par, version, actif, nombre_rappels, recurrente
) VALUES (
gen_random_uuid(), 'COT-MESKA-2024-001',
(SELECT id FROM utilisateurs WHERE email = 'membre.meska@unionflow.test'),
(SELECT id FROM organisations WHERE nom_court = 'MESKA' LIMIT 1),
'ANNUELLE', 'Cotisation annuelle 2024', 25000, 25000, 'XOF',
'PAYEE', '2024-12-31', '2024-01-10 14:00:00', 2024, '2024',
CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'system', 'system', 0, true, 0, true
);

View File

@@ -1,50 +1,50 @@
-- ============================================================================
-- V3.8 — Données de test : un compte épargne pour le membre MUKEFI
-- Permet d'afficher au moins un compte sur l'écran "Comptes épargne".
-- ============================================================================
-- Un compte épargne pour membre.mukefi@unionflow.test (organisation MUKEFI)
INSERT INTO comptes_epargne (
id,
actif,
date_creation,
date_modification,
cree_par,
modifie_par,
version,
date_ouverture,
date_derniere_transaction,
description,
numero_compte,
solde_actuel,
solde_bloque,
statut,
type_compte,
membre_id,
organisation_id
)
SELECT
gen_random_uuid(),
true,
CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP,
'system',
'system',
0,
CURRENT_DATE,
NULL,
'Compte épargne principal test',
'MUK-' || UPPER(SUBSTRING(REPLACE(gen_random_uuid()::text, '-', '') FROM 1 FOR 8)),
0,
0,
'ACTIF',
'EPARGNE_LIBRE',
u.id,
o.id
FROM utilisateurs u,
(SELECT id FROM organisations WHERE nom_court = 'MUKEFI' LIMIT 1) o
WHERE u.email = 'membre.mukefi@unionflow.test'
AND NOT EXISTS (
SELECT 1 FROM comptes_epargne ce
WHERE ce.membre_id = u.id AND ce.actif = true
);
-- ============================================================================
-- V3.8 — Données de test : un compte épargne pour le membre MUKEFI
-- Permet d'afficher au moins un compte sur l'écran "Comptes épargne".
-- ============================================================================
-- Un compte épargne pour membre.mukefi@unionflow.test (organisation MUKEFI)
INSERT INTO comptes_epargne (
id,
actif,
date_creation,
date_modification,
cree_par,
modifie_par,
version,
date_ouverture,
date_derniere_transaction,
description,
numero_compte,
solde_actuel,
solde_bloque,
statut,
type_compte,
membre_id,
organisation_id
)
SELECT
gen_random_uuid(),
true,
CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP,
'system',
'system',
0,
CURRENT_DATE,
NULL,
'Compte épargne principal test',
'MUK-' || UPPER(SUBSTRING(REPLACE(gen_random_uuid()::text, '-', '') FROM 1 FOR 8)),
0,
0,
'ACTIF',
'EPARGNE_LIBRE',
u.id,
o.id
FROM utilisateurs u,
(SELECT id FROM organisations WHERE nom_court = 'MUKEFI' LIMIT 1) o
WHERE u.email = 'membre.mukefi@unionflow.test'
AND NOT EXISTS (
SELECT 1 FROM comptes_epargne ce
WHERE ce.membre_id = u.id AND ce.actif = true
);

View File

@@ -1,57 +1,57 @@
# Stratégie des migrations Flyway
## Vue densemble
| Version | Fichier | Rôle |
|--------|---------|------|
| **V1** | `V1__UnionFlow_Complete_Schema.sql` | Schéma historique consolidé (anciennes V1.2 à V3.7) + données de référence et de test |
| **V2** | `V2__Entity_Schema_Alignment.sql` | Alignement du schéma avec les entités JPA (colonnes/tables manquantes, types, index). Idempotent. |
Les 25 fichiers dorigine sont conservés dans **`db/legacy-migrations/`** (référence uniquement, Flyway ne les exécute pas).
## Ordre dexécution
1. **V1** : crée les tables, contraintes et données de base.
2. **V2** : ajoute ou modifie colonnes/tables pour correspondre aux entités JPA (ADD COLUMN IF NOT EXISTS, CREATE TABLE IF NOT EXISTS). Peut être exécuté plusieurs fois sans effet de bord.
## Nouvelle base de données
Avec une base vide, Flyway exécute **V1** puis **V2**. Aucune autre action.
## Base déjà migrée avec les anciennes versions (V1.2 à V3.7)
Si la base a déjà été migrée avec les 25 anciens scripts, il faut **une seule fois** mettre à jour lhistorique Flyway pour refléter la consolidation :
1. Sauvegarder la base.
2. Se connecter en base (psql, DBeaver, etc.) et exécuter :
```sql
-- Marquer la consolidation comme appliquée (une seule fois)
DELETE FROM flyway_schema_history WHERE version IN (
'1.2','1.3','1.4','1.5','1.6','1.7','2.0','2.1','2.2','2.3','2.4','2.5','2.6','2.7','2.8','2.9','2.10',
'3.0','3.1','3.2','3.3','3.4','3.5','3.6','3.7'
);
INSERT INTO flyway_schema_history (installed_rank, version, description, type, script, checksum, installed_by, execution_time, success)
VALUES (
(SELECT COALESCE(MAX(installed_rank),0) + 1 FROM flyway_schema_history f2),
'1', 'UnionFlow Complete Schema', 'SQL', 'V1__UnionFlow_Complete_Schema.sql', NULL, current_user, 0, true
);
```
Après cela, Flyway considère que la version **1** est appliquée et nexécutera plus les anciens scripts.
## Évolutions futures
- **Changement de schéma métier** (nouvelles tables, nouvelles colonnes métier) : ajouter une migration **V3**, **V4**, etc. (une par release ou lot cohérent).
- **Alignement entités JPA** : si de nouvelles entités ou champs sont ajoutés au code, compléter **`V2__Entity_Schema_Alignment.sql`** avec des `ADD COLUMN IF NOT EXISTS` / `CREATE TABLE IF NOT EXISTS` pour garder un seul fichier dalignement.
## Régénérer le script consolidé
Si les fichiers dans `legacy/` sont modifiés et que vous voulez régénérer `V1__UnionFlow_Complete_Schema.sql` :
```powershell
cd unionflow-server-impl-quarkus
./scripts/merge-migrations.ps1
```
(Remettre temporairement les 25 fichiers dans `db/migration/` avant de lancer le script, puis les redéplacer dans `legacy/`.)
# Stratégie des migrations Flyway
## Vue densemble
| Version | Fichier | Rôle |
|--------|---------|------|
| **V1** | `V1__UnionFlow_Complete_Schema.sql` | Schéma historique consolidé (anciennes V1.2 à V3.7) + données de référence et de test |
| **V2** | `V2__Entity_Schema_Alignment.sql` | Alignement du schéma avec les entités JPA (colonnes/tables manquantes, types, index). Idempotent. |
Les 25 fichiers dorigine sont conservés dans **`db/legacy-migrations/`** (référence uniquement, Flyway ne les exécute pas).
## Ordre dexécution
1. **V1** : crée les tables, contraintes et données de base.
2. **V2** : ajoute ou modifie colonnes/tables pour correspondre aux entités JPA (ADD COLUMN IF NOT EXISTS, CREATE TABLE IF NOT EXISTS). Peut être exécuté plusieurs fois sans effet de bord.
## Nouvelle base de données
Avec une base vide, Flyway exécute **V1** puis **V2**. Aucune autre action.
## Base déjà migrée avec les anciennes versions (V1.2 à V3.7)
Si la base a déjà été migrée avec les 25 anciens scripts, il faut **une seule fois** mettre à jour lhistorique Flyway pour refléter la consolidation :
1. Sauvegarder la base.
2. Se connecter en base (psql, DBeaver, etc.) et exécuter :
```sql
-- Marquer la consolidation comme appliquée (une seule fois)
DELETE FROM flyway_schema_history WHERE version IN (
'1.2','1.3','1.4','1.5','1.6','1.7','2.0','2.1','2.2','2.3','2.4','2.5','2.6','2.7','2.8','2.9','2.10',
'3.0','3.1','3.2','3.3','3.4','3.5','3.6','3.7'
);
INSERT INTO flyway_schema_history (installed_rank, version, description, type, script, checksum, installed_by, execution_time, success)
VALUES (
(SELECT COALESCE(MAX(installed_rank),0) + 1 FROM flyway_schema_history f2),
'1', 'UnionFlow Complete Schema', 'SQL', 'V1__UnionFlow_Complete_Schema.sql', NULL, current_user, 0, true
);
```
Après cela, Flyway considère que la version **1** est appliquée et nexécutera plus les anciens scripts.
## Évolutions futures
- **Changement de schéma métier** (nouvelles tables, nouvelles colonnes métier) : ajouter une migration **V3**, **V4**, etc. (une par release ou lot cohérent).
- **Alignement entités JPA** : si de nouvelles entités ou champs sont ajoutés au code, compléter **`V2__Entity_Schema_Alignment.sql`** avec des `ADD COLUMN IF NOT EXISTS` / `CREATE TABLE IF NOT EXISTS` pour garder un seul fichier dalignement.
## Régénérer le script consolidé
Si les fichiers dans `legacy/` sont modifiés et que vous voulez régénérer `V1__UnionFlow_Complete_Schema.sql` :
```powershell
cd unionflow-server-impl-quarkus
./scripts/merge-migrations.ps1
```
(Remettre temporairement les 25 fichiers dans `db/migration/` avant de lancer le script, puis les redéplacer dans `legacy/`.)

View File

@@ -1,74 +1,74 @@
# Migration V6 - Notes techniques
## Colonnes de timestamp en double
La migration V6 contient volontairement des colonnes de timestamp en double pour certaines tables. Ceci n'est PAS une erreur mais un choix de design.
### transaction_approvals
**Colonnes:**
- `created_at` : Timestamp métier utilisé pour la logique d'approbation (calcul d'expiration)
- `date_creation` : Timestamp d'audit BaseEntity (créé automatiquement par JPA)
**Raison:**
L'entité TransactionApproval a besoin d'un timestamp métier (`createdAt`) pour calculer l'expiration (`expiresAt = createdAt + 7 jours`). Ce timestamp ne doit pas être confondu avec `dateCreation` qui est purement pour l'audit.
**Code Java correspondant:**
```java
@Entity
public class TransactionApproval extends BaseEntity {
@Column(name = "created_at", nullable = false)
private LocalDateTime createdAt;
// Logique métier utilisant createdAt
@PrePersist
protected void onCreate() {
super.onCreate();
if (createdAt == null) {
createdAt = LocalDateTime.now();
}
if (expiresAt == null && createdAt != null) {
expiresAt = createdAt.plusDays(7);
}
}
}
```
### budgets
**Colonnes:**
- `created_at_budget` : Timestamp métier de création du budget (distinct de la modification de l'enregistrement)
- `date_creation` : Timestamp d'audit BaseEntity
**Raison:**
Un budget peut être créé à une date, puis modifié plusieurs fois. `created_at_budget` représente la date de création du budget lui-même (logique métier), tandis que `date_creation` représente la première insertion en base (audit).
**Code Java correspondant:**
```java
@Entity
public class Budget extends BaseEntity {
@Column(name = "created_by_id", nullable = false)
private UUID createdById;
@Column(name = "created_at_budget", nullable = false)
private LocalDateTime createdAtBudget;
}
```
## Recommandation future
Pour éviter cette confusion, on pourrait :
1. Renommer `created_at` en `requested_at` (transaction_approvals)
2. Renommer `created_at_budget` en `budget_creation_date` (budgets)
Mais cela nécessiterait une modification des entités Java et une nouvelle migration.
## Colonnes BaseEntity standard
Toutes les tables incluent les colonnes BaseEntity :
- `date_creation` : Date de création de l'enregistrement (auto)
- `date_modification` : Date de dernière modification (auto)
- `cree_par` : Utilisateur créateur
- `modifie_par` : Dernier utilisateur modificateur
- `version` : Numéro de version (optimistic locking)
- `actif` : Flag soft delete
# Migration V6 - Notes techniques
## Colonnes de timestamp en double
La migration V6 contient volontairement des colonnes de timestamp en double pour certaines tables. Ceci n'est PAS une erreur mais un choix de design.
### transaction_approvals
**Colonnes:**
- `created_at` : Timestamp métier utilisé pour la logique d'approbation (calcul d'expiration)
- `date_creation` : Timestamp d'audit BaseEntity (créé automatiquement par JPA)
**Raison:**
L'entité TransactionApproval a besoin d'un timestamp métier (`createdAt`) pour calculer l'expiration (`expiresAt = createdAt + 7 jours`). Ce timestamp ne doit pas être confondu avec `dateCreation` qui est purement pour l'audit.
**Code Java correspondant:**
```java
@Entity
public class TransactionApproval extends BaseEntity {
@Column(name = "created_at", nullable = false)
private LocalDateTime createdAt;
// Logique métier utilisant createdAt
@PrePersist
protected void onCreate() {
super.onCreate();
if (createdAt == null) {
createdAt = LocalDateTime.now();
}
if (expiresAt == null && createdAt != null) {
expiresAt = createdAt.plusDays(7);
}
}
}
```
### budgets
**Colonnes:**
- `created_at_budget` : Timestamp métier de création du budget (distinct de la modification de l'enregistrement)
- `date_creation` : Timestamp d'audit BaseEntity
**Raison:**
Un budget peut être créé à une date, puis modifié plusieurs fois. `created_at_budget` représente la date de création du budget lui-même (logique métier), tandis que `date_creation` représente la première insertion en base (audit).
**Code Java correspondant:**
```java
@Entity
public class Budget extends BaseEntity {
@Column(name = "created_by_id", nullable = false)
private UUID createdById;
@Column(name = "created_at_budget", nullable = false)
private LocalDateTime createdAtBudget;
}
```
## Recommandation future
Pour éviter cette confusion, on pourrait :
1. Renommer `created_at` en `requested_at` (transaction_approvals)
2. Renommer `created_at_budget` en `budget_creation_date` (budgets)
Mais cela nécessiterait une modification des entités Java et une nouvelle migration.
## Colonnes BaseEntity standard
Toutes les tables incluent les colonnes BaseEntity :
- `date_creation` : Date de création de l'enregistrement (auto)
- `date_modification` : Date de dernière modification (auto)
- `cree_par` : Utilisateur créateur
- `modifie_par` : Dernier utilisateur modificateur
- `version` : Numéro de version (optimistic locking)
- `actif` : Flag soft delete

View File

@@ -1,10 +1,10 @@
-- Script d'insertion de données initiales pour UnionFlow
-- Ce fichier est exécuté automatiquement par Hibernate au démarrage
-- Utilisé uniquement en mode développement (quarkus.hibernate-orm.database.generation=drop-and-create)
--
-- IMPORTANT: Ce fichier ne doit PAS contenir de données fictives pour la production.
-- Les données doivent être insérées manuellement via l'interface d'administration
-- ou via des scripts de migration Flyway si nécessaire.
--
-- Ce fichier est laissé vide intentionnellement pour éviter l'insertion automatique
-- de données fictives lors du démarrage du serveur.
-- Script d'insertion de données initiales pour UnionFlow
-- Ce fichier est exécuté automatiquement par Hibernate au démarrage
-- Utilisé uniquement en mode développement (quarkus.hibernate-orm.database.generation=drop-and-create)
--
-- IMPORTANT: Ce fichier ne doit PAS contenir de données fictives pour la production.
-- Les données doivent être insérées manuellement via l'interface d'administration
-- ou via des scripts de migration Flyway si nécessaire.
--
-- Ce fichier est laissé vide intentionnellement pour éviter l'insertion automatique
-- de données fictives lors du démarrage du serveur.

View File

@@ -1,376 +1,376 @@
{
"realm": "unionflow",
"displayName": "UnionFlow",
"displayNameHtml": "<div class=\"kc-logo-text\"><span>UnionFlow</span></div>",
"enabled": true,
"sslRequired": "external",
"registrationAllowed": true,
"registrationEmailAsUsername": true,
"rememberMe": true,
"verifyEmail": false,
"loginWithEmailAllowed": true,
"duplicateEmailsAllowed": false,
"resetPasswordAllowed": true,
"editUsernameAllowed": false,
"bruteForceProtected": true,
"permanentLockout": false,
"maxFailureWaitSeconds": 900,
"minimumQuickLoginWaitSeconds": 60,
"waitIncrementSeconds": 60,
"quickLoginCheckMilliSeconds": 1000,
"maxDeltaTimeSeconds": 43200,
"failureFactor": 30,
"defaultRoles": [
"offline_access",
"uma_authorization",
"default-roles-unionflow"
],
"requiredCredentials": [
"password"
],
"otpPolicyType": "totp",
"otpPolicyAlgorithm": "HmacSHA1",
"otpPolicyInitialCounter": 0,
"otpPolicyDigits": 6,
"otpPolicyLookAheadWindow": 1,
"otpPolicyPeriod": 30,
"supportedLocales": [
"fr",
"en"
],
"defaultLocale": "fr",
"internationalizationEnabled": true,
"clients": [
{
"clientId": "unionflow-server",
"name": "UnionFlow Server API",
"description": "Client pour l'API serveur UnionFlow",
"enabled": true,
"clientAuthenticatorType": "client-secret",
"secret": "unionflow-secret-2025",
"redirectUris": [
"http://localhost:8080/*"
],
"webOrigins": [
"http://localhost:8080",
"http://localhost:3000"
],
"protocol": "openid-connect",
"attributes": {
"saml.assertion.signature": "false",
"saml.force.post.binding": "false",
"saml.multivalued.roles": "false",
"saml.encrypt": "false",
"saml.server.signature": "false",
"saml.server.signature.keyinfo.ext": "false",
"exclude.session.state.from.auth.response": "false",
"saml_force_name_id_format": "false",
"saml.client.signature": "false",
"tls.client.certificate.bound.access.tokens": "false",
"saml.authnstatement": "false",
"display.on.consent.screen": "false",
"saml.onetimeuse.condition": "false"
},
"authenticationFlowBindingOverrides": {},
"fullScopeAllowed": true,
"nodeReRegistrationTimeout": -1,
"protocolMappers": [
{
"name": "email",
"protocol": "openid-connect",
"protocolMapper": "oidc-usermodel-property-mapper",
"consentRequired": false,
"config": {
"userinfo.token.claim": "true",
"user.attribute": "email",
"id.token.claim": "true",
"access.token.claim": "true",
"claim.name": "email",
"jsonType.label": "String"
}
},
{
"name": "given_name",
"protocol": "openid-connect",
"protocolMapper": "oidc-usermodel-property-mapper",
"consentRequired": false,
"config": {
"userinfo.token.claim": "true",
"user.attribute": "firstName",
"id.token.claim": "true",
"access.token.claim": "true",
"claim.name": "given_name",
"jsonType.label": "String"
}
},
{
"name": "family_name",
"protocol": "openid-connect",
"protocolMapper": "oidc-usermodel-property-mapper",
"consentRequired": false,
"config": {
"userinfo.token.claim": "true",
"user.attribute": "lastName",
"id.token.claim": "true",
"access.token.claim": "true",
"claim.name": "family_name",
"jsonType.label": "String"
}
},
{
"name": "roles",
"protocol": "openid-connect",
"protocolMapper": "oidc-usermodel-realm-role-mapper",
"consentRequired": false,
"config": {
"userinfo.token.claim": "true",
"id.token.claim": "true",
"access.token.claim": "true",
"claim.name": "roles",
"jsonType.label": "String",
"multivalued": "true"
}
}
],
"defaultClientScopes": [
"web-origins",
"role_list",
"profile",
"roles",
"email"
],
"optionalClientScopes": [
"address",
"phone",
"offline_access",
"microprofile-jwt"
],
"serviceAccountsEnabled": true,
"directAccessGrantsEnabled": true
},
{
"clientId": "unionflow-mobile",
"name": "UnionFlow Mobile App",
"description": "Client pour l'application mobile UnionFlow",
"enabled": true,
"publicClient": true,
"redirectUris": [
"unionflow://callback",
"http://localhost:3000/callback"
],
"webOrigins": [
"*"
],
"protocol": "openid-connect",
"attributes": {
"pkce.code.challenge.method": "S256"
},
"fullScopeAllowed": true,
"defaultClientScopes": [
"web-origins",
"role_list",
"profile",
"roles",
"email"
],
"optionalClientScopes": [
"address",
"phone",
"offline_access",
"microprofile-jwt"
]
}
],
"roles": {
"realm": [
{
"name": "ADMIN",
"description": "Administrateur système avec tous les droits",
"composite": false,
"clientRole": false,
"containerId": "unionflow"
},
{
"name": "PRESIDENT",
"description": "Président de l'union avec droits de gestion complète",
"composite": false,
"clientRole": false,
"containerId": "unionflow"
},
{
"name": "SECRETAIRE",
"description": "Secrétaire avec droits de gestion des membres et événements",
"composite": false,
"clientRole": false,
"containerId": "unionflow"
},
{
"name": "TRESORIER",
"description": "Trésorier avec droits de gestion financière",
"composite": false,
"clientRole": false,
"containerId": "unionflow"
},
{
"name": "GESTIONNAIRE_MEMBRE",
"description": "Gestionnaire des membres avec droits de CRUD sur les membres",
"composite": false,
"clientRole": false,
"containerId": "unionflow"
},
{
"name": "ORGANISATEUR_EVENEMENT",
"description": "Organisateur d'événements avec droits de gestion des événements",
"composite": false,
"clientRole": false,
"containerId": "unionflow"
},
{
"name": "MEMBRE",
"description": "Membre standard avec droits de consultation",
"composite": false,
"clientRole": false,
"containerId": "unionflow"
}
]
},
"users": [
{
"username": "admin",
"enabled": true,
"emailVerified": true,
"firstName": "Administrateur",
"lastName": "Système",
"email": "admin@unionflow.dev",
"credentials": [
{
"type": "password",
"value": "admin123",
"temporary": false
}
],
"realmRoles": [
"ADMIN",
"PRESIDENT"
],
"clientRoles": {}
},
{
"username": "president",
"enabled": true,
"emailVerified": true,
"firstName": "Jean",
"lastName": "Dupont",
"email": "president@unionflow.dev",
"credentials": [
{
"type": "password",
"value": "president123",
"temporary": false
}
],
"realmRoles": [
"PRESIDENT",
"MEMBRE"
],
"clientRoles": {}
},
{
"username": "secretaire",
"enabled": true,
"emailVerified": true,
"firstName": "Marie",
"lastName": "Martin",
"email": "secretaire@unionflow.dev",
"credentials": [
{
"type": "password",
"value": "secretaire123",
"temporary": false
}
],
"realmRoles": [
"SECRETAIRE",
"GESTIONNAIRE_MEMBRE",
"MEMBRE"
],
"clientRoles": {}
},
{
"username": "tresorier",
"enabled": true,
"emailVerified": true,
"firstName": "Pierre",
"lastName": "Durand",
"email": "tresorier@unionflow.dev",
"credentials": [
{
"type": "password",
"value": "tresorier123",
"temporary": false
}
],
"realmRoles": [
"TRESORIER",
"MEMBRE"
],
"clientRoles": {}
},
{
"username": "membre1",
"enabled": true,
"emailVerified": true,
"firstName": "Sophie",
"lastName": "Bernard",
"email": "membre1@unionflow.dev",
"credentials": [
{
"type": "password",
"value": "membre123",
"temporary": false
}
],
"realmRoles": [
"MEMBRE"
],
"clientRoles": {}
}
],
"groups": [
{
"name": "Administration",
"path": "/Administration",
"realmRoles": [
"ADMIN"
],
"subGroups": []
},
{
"name": "Bureau",
"path": "/Bureau",
"realmRoles": [
"PRESIDENT",
"SECRETAIRE",
"TRESORIER"
],
"subGroups": []
},
{
"name": "Gestionnaires",
"path": "/Gestionnaires",
"realmRoles": [
"GESTIONNAIRE_MEMBRE",
"ORGANISATEUR_EVENEMENT"
],
"subGroups": []
},
{
"name": "Membres",
"path": "/Membres",
"realmRoles": [
"MEMBRE"
],
"subGroups": []
}
]
{
"realm": "unionflow",
"displayName": "UnionFlow",
"displayNameHtml": "<div class=\"kc-logo-text\"><span>UnionFlow</span></div>",
"enabled": true,
"sslRequired": "external",
"registrationAllowed": true,
"registrationEmailAsUsername": true,
"rememberMe": true,
"verifyEmail": false,
"loginWithEmailAllowed": true,
"duplicateEmailsAllowed": false,
"resetPasswordAllowed": true,
"editUsernameAllowed": false,
"bruteForceProtected": true,
"permanentLockout": false,
"maxFailureWaitSeconds": 900,
"minimumQuickLoginWaitSeconds": 60,
"waitIncrementSeconds": 60,
"quickLoginCheckMilliSeconds": 1000,
"maxDeltaTimeSeconds": 43200,
"failureFactor": 30,
"defaultRoles": [
"offline_access",
"uma_authorization",
"default-roles-unionflow"
],
"requiredCredentials": [
"password"
],
"otpPolicyType": "totp",
"otpPolicyAlgorithm": "HmacSHA1",
"otpPolicyInitialCounter": 0,
"otpPolicyDigits": 6,
"otpPolicyLookAheadWindow": 1,
"otpPolicyPeriod": 30,
"supportedLocales": [
"fr",
"en"
],
"defaultLocale": "fr",
"internationalizationEnabled": true,
"clients": [
{
"clientId": "unionflow-server",
"name": "UnionFlow Server API",
"description": "Client pour l'API serveur UnionFlow",
"enabled": true,
"clientAuthenticatorType": "client-secret",
"secret": "unionflow-secret-2025",
"redirectUris": [
"http://localhost:8080/*"
],
"webOrigins": [
"http://localhost:8080",
"http://localhost:3000"
],
"protocol": "openid-connect",
"attributes": {
"saml.assertion.signature": "false",
"saml.force.post.binding": "false",
"saml.multivalued.roles": "false",
"saml.encrypt": "false",
"saml.server.signature": "false",
"saml.server.signature.keyinfo.ext": "false",
"exclude.session.state.from.auth.response": "false",
"saml_force_name_id_format": "false",
"saml.client.signature": "false",
"tls.client.certificate.bound.access.tokens": "false",
"saml.authnstatement": "false",
"display.on.consent.screen": "false",
"saml.onetimeuse.condition": "false"
},
"authenticationFlowBindingOverrides": {},
"fullScopeAllowed": true,
"nodeReRegistrationTimeout": -1,
"protocolMappers": [
{
"name": "email",
"protocol": "openid-connect",
"protocolMapper": "oidc-usermodel-property-mapper",
"consentRequired": false,
"config": {
"userinfo.token.claim": "true",
"user.attribute": "email",
"id.token.claim": "true",
"access.token.claim": "true",
"claim.name": "email",
"jsonType.label": "String"
}
},
{
"name": "given_name",
"protocol": "openid-connect",
"protocolMapper": "oidc-usermodel-property-mapper",
"consentRequired": false,
"config": {
"userinfo.token.claim": "true",
"user.attribute": "firstName",
"id.token.claim": "true",
"access.token.claim": "true",
"claim.name": "given_name",
"jsonType.label": "String"
}
},
{
"name": "family_name",
"protocol": "openid-connect",
"protocolMapper": "oidc-usermodel-property-mapper",
"consentRequired": false,
"config": {
"userinfo.token.claim": "true",
"user.attribute": "lastName",
"id.token.claim": "true",
"access.token.claim": "true",
"claim.name": "family_name",
"jsonType.label": "String"
}
},
{
"name": "roles",
"protocol": "openid-connect",
"protocolMapper": "oidc-usermodel-realm-role-mapper",
"consentRequired": false,
"config": {
"userinfo.token.claim": "true",
"id.token.claim": "true",
"access.token.claim": "true",
"claim.name": "roles",
"jsonType.label": "String",
"multivalued": "true"
}
}
],
"defaultClientScopes": [
"web-origins",
"role_list",
"profile",
"roles",
"email"
],
"optionalClientScopes": [
"address",
"phone",
"offline_access",
"microprofile-jwt"
],
"serviceAccountsEnabled": true,
"directAccessGrantsEnabled": true
},
{
"clientId": "unionflow-mobile",
"name": "UnionFlow Mobile App",
"description": "Client pour l'application mobile UnionFlow",
"enabled": true,
"publicClient": true,
"redirectUris": [
"unionflow://callback",
"http://localhost:3000/callback"
],
"webOrigins": [
"*"
],
"protocol": "openid-connect",
"attributes": {
"pkce.code.challenge.method": "S256"
},
"fullScopeAllowed": true,
"defaultClientScopes": [
"web-origins",
"role_list",
"profile",
"roles",
"email"
],
"optionalClientScopes": [
"address",
"phone",
"offline_access",
"microprofile-jwt"
]
}
],
"roles": {
"realm": [
{
"name": "ADMIN",
"description": "Administrateur système avec tous les droits",
"composite": false,
"clientRole": false,
"containerId": "unionflow"
},
{
"name": "PRESIDENT",
"description": "Président de l'union avec droits de gestion complète",
"composite": false,
"clientRole": false,
"containerId": "unionflow"
},
{
"name": "SECRETAIRE",
"description": "Secrétaire avec droits de gestion des membres et événements",
"composite": false,
"clientRole": false,
"containerId": "unionflow"
},
{
"name": "TRESORIER",
"description": "Trésorier avec droits de gestion financière",
"composite": false,
"clientRole": false,
"containerId": "unionflow"
},
{
"name": "GESTIONNAIRE_MEMBRE",
"description": "Gestionnaire des membres avec droits de CRUD sur les membres",
"composite": false,
"clientRole": false,
"containerId": "unionflow"
},
{
"name": "ORGANISATEUR_EVENEMENT",
"description": "Organisateur d'événements avec droits de gestion des événements",
"composite": false,
"clientRole": false,
"containerId": "unionflow"
},
{
"name": "MEMBRE",
"description": "Membre standard avec droits de consultation",
"composite": false,
"clientRole": false,
"containerId": "unionflow"
}
]
},
"users": [
{
"username": "admin",
"enabled": true,
"emailVerified": true,
"firstName": "Administrateur",
"lastName": "Système",
"email": "admin@unionflow.dev",
"credentials": [
{
"type": "password",
"value": "admin123",
"temporary": false
}
],
"realmRoles": [
"ADMIN",
"PRESIDENT"
],
"clientRoles": {}
},
{
"username": "president",
"enabled": true,
"emailVerified": true,
"firstName": "Jean",
"lastName": "Dupont",
"email": "president@unionflow.dev",
"credentials": [
{
"type": "password",
"value": "president123",
"temporary": false
}
],
"realmRoles": [
"PRESIDENT",
"MEMBRE"
],
"clientRoles": {}
},
{
"username": "secretaire",
"enabled": true,
"emailVerified": true,
"firstName": "Marie",
"lastName": "Martin",
"email": "secretaire@unionflow.dev",
"credentials": [
{
"type": "password",
"value": "secretaire123",
"temporary": false
}
],
"realmRoles": [
"SECRETAIRE",
"GESTIONNAIRE_MEMBRE",
"MEMBRE"
],
"clientRoles": {}
},
{
"username": "tresorier",
"enabled": true,
"emailVerified": true,
"firstName": "Pierre",
"lastName": "Durand",
"email": "tresorier@unionflow.dev",
"credentials": [
{
"type": "password",
"value": "tresorier123",
"temporary": false
}
],
"realmRoles": [
"TRESORIER",
"MEMBRE"
],
"clientRoles": {}
},
{
"username": "membre1",
"enabled": true,
"emailVerified": true,
"firstName": "Sophie",
"lastName": "Bernard",
"email": "membre1@unionflow.dev",
"credentials": [
{
"type": "password",
"value": "membre123",
"temporary": false
}
],
"realmRoles": [
"MEMBRE"
],
"clientRoles": {}
}
],
"groups": [
{
"name": "Administration",
"path": "/Administration",
"realmRoles": [
"ADMIN"
],
"subGroups": []
},
{
"name": "Bureau",
"path": "/Bureau",
"realmRoles": [
"PRESIDENT",
"SECRETAIRE",
"TRESORIER"
],
"subGroups": []
},
{
"name": "Gestionnaires",
"path": "/Gestionnaires",
"realmRoles": [
"GESTIONNAIRE_MEMBRE",
"ORGANISATEUR_EVENEMENT"
],
"subGroups": []
},
{
"name": "Membres",
"path": "/Membres",
"realmRoles": [
"MEMBRE"
],
"subGroups": []
}
]
}

View File

@@ -1,71 +1,71 @@
# =============================================================
# UnionFlow — Messages externalisés (i18n)
# =============================================================
# Fichier principal (FR). Pour d'autres locales, créer
# messages_en.properties, messages_pt.properties, etc.
# =============================================================
# ── Validation ────────────────────────────────────────────────
validation.champ.obligatoire=Ce champ est obligatoire
validation.email.invalide=Adresse email invalide
validation.telephone.invalide=Numéro de téléphone invalide
validation.montant.positif=Le montant doit être positif
validation.ordre.positif=L'ordre doit être positif (>= 1)
validation.date.future=La date ne peut pas être dans le futur
validation.doublon.email=Une entité avec cet email existe déjà
validation.doublon.nom=Une entité avec ce nom existe déjà
validation.doublon.code=Un élément avec ce code existe déjà
# ── Organisation ──────────────────────────────────────────────
organisation.creation.succes=Organisation créée avec succès
organisation.modification.succes=Organisation mise à jour
organisation.suppression.succes=Organisation supprimée
organisation.suppression.membres.actifs=Impossible de supprimer une organisation avec des membres actifs
organisation.introuvable=Organisation non trouvée avec l''ID: {0}
organisation.email.doublon=Une organisation avec cet email existe déjà
organisation.nom.doublon=Une organisation avec ce nom existe déjà
organisation.numero.doublon=Une organisation avec ce numéro d''enregistrement existe déjà
# ── Membre ────────────────────────────────────────────────────
membre.creation.succes=Membre créé avec succès
membre.modification.succes=Membre mis à jour
membre.introuvable=Membre non trouvé avec l''ID: {0}
membre.email.doublon=Un membre avec cet email existe déjà
membre.numero.doublon=Un membre avec ce numéro existe déjà
# ── Cotisation ────────────────────────────────────────────────
cotisation.creation.succes=Cotisation créée avec succès
cotisation.introuvable=Cotisation non trouvée avec l''ID: {0}
cotisation.paiement.depasse=Le montant payé dépasse le montant dû
# ── Paiement ──────────────────────────────────────────────────
paiement.creation.succes=Paiement enregistré avec succès
paiement.introuvable=Paiement non trouvé avec l''ID: {0}
paiement.rattachement.obligatoire=type_entite_rattachee et entite_rattachee_id sont obligatoires
# ── Document ──────────────────────────────────────────────────
document.creation.succes=Document créé avec succès
document.introuvable=Document non trouvé avec l''ID: {0}
# ── Pièce jointe ─────────────────────────────────────────────
piecejointe.creation.succes=Pièce jointe créée
piecejointe.validation.rattachement=Le type d'entité et l'ID rattaché sont obligatoires
# ── Type référence ────────────────────────────────────────────
typeref.creation.succes=Type de référence créé
typeref.modification.succes=Type de référence mis à jour
typeref.suppression.succes=Type de référence supprimé
typeref.introuvable=Type de référence non trouvé: {0}
typeref.doublon=Un type de référence avec ce domaine/code existe déjà
typeref.systeme.protege=Les valeurs système ne peuvent pas être modifiées
# ── Sécurité ──────────────────────────────────────────────────
securite.non.authentifie=Utilisateur non authentifié
securite.acces.refuse=Accès refusé
securite.token.invalide=Token d''accès invalide
# ── Événement ─────────────────────────────────────────────────
evenement.creation.succes=Événement créé avec succès
evenement.introuvable=Événement non trouvé avec l''ID: {0}
evenement.inscription.fermee=Les inscriptions sont fermées
evenement.capacite.atteinte=Capacité maximale atteinte
# =============================================================
# UnionFlow — Messages externalisés (i18n)
# =============================================================
# Fichier principal (FR). Pour d'autres locales, créer
# messages_en.properties, messages_pt.properties, etc.
# =============================================================
# ── Validation ────────────────────────────────────────────────
validation.champ.obligatoire=Ce champ est obligatoire
validation.email.invalide=Adresse email invalide
validation.telephone.invalide=Numéro de téléphone invalide
validation.montant.positif=Le montant doit être positif
validation.ordre.positif=L'ordre doit être positif (>= 1)
validation.date.future=La date ne peut pas être dans le futur
validation.doublon.email=Une entité avec cet email existe déjà
validation.doublon.nom=Une entité avec ce nom existe déjà
validation.doublon.code=Un élément avec ce code existe déjà
# ── Organisation ──────────────────────────────────────────────
organisation.creation.succes=Organisation créée avec succès
organisation.modification.succes=Organisation mise à jour
organisation.suppression.succes=Organisation supprimée
organisation.suppression.membres.actifs=Impossible de supprimer une organisation avec des membres actifs
organisation.introuvable=Organisation non trouvée avec l''ID: {0}
organisation.email.doublon=Une organisation avec cet email existe déjà
organisation.nom.doublon=Une organisation avec ce nom existe déjà
organisation.numero.doublon=Une organisation avec ce numéro d''enregistrement existe déjà
# ── Membre ────────────────────────────────────────────────────
membre.creation.succes=Membre créé avec succès
membre.modification.succes=Membre mis à jour
membre.introuvable=Membre non trouvé avec l''ID: {0}
membre.email.doublon=Un membre avec cet email existe déjà
membre.numero.doublon=Un membre avec ce numéro existe déjà
# ── Cotisation ────────────────────────────────────────────────
cotisation.creation.succes=Cotisation créée avec succès
cotisation.introuvable=Cotisation non trouvée avec l''ID: {0}
cotisation.paiement.depasse=Le montant payé dépasse le montant dû
# ── Paiement ──────────────────────────────────────────────────
paiement.creation.succes=Paiement enregistré avec succès
paiement.introuvable=Paiement non trouvé avec l''ID: {0}
paiement.rattachement.obligatoire=type_entite_rattachee et entite_rattachee_id sont obligatoires
# ── Document ──────────────────────────────────────────────────
document.creation.succes=Document créé avec succès
document.introuvable=Document non trouvé avec l''ID: {0}
# ── Pièce jointe ─────────────────────────────────────────────
piecejointe.creation.succes=Pièce jointe créée
piecejointe.validation.rattachement=Le type d'entité et l'ID rattaché sont obligatoires
# ── Type référence ────────────────────────────────────────────
typeref.creation.succes=Type de référence créé
typeref.modification.succes=Type de référence mis à jour
typeref.suppression.succes=Type de référence supprimé
typeref.introuvable=Type de référence non trouvé: {0}
typeref.doublon=Un type de référence avec ce domaine/code existe déjà
typeref.systeme.protege=Les valeurs système ne peuvent pas être modifiées
# ── Sécurité ──────────────────────────────────────────────────
securite.non.authentifie=Utilisateur non authentifié
securite.acces.refuse=Accès refusé
securite.token.invalide=Token d''accès invalide
# ── Événement ─────────────────────────────────────────────────
evenement.creation.succes=Événement créé avec succès
evenement.introuvable=Événement non trouvé avec l''ID: {0}
evenement.inscription.fermee=Les inscriptions sont fermées
evenement.capacite.atteinte=Capacité maximale atteinte