chore(docker): add root Dockerfile pinning ubi8/openjdk-21:1.21 + UID 1001 for lionsctl pipeline
Some checks failed
CI/CD Pipeline / pipeline (push) Failing after 4m2s
Some checks failed
CI/CD Pipeline / pipeline (push) Failing after 4m2s
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -4,10 +4,6 @@
|
||||
# Surcharge application.properties — sans préfixes %dev.
|
||||
# ============================================================================
|
||||
|
||||
# DevServices désactivés en dev — on utilise le PostgreSQL local (localhost:5432/unionflow)
|
||||
# Les tests d'intégration avec Docker requièrent USE_DOCKER_TESTS=true
|
||||
quarkus.devservices.enabled=false
|
||||
|
||||
# Base de données PostgreSQL locale
|
||||
quarkus.datasource.username=skyfile
|
||||
quarkus.datasource.password=${DB_PASSWORD_DEV:skyfile}
|
||||
@@ -16,14 +12,12 @@ 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.schema-management.strategy=update
|
||||
quarkus.hibernate-orm.database.generation=update
|
||||
quarkus.hibernate-orm.log.sql=true
|
||||
|
||||
# Flyway — activé avec réparation auto des checksums modifiés
|
||||
quarkus.flyway.migrate-at-start=true
|
||||
quarkus.flyway.repair-at-start=true
|
||||
# Désactiver le remplacement de placeholders ${...} — les migrations utilisent $$ PL/pgSQL
|
||||
quarkus.flyway.placeholder-replacement=false
|
||||
|
||||
# CORS — permissif en dev (autorise tous les ports localhost pour Flutter Web)
|
||||
quarkus.http.cors.origins=*
|
||||
@@ -56,9 +50,6 @@ quarkus.log.category."org.hibernate.SQL".level=DEBUG
|
||||
quarkus.log.category."io.quarkus.oidc".level=INFO
|
||||
quarkus.log.category."io.quarkus.security".level=INFO
|
||||
|
||||
# Kafka — utiliser le broker local, pas de DevServices
|
||||
quarkus.kafka.devservices.enabled=false
|
||||
|
||||
# Wave — mock pour dev (pas de clé API requise)
|
||||
wave.mock.enabled=true
|
||||
wave.redirect.base.url=http://localhost:8085
|
||||
|
||||
@@ -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.schema-management.strategy=validate
|
||||
quarkus.hibernate-orm.database.generation=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.enabled=false
|
||||
quarkus.log.file.enable=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
|
||||
|
||||
@@ -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.schema-management.strategy=update
|
||||
quarkus.hibernate-orm.database.generation=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
|
||||
|
||||
@@ -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.enabled=true
|
||||
quarkus.http.cors=true
|
||||
quarkus.http.cors.methods=GET,POST,PUT,DELETE,OPTIONS
|
||||
quarkus.http.cors.headers=Content-Type,Authorization
|
||||
|
||||
@@ -49,9 +49,11 @@ 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.schema-management.strategy=update
|
||||
quarkus.hibernate-orm.database.generation=update
|
||||
quarkus.hibernate-orm.log.sql=false
|
||||
quarkus.hibernate-orm.jdbc.timezone=UTC
|
||||
quarkus.hibernate-orm.metrics.enabled=false
|
||||
|
||||
# Configuration Flyway — base commune
|
||||
quarkus.flyway.migrate-at-start=true
|
||||
quarkus.flyway.baseline-on-migrate=true
|
||||
@@ -87,16 +89,8 @@ quarkus.swagger-ui.tags-sorter=alpha
|
||||
# Health
|
||||
quarkus.smallrye-health.root-path=/health
|
||||
|
||||
# Métriques Prometheus (Micrometer) — exposées sur /q/metrics
|
||||
quarkus.micrometer.enabled=true
|
||||
quarkus.micrometer.export.prometheus.enabled=true
|
||||
quarkus.micrometer.export.prometheus.path=/q/metrics
|
||||
# Métriques Hibernate ORM
|
||||
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.enabled=true
|
||||
quarkus.log.console.enable=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
|
||||
@@ -203,20 +197,3 @@ mp.messaging.incoming.chat-messages-in.topic=unionflow.chat.messages
|
||||
mp.messaging.incoming.chat-messages-in.value.deserializer=org.apache.kafka.common.serialization.StringDeserializer
|
||||
mp.messaging.incoming.chat-messages-in.key.deserializer=org.apache.kafka.common.serialization.StringDeserializer
|
||||
mp.messaging.incoming.chat-messages-in.group.id=unionflow-websocket-server
|
||||
|
||||
# === PI-SPI BCEAO (P0.3 — deadline 30/06/2026) ===
|
||||
pispi.api.base-url=${PISPI_API_URL:https://sandbox.pispi.bceao.int/business-api/v1}
|
||||
pispi.institution.bic=${PISPI_BIC:BCEAOCIAB}
|
||||
# Activer la priorité PI-SPI dans l'orchestrateur (obligatoire en prod après certification)
|
||||
payment.pispi-priority=${PAYMENT_PISPI_PRIORITY:false}
|
||||
|
||||
# Secrets externes : mappage env vars actif en prod uniquement (profile-scoped).
|
||||
# En dev : propriétés non définies, @ConfigProperty(defaultValue="") côté Java (mode mock).
|
||||
%prod.pispi.api.client-id=${PISPI_CLIENT_ID:}
|
||||
%prod.pispi.api.client-secret=${PISPI_CLIENT_SECRET:}
|
||||
%prod.pispi.institution.code=${PISPI_INSTITUTION_CODE:}
|
||||
%prod.pispi.webhook.secret=${PISPI_WEBHOOK_SECRET:}
|
||||
%prod.pispi.webhook.allowed-ips=${PISPI_ALLOWED_IPS:}
|
||||
%prod.mtnmomo.collection.subscription-key=${MTNMOMO_SUBSCRIPTION_KEY:}
|
||||
%prod.orange.api.client-id=${ORANGE_API_CLIENT_ID:}
|
||||
%prod.firebase.service-account-key-path=${FIREBASE_SERVICE_ACCOUNT_KEY_PATH:}
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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)';
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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';
|
||||
|
||||
@@ -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)';
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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, 5000–10000 XOF/mois)';
|
||||
COMMENT ON TABLE souscriptions_organisation IS 'Abonnement actif d''une organisation racine — quota, durée, référence Wave';
|
||||
COMMENT ON COLUMN souscriptions_organisation.quota_utilise IS 'Incrémenté automatiquement à chaque adhésion validée. Bloquant si = quota_max.';
|
||||
-- ============================================================
|
||||
-- V2.2 — SaaS : formules_abonnement + souscriptions_organisation
|
||||
-- Auteur: UnionFlow Team | BCEAO/OHADA compliant
|
||||
-- ============================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS formules_abonnement (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
|
||||
code VARCHAR(20) UNIQUE NOT NULL, -- STARTER, STANDARD, PREMIUM, CRYSTAL
|
||||
libelle VARCHAR(100) NOT NULL,
|
||||
description TEXT,
|
||||
max_membres INTEGER, -- NULL = illimité (Crystal+)
|
||||
max_stockage_mo INTEGER NOT NULL DEFAULT 1024, -- 1 Go par défaut
|
||||
prix_mensuel DECIMAL(10,2) NOT NULL CHECK (prix_mensuel >= 0),
|
||||
prix_annuel DECIMAL(10,2) NOT NULL CHECK (prix_annuel >= 0),
|
||||
actif BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
ordre_affichage INTEGER DEFAULT 0,
|
||||
|
||||
-- Métadonnées BaseEntity
|
||||
date_creation TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
date_modification TIMESTAMP,
|
||||
cree_par VARCHAR(255),
|
||||
modifie_par VARCHAR(255),
|
||||
version BIGINT NOT NULL DEFAULT 0,
|
||||
|
||||
CONSTRAINT chk_formule_code CHECK (code IN ('STARTER','STANDARD','PREMIUM','CRYSTAL'))
|
||||
);
|
||||
|
||||
-- Données initiales des forfaits (XOF, 1er Janvier 2026)
|
||||
INSERT INTO formules_abonnement (id, code, libelle, description, max_membres, max_stockage_mo, prix_mensuel, prix_annuel, actif, ordre_affichage)
|
||||
VALUES
|
||||
(gen_random_uuid(), 'STARTER', 'Formule Starter', 'Idéal pour démarrer — jusqu''à 50 membres', 50, 1024, 5000.00, 50000.00, true, 1),
|
||||
(gen_random_uuid(), 'STANDARD', 'Formule Standard', 'Pour les organisations en croissance', 200, 1024, 7000.00, 70000.00, true, 2),
|
||||
(gen_random_uuid(), 'PREMIUM', 'Formule Premium', 'Organisations établies', 500, 1024, 9000.00, 90000.00, true, 3),
|
||||
(gen_random_uuid(), 'CRYSTAL', 'Formule Crystal', 'Fédérations et grandes organisations', NULL,1024, 10000.00, 100000.00, true, 4)
|
||||
ON CONFLICT (code) DO NOTHING;
|
||||
|
||||
-- ============================================================
|
||||
CREATE TABLE IF NOT EXISTS souscriptions_organisation (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
|
||||
organisation_id UUID UNIQUE NOT NULL,
|
||||
formule_id UUID NOT NULL,
|
||||
type_periode VARCHAR(10) NOT NULL DEFAULT 'MENSUEL', -- MENSUEL | ANNUEL
|
||||
date_debut DATE NOT NULL,
|
||||
date_fin DATE NOT NULL,
|
||||
quota_max INTEGER, -- snapshot de formule.max_membres
|
||||
quota_utilise INTEGER NOT NULL DEFAULT 0,
|
||||
statut VARCHAR(30) NOT NULL DEFAULT 'ACTIVE',
|
||||
reference_paiement_wave VARCHAR(100),
|
||||
wave_session_id VARCHAR(255),
|
||||
date_dernier_paiement DATE,
|
||||
date_prochain_paiement DATE,
|
||||
|
||||
-- Métadonnées BaseEntity
|
||||
actif BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
date_creation TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
date_modification TIMESTAMP,
|
||||
cree_par VARCHAR(255),
|
||||
modifie_par VARCHAR(255),
|
||||
version BIGINT NOT NULL DEFAULT 0,
|
||||
|
||||
CONSTRAINT fk_souscription_org FOREIGN KEY (organisation_id) REFERENCES organisations(id) ON DELETE CASCADE,
|
||||
CONSTRAINT fk_souscription_formule FOREIGN KEY (formule_id) REFERENCES formules_abonnement(id),
|
||||
CONSTRAINT chk_souscription_statut CHECK (statut IN ('ACTIVE','EXPIREE','SUSPENDUE','RESILIEE')),
|
||||
CONSTRAINT chk_souscription_periode CHECK (type_periode IN ('MENSUEL','ANNUEL')),
|
||||
CONSTRAINT chk_souscription_quota CHECK (quota_utilise >= 0)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_souscription_org ON souscriptions_organisation(organisation_id);
|
||||
CREATE INDEX idx_souscription_statut ON souscriptions_organisation(statut);
|
||||
CREATE INDEX idx_souscription_fin ON souscriptions_organisation(date_fin);
|
||||
|
||||
COMMENT ON TABLE formules_abonnement IS 'Catalogue des forfaits SaaS UnionFlow (Starter→Crystal, 5000–10000 XOF/mois)';
|
||||
COMMENT ON TABLE souscriptions_organisation IS 'Abonnement actif d''une organisation racine — quota, durée, référence Wave';
|
||||
COMMENT ON COLUMN souscriptions_organisation.quota_utilise IS 'Incrémenté automatiquement à chaque adhésion validée. Bloquant si = quota_max.';
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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é';
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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
|
||||
);
|
||||
|
||||
@@ -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
|
||||
);
|
||||
|
||||
@@ -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
|
||||
);
|
||||
|
||||
@@ -1,57 +1,57 @@
|
||||
# Stratégie des migrations Flyway
|
||||
|
||||
## Vue d’ensemble
|
||||
|
||||
| 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 d’origine sont conservés dans **`db/legacy-migrations/`** (référence uniquement, Flyway ne les exécute pas).
|
||||
|
||||
## Ordre d’exé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 l’historique 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 n’exé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 d’alignement.
|
||||
|
||||
## 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 d’ensemble
|
||||
|
||||
| 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 d’origine sont conservés dans **`db/legacy-migrations/`** (référence uniquement, Flyway ne les exécute pas).
|
||||
|
||||
## Ordre d’exé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 l’historique 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 n’exé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 d’alignement.
|
||||
|
||||
## 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/`.)
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
-- ============================================================================
|
||||
-- V32 — Mutuelle : Parts Sociales + Paramètres Financiers + Intérêts
|
||||
--
|
||||
-- Ajoute les tables nécessaires pour les fonctionnalités manquantes identifiées
|
||||
-- dans l'analyse du fichier FUSION 2013-2021.xlsx de la Mutuelle GBANE :
|
||||
-- 1. comptes_parts_sociales — capital social des membres
|
||||
-- 2. transactions_parts_sociales — historique des mouvements de parts
|
||||
-- 3. parametres_financiers_mutuelle — taux, périodicités, valeur nominale
|
||||
-- ============================================================================
|
||||
|
||||
-- ── 1. Paramètres financiers de la mutuelle ────────────────────────────────
|
||||
CREATE TABLE IF NOT EXISTS parametres_financiers_mutuelle (
|
||||
id UUID NOT NULL DEFAULT gen_random_uuid() PRIMARY KEY,
|
||||
organisation_id UUID NOT NULL UNIQUE,
|
||||
valeur_nominale_par_defaut NUMERIC(19,4) NOT NULL DEFAULT 5000,
|
||||
taux_interet_annuel_epargne NUMERIC(6,4) NOT NULL DEFAULT 0.0300,
|
||||
taux_dividende_parts_annuel NUMERIC(6,4) NOT NULL DEFAULT 0.0500,
|
||||
periodicite_calcul VARCHAR(20) NOT NULL DEFAULT 'MENSUEL',
|
||||
seuil_min_epargne_interets NUMERIC(19,4) DEFAULT 0,
|
||||
prochaine_calcul_interets DATE,
|
||||
dernier_calcul_interets DATE,
|
||||
dernier_nb_comptes_traites INTEGER DEFAULT 0,
|
||||
-- BaseEntity cols
|
||||
date_creation TIMESTAMP NOT NULL DEFAULT NOW(),
|
||||
date_modification TIMESTAMP,
|
||||
cree_par VARCHAR(255),
|
||||
modifie_par VARCHAR(255),
|
||||
version BIGINT DEFAULT 0,
|
||||
actif BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
CONSTRAINT fk_pfm_organisation FOREIGN KEY (organisation_id) REFERENCES organisations(id)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_pfm_org ON parametres_financiers_mutuelle(organisation_id);
|
||||
|
||||
-- ── 2. Comptes de parts sociales ───────────────────────────────────────────
|
||||
CREATE TABLE IF NOT EXISTS comptes_parts_sociales (
|
||||
id UUID NOT NULL DEFAULT gen_random_uuid() PRIMARY KEY,
|
||||
membre_id UUID NOT NULL,
|
||||
organisation_id UUID NOT NULL,
|
||||
numero_compte VARCHAR(50) NOT NULL UNIQUE,
|
||||
nombre_parts INTEGER NOT NULL DEFAULT 0,
|
||||
valeur_nominale NUMERIC(19,4) NOT NULL,
|
||||
montant_total NUMERIC(19,4) NOT NULL DEFAULT 0,
|
||||
total_dividendes_recus NUMERIC(19,4) NOT NULL DEFAULT 0,
|
||||
statut VARCHAR(30) NOT NULL DEFAULT 'ACTIF',
|
||||
date_ouverture DATE NOT NULL DEFAULT CURRENT_DATE,
|
||||
date_derniere_operation DATE,
|
||||
notes VARCHAR(500),
|
||||
-- BaseEntity cols
|
||||
date_creation TIMESTAMP NOT NULL DEFAULT NOW(),
|
||||
date_modification TIMESTAMP,
|
||||
cree_par VARCHAR(255),
|
||||
modifie_par VARCHAR(255),
|
||||
version BIGINT DEFAULT 0,
|
||||
actif BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
CONSTRAINT fk_cps_membre FOREIGN KEY (membre_id) REFERENCES utilisateurs(id),
|
||||
CONSTRAINT fk_cps_organisation FOREIGN KEY (organisation_id) REFERENCES organisations(id)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_cps_numero ON comptes_parts_sociales(numero_compte);
|
||||
CREATE INDEX IF NOT EXISTS idx_cps_membre ON comptes_parts_sociales(membre_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_cps_org ON comptes_parts_sociales(organisation_id);
|
||||
|
||||
-- ── 3. Transactions sur parts sociales ────────────────────────────────────
|
||||
CREATE TABLE IF NOT EXISTS transactions_parts_sociales (
|
||||
id UUID NOT NULL DEFAULT gen_random_uuid() PRIMARY KEY,
|
||||
compte_id UUID NOT NULL,
|
||||
type_transaction VARCHAR(50) NOT NULL,
|
||||
nombre_parts INTEGER NOT NULL,
|
||||
montant NUMERIC(19,4) NOT NULL,
|
||||
solde_parts_avant INTEGER NOT NULL DEFAULT 0,
|
||||
solde_parts_apres INTEGER NOT NULL DEFAULT 0,
|
||||
motif VARCHAR(500),
|
||||
reference_externe VARCHAR(100),
|
||||
date_transaction TIMESTAMP NOT NULL DEFAULT NOW(),
|
||||
-- BaseEntity cols
|
||||
date_creation TIMESTAMP NOT NULL DEFAULT NOW(),
|
||||
date_modification TIMESTAMP,
|
||||
cree_par VARCHAR(255),
|
||||
modifie_par VARCHAR(255),
|
||||
version BIGINT DEFAULT 0,
|
||||
actif BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
CONSTRAINT fk_tps_compte FOREIGN KEY (compte_id) REFERENCES comptes_parts_sociales(id)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_tps_compte ON transactions_parts_sociales(compte_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_tps_date ON transactions_parts_sociales(date_transaction);
|
||||
@@ -1,15 +0,0 @@
|
||||
-- ============================================================================
|
||||
-- V33 — Correction colonnes legacy de audit_logs
|
||||
--
|
||||
-- La V1 crée audit_logs avec action VARCHAR(50) NOT NULL (ancien schéma).
|
||||
-- L'entité AuditLog utilise type_action à la place.
|
||||
-- Hibernate ne remplit pas action → violation NOT NULL sur chaque insert.
|
||||
-- Fix : rendre action nullable + nettoyer les autres colonnes orphelines.
|
||||
-- ============================================================================
|
||||
|
||||
-- Rendre la colonne legacy nullable (elle est supersédée par type_action)
|
||||
ALTER TABLE audit_logs ALTER COLUMN action DROP NOT NULL;
|
||||
|
||||
-- Aligner entite_id : la V1 déclare UUID mais l'entité stocke une String (UUID textuel)
|
||||
-- → changer en VARCHAR pour éviter des cast errors sur certains IDs non-UUID
|
||||
ALTER TABLE audit_logs ALTER COLUMN entite_id TYPE VARCHAR(255) USING entite_id::VARCHAR;
|
||||
@@ -1,39 +0,0 @@
|
||||
-- ============================================================================
|
||||
-- V34 — Rendre membre_id nullable dans les tables où l'entité Hibernate
|
||||
-- utilise désormais une autre colonne (utilisateur_id, membre_organisation_id).
|
||||
--
|
||||
-- Contexte : V1 crée ces tables avec membre_id UUID NOT NULL. Les entités ont
|
||||
-- évolué pour utiliser utilisateur_id (MembreOrganisation, DemandeAdhesion,
|
||||
-- IntentionPaiement) ou membre_organisation_id (MembreRole). Hibernate update
|
||||
-- a ajouté les nouvelles colonnes mais n'a pas supprimé membre_id.
|
||||
-- Résultat : chaque insert lève une violation NOT NULL sur membre_id.
|
||||
-- Fix : rendre membre_id nullable (colonne legacy, plus utilisée par le code).
|
||||
-- ============================================================================
|
||||
|
||||
-- membres_organisations : entité utilise utilisateur_id
|
||||
ALTER TABLE membres_organisations ALTER COLUMN membre_id DROP NOT NULL;
|
||||
|
||||
-- membres_roles : entité utilise membre_organisation_id
|
||||
ALTER TABLE membres_roles ALTER COLUMN membre_id DROP NOT NULL;
|
||||
|
||||
-- demandes_adhesion : entité utilise utilisateur_id
|
||||
DO $$
|
||||
BEGIN
|
||||
IF EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'demandes_adhesion' AND column_name = 'membre_id'
|
||||
) THEN
|
||||
ALTER TABLE demandes_adhesion ALTER COLUMN membre_id DROP NOT NULL;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- intentions_paiement : entité utilise utilisateur_id
|
||||
DO $$
|
||||
BEGIN
|
||||
IF EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'intentions_paiement' AND column_name = 'membre_id'
|
||||
) THEN
|
||||
ALTER TABLE intentions_paiement ALTER COLUMN membre_id DROP NOT NULL;
|
||||
END IF;
|
||||
END $$;
|
||||
@@ -1,77 +0,0 @@
|
||||
-- ============================================================================
|
||||
-- V35 — Recalibrage nombre_membres + trigger auto-maintien
|
||||
--
|
||||
-- DATA-01 : Le compteur organisations.nombre_membres est désynchronisé quand
|
||||
-- des membres sont importés directement en DB (hors service Java).
|
||||
-- Fix :
|
||||
-- 1. Recalibrage immédiat depuis membres_organisations réels (actifs)
|
||||
-- 2. Trigger PostgreSQL pour maintenir le compteur à jour automatiquement
|
||||
-- ============================================================================
|
||||
|
||||
-- 1. Recalibrage ponctuel : recalculer depuis la table membres_organisations
|
||||
UPDATE organisations o
|
||||
SET nombre_membres = (
|
||||
SELECT COUNT(*)
|
||||
FROM membres_organisations mo
|
||||
WHERE mo.organisation_id = o.id
|
||||
AND mo.actif = true
|
||||
AND mo.statut IN ('ACTIF', 'ACTIF_PREMIUM')
|
||||
);
|
||||
|
||||
-- 2. Fonction trigger : incrémente/décrémente selon INSERT/UPDATE/DELETE
|
||||
CREATE OR REPLACE FUNCTION update_organisation_nombre_membres()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
IF TG_OP = 'INSERT' THEN
|
||||
-- Nouveau membre actif → incrémenter
|
||||
IF NEW.actif = true AND NEW.statut IN ('ACTIF', 'ACTIF_PREMIUM') THEN
|
||||
UPDATE organisations
|
||||
SET nombre_membres = GREATEST(0, nombre_membres + 1)
|
||||
WHERE id = NEW.organisation_id;
|
||||
END IF;
|
||||
|
||||
ELSIF TG_OP = 'UPDATE' THEN
|
||||
-- Transition actif/inactif ou statut
|
||||
DECLARE
|
||||
was_counted BOOLEAN := OLD.actif = true AND OLD.statut IN ('ACTIF', 'ACTIF_PREMIUM');
|
||||
is_counted BOOLEAN := NEW.actif = true AND NEW.statut IN ('ACTIF', 'ACTIF_PREMIUM');
|
||||
BEGIN
|
||||
IF NOT was_counted AND is_counted THEN
|
||||
UPDATE organisations
|
||||
SET nombre_membres = GREATEST(0, nombre_membres + 1)
|
||||
WHERE id = NEW.organisation_id;
|
||||
ELSIF was_counted AND NOT is_counted THEN
|
||||
UPDATE organisations
|
||||
SET nombre_membres = GREATEST(0, nombre_membres - 1)
|
||||
WHERE id = OLD.organisation_id;
|
||||
END IF;
|
||||
END;
|
||||
|
||||
ELSIF TG_OP = 'DELETE' THEN
|
||||
-- Suppression physique (rare)
|
||||
IF OLD.actif = true AND OLD.statut IN ('ACTIF', 'ACTIF_PREMIUM') THEN
|
||||
UPDATE organisations
|
||||
SET nombre_membres = GREATEST(0, nombre_membres - 1)
|
||||
WHERE id = OLD.organisation_id;
|
||||
END IF;
|
||||
END IF;
|
||||
|
||||
RETURN COALESCE(NEW, OLD);
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
-- 3. Attacher le trigger à membres_organisations
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM pg_trigger
|
||||
WHERE tgname = 'trg_update_nombre_membres'
|
||||
AND tgrelid = 'membres_organisations'::regclass
|
||||
) THEN
|
||||
CREATE TRIGGER trg_update_nombre_membres
|
||||
AFTER INSERT OR UPDATE OF actif, statut OR DELETE
|
||||
ON membres_organisations
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION update_organisation_nombre_membres();
|
||||
END IF;
|
||||
END $$;
|
||||
@@ -1,393 +0,0 @@
|
||||
-- ============================================================================
|
||||
-- V36 — SYSCOHADA : Alignement schéma + Seeds plan comptable standard + Trigger
|
||||
--
|
||||
-- P0.4 ROADMAP_2026.md — Obligation OHADA SYSCOHADA révisé (applicable depuis 2018)
|
||||
-- Corrige l'écart entre V1 (schéma minimal) et les entités Java (colonnes Hibernate).
|
||||
-- Ajoute le plan comptable standard SYSCOHADA pour mutuelles/coopératives UEMOA.
|
||||
-- ============================================================================
|
||||
|
||||
-- ============================================================================
|
||||
-- 1. COMPTES_COMPTABLES — Alignement colonnes V1 → entité Java
|
||||
-- ============================================================================
|
||||
|
||||
-- La V1 crée la table avec numero/libelle/type_compte/organisation_id seulement.
|
||||
-- L'entité Java attend : numero_compte, classe_comptable, solde_initial, solde_actuel,
|
||||
-- compte_collectif, compte_analytique, cree_par, modifie_par.
|
||||
|
||||
-- Renommer la colonne numero → numero_compte si elle n'a pas déjà été renommée par Hibernate
|
||||
-- Sinon : si les deux colonnes coexistent (Hibernate a créé numero_compte, V1 a laissé numero),
|
||||
-- on supprime l'ancienne colonne obsolète numero (NOT NULL sans défaut, bloque les INSERTs).
|
||||
DO $$
|
||||
BEGIN
|
||||
IF EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'comptes_comptables' AND column_name = 'numero'
|
||||
) THEN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'comptes_comptables' AND column_name = 'numero_compte'
|
||||
) THEN
|
||||
ALTER TABLE comptes_comptables RENAME COLUMN numero TO numero_compte;
|
||||
ELSE
|
||||
-- Les deux colonnes coexistent : recopier les valeurs vers numero_compte si besoin,
|
||||
-- puis supprimer la colonne obsolète numero.
|
||||
UPDATE comptes_comptables SET numero_compte = numero
|
||||
WHERE numero_compte IS NULL AND numero IS NOT NULL;
|
||||
ALTER TABLE comptes_comptables DROP COLUMN numero;
|
||||
END IF;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- Ajouter colonnes manquantes si pas encore créées par Hibernate update
|
||||
ALTER TABLE comptes_comptables
|
||||
ADD COLUMN IF NOT EXISTS classe_comptable INTEGER,
|
||||
ADD COLUMN IF NOT EXISTS solde_initial DECIMAL(14,2) DEFAULT 0,
|
||||
ADD COLUMN IF NOT EXISTS solde_actuel DECIMAL(14,2) DEFAULT 0,
|
||||
ADD COLUMN IF NOT EXISTS compte_collectif BOOLEAN DEFAULT false,
|
||||
ADD COLUMN IF NOT EXISTS compte_analytique BOOLEAN DEFAULT false,
|
||||
ADD COLUMN IF NOT EXISTS description VARCHAR(500),
|
||||
ADD COLUMN IF NOT EXISTS cree_par VARCHAR(255),
|
||||
ADD COLUMN IF NOT EXISTS modifie_par VARCHAR(255);
|
||||
|
||||
-- Déduire classe_comptable depuis numero_compte si null (première chiffre du numéro)
|
||||
UPDATE comptes_comptables
|
||||
SET classe_comptable = CAST(LEFT(numero_compte, 1) AS INTEGER)
|
||||
WHERE classe_comptable IS NULL AND numero_compte IS NOT NULL AND LENGTH(numero_compte) > 0;
|
||||
|
||||
-- Rendre classe_comptable NOT NULL après backfill
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'comptes_comptables' AND column_name = 'classe_comptable'
|
||||
AND is_nullable = 'NO'
|
||||
) THEN
|
||||
ALTER TABLE comptes_comptables ALTER COLUMN classe_comptable SET NOT NULL;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- Contrainte classe 1-9 (SYSCOHADA a 9 classes, pas 7)
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'chk_compte_classe_syscohada') THEN
|
||||
ALTER TABLE comptes_comptables
|
||||
ADD CONSTRAINT chk_compte_classe_syscohada
|
||||
CHECK (classe_comptable >= 1 AND classe_comptable <= 9);
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- ============================================================================
|
||||
-- 2. JOURNAUX_COMPTABLES — Alignement colonnes
|
||||
-- ============================================================================
|
||||
ALTER TABLE journaux_comptables
|
||||
ADD COLUMN IF NOT EXISTS date_debut DATE,
|
||||
ADD COLUMN IF NOT EXISTS date_fin DATE,
|
||||
ADD COLUMN IF NOT EXISTS statut VARCHAR(20) DEFAULT 'OUVERT',
|
||||
ADD COLUMN IF NOT EXISTS description VARCHAR(500),
|
||||
ADD COLUMN IF NOT EXISTS cree_par VARCHAR(255),
|
||||
ADD COLUMN IF NOT EXISTS modifie_par VARCHAR(255);
|
||||
|
||||
-- ============================================================================
|
||||
-- 3. ECRITURES_COMPTABLES — Alignement colonnes
|
||||
-- ============================================================================
|
||||
ALTER TABLE ecritures_comptables
|
||||
ADD COLUMN IF NOT EXISTS organisation_id UUID REFERENCES organisations(id),
|
||||
ADD COLUMN IF NOT EXISTS paiement_id UUID REFERENCES paiements(id),
|
||||
ADD COLUMN IF NOT EXISTS reference VARCHAR(100),
|
||||
ADD COLUMN IF NOT EXISTS lettrage VARCHAR(20),
|
||||
ADD COLUMN IF NOT EXISTS pointe BOOLEAN DEFAULT false,
|
||||
ADD COLUMN IF NOT EXISTS montant_debit DECIMAL(14,2) DEFAULT 0,
|
||||
ADD COLUMN IF NOT EXISTS montant_credit DECIMAL(14,2) DEFAULT 0,
|
||||
ADD COLUMN IF NOT EXISTS commentaire VARCHAR(1000),
|
||||
ADD COLUMN IF NOT EXISTS cree_par VARCHAR(255),
|
||||
ADD COLUMN IF NOT EXISTS modifie_par VARCHAR(255);
|
||||
|
||||
-- ============================================================================
|
||||
-- 4. LIGNES_ECRITURE — Alignement colonnes (debit/credit → montant_debit/credit)
|
||||
-- ============================================================================
|
||||
|
||||
-- Renommer compte_id → compte_comptable_id si besoin
|
||||
DO $$
|
||||
BEGIN
|
||||
IF EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'lignes_ecriture' AND column_name = 'compte_id'
|
||||
) AND NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'lignes_ecriture' AND column_name = 'compte_comptable_id'
|
||||
) THEN
|
||||
ALTER TABLE lignes_ecriture RENAME COLUMN compte_id TO compte_comptable_id;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- Renommer debit/credit → montant_debit/montant_credit si besoin
|
||||
DO $$
|
||||
BEGIN
|
||||
IF EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'lignes_ecriture' AND column_name = 'debit'
|
||||
) AND NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'lignes_ecriture' AND column_name = 'montant_debit'
|
||||
) THEN
|
||||
ALTER TABLE lignes_ecriture RENAME COLUMN debit TO montant_debit;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'lignes_ecriture' AND column_name = 'credit'
|
||||
) AND NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'lignes_ecriture' AND column_name = 'montant_credit'
|
||||
) THEN
|
||||
ALTER TABLE lignes_ecriture RENAME COLUMN credit TO montant_credit;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
ALTER TABLE lignes_ecriture
|
||||
ADD COLUMN IF NOT EXISTS numero_ligne INTEGER,
|
||||
ADD COLUMN IF NOT EXISTS reference VARCHAR(100),
|
||||
ADD COLUMN IF NOT EXISTS cree_par VARCHAR(255),
|
||||
ADD COLUMN IF NOT EXISTS modifie_par VARCHAR(255);
|
||||
|
||||
-- ============================================================================
|
||||
-- 5. TABLE MODELE_PLAN_COMPTABLE — Template SYSCOHADA (comptes standards réutilisables)
|
||||
-- ============================================================================
|
||||
CREATE TABLE IF NOT EXISTS modele_plan_comptable (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
numero_compte VARCHAR(10) NOT NULL UNIQUE,
|
||||
libelle VARCHAR(200) NOT NULL,
|
||||
classe_comptable INTEGER NOT NULL CHECK (classe_comptable >= 1 AND classe_comptable <= 9),
|
||||
type_compte VARCHAR(30) NOT NULL,
|
||||
description VARCHAR(500),
|
||||
actif BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
CONSTRAINT chk_modele_classe CHECK (classe_comptable >= 1 AND classe_comptable <= 9)
|
||||
);
|
||||
|
||||
-- ============================================================================
|
||||
-- 6. SEEDS — Plan comptable SYSCOHADA standard pour mutuelles/coopératives UEMOA
|
||||
-- ============================================================================
|
||||
INSERT INTO modele_plan_comptable (numero_compte, libelle, classe_comptable, type_compte) VALUES
|
||||
-- CLASSE 1 — Ressources durables
|
||||
('101000', 'Fonds propres', 1, 'PASSIF'),
|
||||
('104000', 'Réserve légale', 1, 'PASSIF'),
|
||||
('106000', 'Réserves statutaires', 1, 'PASSIF'),
|
||||
('120000', 'Résultat de l''exercice', 1, 'PASSIF'),
|
||||
('160000', 'Emprunts à long terme', 1, 'PASSIF'),
|
||||
('165000', 'Dépôts et cautionnements reçus', 1, 'PASSIF'),
|
||||
|
||||
-- CLASSE 2 — Actif immobilisé
|
||||
('222000', 'Matériel de transport', 2, 'ACTIF'),
|
||||
('232000', 'Matériel informatique', 2, 'ACTIF'),
|
||||
('244000', 'Logiciels informatiques', 2, 'ACTIF'),
|
||||
('281000', 'Amortissements immobilisations', 2, 'ACTIF'),
|
||||
|
||||
-- CLASSE 4 — Tiers
|
||||
('411000', 'Membres débiteurs — cotisations dues', 4, 'ACTIF'),
|
||||
('412000', 'Membres débiteurs — parts sociales dues', 4, 'ACTIF'),
|
||||
('413000', 'Membres débiteurs — avances sur prestations', 4, 'ACTIF'),
|
||||
('421000', 'Personnel — rémunérations dues', 4, 'PASSIF'),
|
||||
('431000', 'Sécurité sociale — cotisations patronales', 4, 'PASSIF'),
|
||||
('441000', 'État — TVA collectée', 4, 'PASSIF'),
|
||||
('447000', 'État — autres impôts et taxes', 4, 'PASSIF'),
|
||||
('467000', 'Tiers divers débiteurs', 4, 'ACTIF'),
|
||||
('468000', 'Tiers divers créditeurs', 4, 'PASSIF'),
|
||||
|
||||
-- CLASSE 5 — Trésorerie
|
||||
('512100', 'Compte Wave Senegal', 5, 'TRESORERIE'),
|
||||
('512200', 'Compte Orange Money', 5, 'TRESORERIE'),
|
||||
('512300', 'Compte MTN MoMo', 5, 'TRESORERIE'),
|
||||
('512400', 'Compte Moov Money', 5, 'TRESORERIE'),
|
||||
('512500', 'Compte bancaire principal', 5, 'TRESORERIE'),
|
||||
('531000', 'Caisse principale', 5, 'TRESORERIE'),
|
||||
('581000', 'Virements internes de trésorerie', 5, 'TRESORERIE'),
|
||||
|
||||
-- CLASSE 6 — Charges
|
||||
('601000', 'Achats de marchandises', 6, 'CHARGES'),
|
||||
('611000', 'Transports', 6, 'CHARGES'),
|
||||
('612000', 'Frais de télécommunications', 6, 'CHARGES'),
|
||||
('613000', 'Frais d''assurance', 6, 'CHARGES'),
|
||||
('614000', 'Location matériel', 6, 'CHARGES'),
|
||||
('616000', 'Frais d''entretien et réparations', 6, 'CHARGES'),
|
||||
('621000', 'Personnel externe (prestataires)', 6, 'CHARGES'),
|
||||
('622000', 'Rémunérations du personnel', 6, 'CHARGES'),
|
||||
('631000', 'Frais financiers — intérêts d''emprunts', 6, 'CHARGES'),
|
||||
('641000', 'Charges sur prestations mutuelles', 6, 'CHARGES'),
|
||||
('651000', 'Pertes sur créances irrécouvrables', 6, 'CHARGES'),
|
||||
|
||||
-- CLASSE 7 — Produits
|
||||
('706100', 'Cotisations ordinaires membres', 7, 'PRODUITS'),
|
||||
('706200', 'Cotisations spéciales / majorées', 7, 'PRODUITS'),
|
||||
('706300', 'Parts sociales', 7, 'PRODUITS'),
|
||||
('706400', 'Droits d''adhésion', 7, 'PRODUITS'),
|
||||
('762000', 'Produits financiers — intérêts épargne', 7, 'PRODUITS'),
|
||||
('771000', 'Subventions d''exploitation reçues', 7, 'PRODUITS'),
|
||||
('775000', 'Prestations de services', 7, 'PRODUITS'),
|
||||
|
||||
-- CLASSE 8 — Charges et produits exceptionnels / hors activité
|
||||
('870000', 'Dons reçus', 8, 'PRODUITS'),
|
||||
('871000', 'Legs et donations', 8, 'PRODUITS'),
|
||||
('875000', 'Produits exceptionnels d''événements', 8, 'PRODUITS'),
|
||||
('878000', 'Autres produits hors activité ordinaire', 8, 'PRODUITS'),
|
||||
('880000', 'Charges exceptionnelles', 8, 'CHARGES'),
|
||||
|
||||
-- CLASSE 9 — Engagements / comptabilité analytique
|
||||
('990000', 'Engagements hors bilan donnés', 9, 'AUTRE'),
|
||||
('991000', 'Engagements hors bilan reçus', 9, 'AUTRE')
|
||||
|
||||
ON CONFLICT (numero_compte) DO NOTHING;
|
||||
|
||||
-- ============================================================================
|
||||
-- 7. TRIGGER — Initialisation automatique du plan comptable à la création d'org
|
||||
-- ============================================================================
|
||||
CREATE OR REPLACE FUNCTION init_plan_comptable_organisation()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
INSERT INTO comptes_comptables (
|
||||
id, numero_compte, libelle, classe_comptable, type_compte,
|
||||
description, organisation_id, solde_initial, solde_actuel,
|
||||
compte_collectif, compte_analytique, actif,
|
||||
date_creation, version
|
||||
)
|
||||
SELECT
|
||||
gen_random_uuid(),
|
||||
m.numero_compte,
|
||||
m.libelle,
|
||||
m.classe_comptable,
|
||||
m.type_compte,
|
||||
m.description,
|
||||
NEW.id,
|
||||
0, 0,
|
||||
false, false, true,
|
||||
NOW(), 0
|
||||
FROM modele_plan_comptable m
|
||||
WHERE m.actif = true;
|
||||
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM pg_trigger
|
||||
WHERE tgname = 'trg_init_plan_comptable_org'
|
||||
AND tgrelid = 'organisations'::regclass
|
||||
) THEN
|
||||
CREATE TRIGGER trg_init_plan_comptable_org
|
||||
AFTER INSERT ON organisations
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION init_plan_comptable_organisation();
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- ============================================================================
|
||||
-- 8. BACKFILL — Initialiser le plan comptable pour les organisations existantes
|
||||
-- (qui ont été créées avant ce trigger)
|
||||
-- ============================================================================
|
||||
INSERT INTO comptes_comptables (
|
||||
id, numero_compte, libelle, classe_comptable, type_compte,
|
||||
description, organisation_id, solde_initial, solde_actuel,
|
||||
compte_collectif, compte_analytique, actif,
|
||||
date_creation, version
|
||||
)
|
||||
SELECT
|
||||
gen_random_uuid(),
|
||||
m.numero_compte,
|
||||
m.libelle,
|
||||
m.classe_comptable,
|
||||
m.type_compte,
|
||||
m.description,
|
||||
o.id,
|
||||
0, 0,
|
||||
false, false, true,
|
||||
NOW(), 0
|
||||
FROM organisations o
|
||||
CROSS JOIN modele_plan_comptable m
|
||||
WHERE m.actif = true
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM comptes_comptables cc
|
||||
WHERE cc.organisation_id = o.id
|
||||
AND cc.numero_compte = m.numero_compte
|
||||
);
|
||||
|
||||
-- ============================================================================
|
||||
-- 9. JOURNAUX STANDARD par organisation
|
||||
-- ============================================================================
|
||||
|
||||
-- Remplacer la contrainte UNIQUE globale sur `code` par une contrainte composite
|
||||
-- (organisation_id, code) — plusieurs orgs peuvent avoir un journal ACH/VTE/etc.
|
||||
DO $$
|
||||
DECLARE
|
||||
constraint_name text;
|
||||
BEGIN
|
||||
SELECT tc.constraint_name INTO constraint_name
|
||||
FROM information_schema.table_constraints tc
|
||||
JOIN information_schema.constraint_column_usage ccu
|
||||
ON tc.constraint_name = ccu.constraint_name
|
||||
WHERE tc.table_name = 'journaux_comptables'
|
||||
AND tc.constraint_type = 'UNIQUE'
|
||||
AND ccu.column_name = 'code'
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.constraint_column_usage ccu2
|
||||
WHERE ccu2.constraint_name = tc.constraint_name
|
||||
AND ccu2.column_name = 'organisation_id'
|
||||
);
|
||||
IF constraint_name IS NOT NULL THEN
|
||||
EXECUTE 'ALTER TABLE journaux_comptables DROP CONSTRAINT ' || quote_ident(constraint_name);
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM pg_constraint
|
||||
WHERE conname = 'uk_journaux_org_code'
|
||||
) THEN
|
||||
ALTER TABLE journaux_comptables
|
||||
ADD CONSTRAINT uk_journaux_org_code UNIQUE (organisation_id, code);
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
INSERT INTO journaux_comptables (
|
||||
id, code, libelle, type_journal, organisation_id,
|
||||
statut, actif, date_creation, version
|
||||
)
|
||||
SELECT
|
||||
gen_random_uuid(),
|
||||
jtype.code,
|
||||
jtype.libelle,
|
||||
jtype.type_journal,
|
||||
o.id,
|
||||
'OUVERT', true, NOW(), 0
|
||||
FROM organisations o
|
||||
CROSS JOIN (VALUES
|
||||
('ACH', 'Journal des achats', 'ACHATS'),
|
||||
('VTE', 'Journal des ventes / cotisations', 'VENTES'),
|
||||
('BQ', 'Journal bancaire', 'BANQUE'),
|
||||
('CAI', 'Journal de caisse', 'CAISSE'),
|
||||
('OD', 'Journal des opérations diverses', 'OD')
|
||||
) AS jtype(code, libelle, type_journal)
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1 FROM journaux_comptables jc
|
||||
WHERE jc.organisation_id = o.id
|
||||
AND jc.type_journal = jtype.type_journal
|
||||
);
|
||||
|
||||
-- ============================================================================
|
||||
-- 10. INDEX utiles
|
||||
-- ============================================================================
|
||||
CREATE INDEX IF NOT EXISTS idx_comptes_org_numero
|
||||
ON comptes_comptables (organisation_id, numero_compte);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_comptes_org_classe
|
||||
ON comptes_comptables (organisation_id, classe_comptable);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_ecritures_org_date
|
||||
ON ecritures_comptables (organisation_id, date_ecriture);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_lignes_compte
|
||||
ON lignes_ecriture (compte_comptable_id);
|
||||
@@ -1,14 +0,0 @@
|
||||
-- ============================================================================
|
||||
-- V37 — Keycloak 26 Organizations : ajout keycloak_org_id sur organisations
|
||||
--
|
||||
-- P0.2 ROADMAP_2026.md — Migration Keycloak 23 → 26 + Organizations natives
|
||||
-- Stocke l'ID Keycloak Organization correspondant à chaque organisation UnionFlow.
|
||||
-- Null = organisation pas encore migrée vers Keycloak 26 Organizations.
|
||||
-- ============================================================================
|
||||
|
||||
ALTER TABLE organisations
|
||||
ADD COLUMN IF NOT EXISTS keycloak_org_id UUID;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_organisations_keycloak_org_id
|
||||
ON organisations (keycloak_org_id)
|
||||
WHERE keycloak_org_id IS NOT NULL;
|
||||
@@ -1,64 +0,0 @@
|
||||
-- ============================================================================
|
||||
-- V38 — Module KYC/AML : table kyc_dossier
|
||||
--
|
||||
-- P1.5 ROADMAP_2026.md — KYC/AML — conformité GIABA/BCEAO LCB-FT
|
||||
-- Rétention 10 ans (GIABA) gérée par colonne annee_reference + archivage planifié.
|
||||
-- ============================================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS kyc_dossier (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
|
||||
-- Identité du membre
|
||||
membre_id UUID NOT NULL REFERENCES utilisateurs(id),
|
||||
|
||||
-- Pièce d'identité
|
||||
type_piece VARCHAR(30) NOT NULL,
|
||||
numero_piece VARCHAR(50) NOT NULL,
|
||||
date_expiration_piece DATE,
|
||||
|
||||
-- Fichiers stockés (MinIO/S3 — identifiants opaques)
|
||||
piece_identite_recto_file_id VARCHAR(500),
|
||||
piece_identite_verso_file_id VARCHAR(500),
|
||||
justif_domicile_file_id VARCHAR(500),
|
||||
|
||||
-- Évaluation risque LCB-FT
|
||||
statut VARCHAR(20) NOT NULL DEFAULT 'NON_VERIFIE',
|
||||
niveau_risque VARCHAR(20) NOT NULL DEFAULT 'FAIBLE',
|
||||
score_risque INTEGER NOT NULL DEFAULT 0
|
||||
CHECK (score_risque >= 0 AND score_risque <= 100),
|
||||
|
||||
-- PEP (Personne Exposée Politiquement)
|
||||
est_pep BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
nationalite VARCHAR(5),
|
||||
|
||||
-- Validation
|
||||
date_verification TIMESTAMP,
|
||||
validateur_id UUID REFERENCES utilisateurs(id),
|
||||
notes_validateur VARCHAR(1000),
|
||||
|
||||
-- Rétention 10 ans GIABA — partitionnement logique par année
|
||||
annee_reference INTEGER NOT NULL DEFAULT EXTRACT(YEAR FROM NOW()),
|
||||
|
||||
-- BaseEntity
|
||||
date_creation TIMESTAMP NOT NULL DEFAULT NOW(),
|
||||
date_modification TIMESTAMP,
|
||||
cree_par VARCHAR(255),
|
||||
modifie_par VARCHAR(255),
|
||||
version BIGINT NOT NULL DEFAULT 0,
|
||||
actif BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
|
||||
CONSTRAINT chk_kyc_annee_reference CHECK (annee_reference >= 2020 AND annee_reference <= 2100)
|
||||
);
|
||||
|
||||
-- Un seul dossier actif par membre (le plus récent est actif, les anciens archivés)
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_kyc_membre_actif
|
||||
ON kyc_dossier (membre_id)
|
||||
WHERE actif = TRUE;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_kyc_membre_id ON kyc_dossier (membre_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_kyc_statut ON kyc_dossier (statut);
|
||||
CREATE INDEX IF NOT EXISTS idx_kyc_niveau_risque ON kyc_dossier (niveau_risque);
|
||||
CREATE INDEX IF NOT EXISTS idx_kyc_est_pep ON kyc_dossier (est_pep) WHERE est_pep = TRUE;
|
||||
CREATE INDEX IF NOT EXISTS idx_kyc_annee ON kyc_dossier (annee_reference);
|
||||
CREATE INDEX IF NOT EXISTS idx_kyc_date_expiration ON kyc_dossier (date_expiration_piece)
|
||||
WHERE date_expiration_piece IS NOT NULL;
|
||||
@@ -1,174 +0,0 @@
|
||||
-- ============================================================================
|
||||
-- V39 — PostgreSQL Row-Level Security : isolation multi-tenant
|
||||
--
|
||||
-- P1.2 ROADMAP_2026.md — Multi-tenancy RLS sur tables tenant-scoped
|
||||
--
|
||||
-- Variables de session :
|
||||
-- app.current_org_id : UUID de l'organisation active (set par RlsConnectionInitializer)
|
||||
-- app.is_super_admin : 'true' si SUPER_ADMIN (bypass RLS pour dashboards globaux)
|
||||
--
|
||||
-- Notes sécurité :
|
||||
-- - Ne pas activer FORCE ROW LEVEL SECURITY ici — le user Flyway (owner) bypasse naturellement.
|
||||
-- - En prod : créer user `unionflow_app` sans BYPASSRLS pour le pool Quarkus.
|
||||
-- - Le user Flyway (`unionflow_admin` ou `postgres`) doit avoir BYPASSRLS ou être owner.
|
||||
-- ============================================================================
|
||||
|
||||
-- ============================================================================
|
||||
-- Helper : policy template pour tables avec organisation_id direct
|
||||
-- ============================================================================
|
||||
|
||||
-- TABLE cotisations
|
||||
ALTER TABLE cotisations ENABLE ROW LEVEL SECURITY;
|
||||
DO $$ BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_policy WHERE polname = 'rls_tenant_cotisations') THEN
|
||||
CREATE POLICY rls_tenant_cotisations ON cotisations
|
||||
USING (
|
||||
organisation_id = current_setting('app.current_org_id', true)::uuid
|
||||
OR current_setting('app.is_super_admin', true) = 'true'
|
||||
);
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- TABLE souscriptions_organisation
|
||||
ALTER TABLE souscriptions_organisation ENABLE ROW LEVEL SECURITY;
|
||||
DO $$ BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_policy WHERE polname = 'rls_tenant_souscriptions') THEN
|
||||
CREATE POLICY rls_tenant_souscriptions ON souscriptions_organisation
|
||||
USING (
|
||||
organisation_id = current_setting('app.current_org_id', true)::uuid
|
||||
OR current_setting('app.is_super_admin', true) = 'true'
|
||||
);
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- TABLE evenements
|
||||
ALTER TABLE evenements ENABLE ROW LEVEL SECURITY;
|
||||
DO $$ BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_policy WHERE polname = 'rls_tenant_evenements') THEN
|
||||
CREATE POLICY rls_tenant_evenements ON evenements
|
||||
USING (
|
||||
organisation_id = current_setting('app.current_org_id', true)::uuid
|
||||
OR current_setting('app.is_super_admin', true) = 'true'
|
||||
);
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- TABLE documents
|
||||
ALTER TABLE documents ENABLE ROW LEVEL SECURITY;
|
||||
DO $$ BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_policy WHERE polname = 'rls_tenant_documents') THEN
|
||||
CREATE POLICY rls_tenant_documents ON documents
|
||||
USING (
|
||||
organisation_id = current_setting('app.current_org_id', true)::uuid
|
||||
OR current_setting('app.is_super_admin', true) = 'true'
|
||||
);
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- TABLE comptes_comptables
|
||||
ALTER TABLE comptes_comptables ENABLE ROW LEVEL SECURITY;
|
||||
DO $$ BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_policy WHERE polname = 'rls_tenant_comptes_comptables') THEN
|
||||
CREATE POLICY rls_tenant_comptes_comptables ON comptes_comptables
|
||||
USING (
|
||||
organisation_id = current_setting('app.current_org_id', true)::uuid
|
||||
OR current_setting('app.is_super_admin', true) = 'true'
|
||||
);
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- TABLE journaux_comptables
|
||||
ALTER TABLE journaux_comptables ENABLE ROW LEVEL SECURITY;
|
||||
DO $$ BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_policy WHERE polname = 'rls_tenant_journaux_comptables') THEN
|
||||
CREATE POLICY rls_tenant_journaux_comptables ON journaux_comptables
|
||||
USING (
|
||||
organisation_id = current_setting('app.current_org_id', true)::uuid
|
||||
OR current_setting('app.is_super_admin', true) = 'true'
|
||||
);
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- TABLE ecritures_comptables
|
||||
ALTER TABLE ecritures_comptables ENABLE ROW LEVEL SECURITY;
|
||||
DO $$ BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_policy WHERE polname = 'rls_tenant_ecritures_comptables') THEN
|
||||
CREATE POLICY rls_tenant_ecritures_comptables ON ecritures_comptables
|
||||
USING (
|
||||
organisation_id = current_setting('app.current_org_id', true)::uuid
|
||||
OR current_setting('app.is_super_admin', true) = 'true'
|
||||
);
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- TABLE kyc_dossier (scoped via membres_organisations JOIN)
|
||||
-- Note : kyc_dossier n'a pas d'organisation_id direct — scope via membre_id + membres_organisations
|
||||
ALTER TABLE kyc_dossier ENABLE ROW LEVEL SECURITY;
|
||||
DO $$ BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_policy WHERE polname = 'rls_tenant_kyc_dossier') THEN
|
||||
CREATE POLICY rls_tenant_kyc_dossier ON kyc_dossier
|
||||
USING (
|
||||
EXISTS (
|
||||
SELECT 1 FROM membres_organisations mo
|
||||
WHERE mo.utilisateur_id = kyc_dossier.membre_id
|
||||
AND mo.organisation_id = current_setting('app.current_org_id', true)::uuid
|
||||
AND mo.actif = true
|
||||
)
|
||||
OR current_setting('app.is_super_admin', true) = 'true'
|
||||
);
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- TABLE membres_organisations (scope par organisation)
|
||||
ALTER TABLE membres_organisations ENABLE ROW LEVEL SECURITY;
|
||||
DO $$ BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_policy WHERE polname = 'rls_tenant_membres_organisations') THEN
|
||||
CREATE POLICY rls_tenant_membres_organisations ON membres_organisations
|
||||
USING (
|
||||
organisation_id = current_setting('app.current_org_id', true)::uuid
|
||||
OR current_setting('app.is_super_admin', true) = 'true'
|
||||
);
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- TABLE budgets
|
||||
ALTER TABLE budgets ENABLE ROW LEVEL SECURITY;
|
||||
DO $$ BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_policy WHERE polname = 'rls_tenant_budgets') THEN
|
||||
CREATE POLICY rls_tenant_budgets ON budgets
|
||||
USING (
|
||||
organisation_id = current_setting('app.current_org_id', true)::uuid
|
||||
OR current_setting('app.is_super_admin', true) = 'true'
|
||||
);
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- TABLE tontines (si applicable)
|
||||
DO $$ BEGIN
|
||||
IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = 'tontines')
|
||||
AND NOT EXISTS (SELECT 1 FROM pg_policy WHERE polname = 'rls_tenant_tontines') THEN
|
||||
EXECUTE 'ALTER TABLE tontines ENABLE ROW LEVEL SECURITY';
|
||||
EXECUTE '
|
||||
CREATE POLICY rls_tenant_tontines ON tontines
|
||||
USING (
|
||||
organisation_id = current_setting(''app.current_org_id'', true)::uuid
|
||||
OR current_setting(''app.is_super_admin'', true) = ''true''
|
||||
)';
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- ============================================================================
|
||||
-- Rôle PostgreSQL applicatif (prod only — commenté pour ne pas casser dev)
|
||||
-- À exécuter manuellement en prod avec le bon mot de passe.
|
||||
-- ============================================================================
|
||||
-- CREATE ROLE unionflow_app LOGIN PASSWORD '<UNIONFLOW_APP_DB_PASSWORD>';
|
||||
-- GRANT CONNECT ON DATABASE unionflow TO unionflow_app;
|
||||
-- GRANT USAGE ON SCHEMA public TO unionflow_app;
|
||||
-- GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO unionflow_app;
|
||||
-- GRANT USAGE ON ALL SEQUENCES IN SCHEMA public TO unionflow_app;
|
||||
-- -- unionflow_app N'A PAS BYPASSRLS — RLS s'applique toujours
|
||||
--
|
||||
-- CREATE ROLE unionflow_admin LOGIN PASSWORD '<UNIONFLOW_ADMIN_DB_PASSWORD>' BYPASSRLS;
|
||||
-- GRANT ALL ON ALL TABLES IN SCHEMA public TO unionflow_admin;
|
||||
-- GRANT ALL ON ALL SEQUENCES IN SCHEMA public TO unionflow_admin;
|
||||
-- -- unionflow_admin utilisé par Flyway et SuperAdminCrossTenantService
|
||||
@@ -1,9 +0,0 @@
|
||||
-- V40: Ajout du provider de paiement par défaut sur FormuleAbonnement
|
||||
-- Permet de configurer le provider (WAVE, ORANGE_MONEY, MTN_MOMO, PISPI) par formule
|
||||
-- NULL = utiliser le provider global configuré dans application.properties
|
||||
|
||||
ALTER TABLE formules_abonnement
|
||||
ADD COLUMN IF NOT EXISTS provider_defaut VARCHAR(20);
|
||||
|
||||
COMMENT ON COLUMN formules_abonnement.provider_defaut IS
|
||||
'Code du provider de paiement par défaut pour cette formule (WAVE, ORANGE_MONEY, MTN_MOMO, PISPI). NULL = provider global.';
|
||||
@@ -1,12 +0,0 @@
|
||||
-- V41: Token FCM (Firebase Cloud Messaging) pour les notifications push mobile
|
||||
-- Nullable : vide si le membre n'a pas installé l'app mobile ou refusé les notifications
|
||||
-- Table : utilisateurs (entité Membre.java → @Table(name = "utilisateurs"))
|
||||
|
||||
ALTER TABLE utilisateurs
|
||||
ADD COLUMN IF NOT EXISTS fcm_token VARCHAR(500);
|
||||
|
||||
COMMENT ON COLUMN utilisateurs.fcm_token IS
|
||||
'Token FCM pour les notifications push Firebase. NULL si non enregistré.';
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_utilisateurs_fcm_token
|
||||
ON utilisateurs (fcm_token) WHERE fcm_token IS NOT NULL;
|
||||
@@ -1,41 +0,0 @@
|
||||
-- V42: Créer les rôles PostgreSQL pour l'isolation RLS
|
||||
-- unionflow_app : rôle applicatif (sans BYPASSRLS) — utilisé en prod par le backend
|
||||
-- unionflow_admin: rôle administrateur (BYPASSRLS) — utilisé pour les migrations Flyway et les ops DBA
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
-- Rôle applicatif (sans bypass RLS — soumis aux policies)
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'unionflow_app') THEN
|
||||
CREATE ROLE unionflow_app LOGIN PASSWORD 'CHANGE_ME_APP_PASSWORD';
|
||||
END IF;
|
||||
|
||||
-- Rôle administrateur (bypass RLS — pour Flyway, exports, audits DBA)
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'unionflow_admin') THEN
|
||||
CREATE ROLE unionflow_admin LOGIN PASSWORD 'CHANGE_ME_ADMIN_PASSWORD' BYPASSRLS;
|
||||
END IF;
|
||||
END
|
||||
$$;
|
||||
|
||||
-- Accorder les privilèges sur le schéma public
|
||||
GRANT USAGE ON SCHEMA public TO unionflow_app, unionflow_admin;
|
||||
|
||||
-- unionflow_app : DML uniquement (pas DDL)
|
||||
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO unionflow_app;
|
||||
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO unionflow_app;
|
||||
|
||||
-- unionflow_admin : tous les droits (DDL inclus pour Flyway)
|
||||
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO unionflow_admin;
|
||||
GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO unionflow_admin;
|
||||
|
||||
-- Garantir les droits sur les objets créés ultérieurement (nouvelles tables Flyway)
|
||||
ALTER DEFAULT PRIVILEGES IN SCHEMA public
|
||||
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO unionflow_app;
|
||||
ALTER DEFAULT PRIVILEGES IN SCHEMA public
|
||||
GRANT USAGE, SELECT ON SEQUENCES TO unionflow_app;
|
||||
ALTER DEFAULT PRIVILEGES IN SCHEMA public
|
||||
GRANT ALL PRIVILEGES ON TABLES TO unionflow_admin;
|
||||
ALTER DEFAULT PRIVILEGES IN SCHEMA public
|
||||
GRANT ALL PRIVILEGES ON SEQUENCES TO unionflow_admin;
|
||||
|
||||
COMMENT ON ROLE unionflow_app IS 'Rôle applicatif UnionFlow — soumis aux policies RLS tenant isolation';
|
||||
COMMENT ON ROLE unionflow_admin IS 'Rôle DBA UnionFlow — BYPASSRLS pour Flyway et exports';
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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": []
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Bienvenue sur UnionFlow</title>
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; background: #f4f6f9; margin: 0; padding: 0; }
|
||||
.container { max-width: 600px; margin: 30px auto; background: #fff; border-radius: 8px; overflow: hidden; box-shadow: 0 2px 8px rgba(0,0,0,.1); }
|
||||
.header { background: #1A568C; color: #fff; padding: 28px 32px; }
|
||||
.header h1 { margin: 0; font-size: 22px; }
|
||||
.body { padding: 28px 32px; color: #333; line-height: 1.6; }
|
||||
.btn { display: inline-block; margin-top: 20px; padding: 12px 28px; background: #1A568C; color: #fff; text-decoration: none; border-radius: 5px; font-weight: bold; }
|
||||
.footer { background: #f4f6f9; text-align: center; padding: 16px; font-size: 12px; color: #999; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<h1>🎉 Bienvenue sur UnionFlow !</h1>
|
||||
</div>
|
||||
<div class="body">
|
||||
<p>Bonjour <strong>{prenom} {nom}</strong>,</p>
|
||||
<p>Votre compte a été créé avec succès sur <strong>UnionFlow</strong>, la plateforme de gestion des mutuelles, coopératives et syndicats de Côte d'Ivoire.</p>
|
||||
|
||||
<p>Vous faites maintenant partie de l'organisation : <strong>{nomOrganisation}</strong></p>
|
||||
|
||||
<p>Votre identifiant de connexion est votre adresse email : <strong>{email}</strong></p>
|
||||
|
||||
{#if lienConnexion}
|
||||
<p>
|
||||
<a href="{lienConnexion}" class="btn">Accéder à mon espace</a>
|
||||
</p>
|
||||
{/if}
|
||||
|
||||
<p>En cas de question, contactez votre administrateur ou notre support : <a href="mailto:support@lions.dev">support@lions.dev</a></p>
|
||||
|
||||
<p>Cordialement,<br>L'équipe UnionFlow</p>
|
||||
</div>
|
||||
<div class="footer">UnionFlow © 2026 — Lions Tech SARL — Abidjan, Côte d'Ivoire</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,47 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Confirmation de cotisation</title>
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; background: #f4f6f9; margin: 0; padding: 0; }
|
||||
.container { max-width: 600px; margin: 30px auto; background: #fff; border-radius: 8px; overflow: hidden; box-shadow: 0 2px 8px rgba(0,0,0,.1); }
|
||||
.header { background: #1A568C; color: #fff; padding: 28px 32px; }
|
||||
.header h1 { margin: 0; font-size: 22px; }
|
||||
.body { padding: 28px 32px; color: #333; line-height: 1.6; }
|
||||
.receipt { background: #f8faff; border: 1px solid #dce8f8; border-radius: 6px; padding: 18px; margin: 18px 0; }
|
||||
.receipt table { width: 100%; border-collapse: collapse; }
|
||||
.receipt td { padding: 7px 0; }
|
||||
.receipt td:last-child { text-align: right; font-weight: bold; }
|
||||
.amount { font-size: 24px; font-weight: bold; color: #1A568C; }
|
||||
.badge-success { display: inline-block; background: #e6f4ea; color: #2e7d32; padding: 4px 12px; border-radius: 20px; font-size: 13px; font-weight: bold; }
|
||||
.footer { background: #f4f6f9; text-align: center; padding: 16px; font-size: 12px; color: #999; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<h1>✅ Cotisation confirmée</h1>
|
||||
</div>
|
||||
<div class="body">
|
||||
<p>Bonjour <strong>{prenom} {nom}</strong>,</p>
|
||||
<p>Nous avons bien reçu votre cotisation. <span class="badge-success">CONFIRMÉ</span></p>
|
||||
|
||||
<div class="receipt">
|
||||
<table>
|
||||
<tr><td>Organisation</td><td>{nomOrganisation}</td></tr>
|
||||
<tr><td>Période</td><td>{periode}</td></tr>
|
||||
<tr><td>Référence</td><td>{numeroReference}</td></tr>
|
||||
<tr><td>Mode de paiement</td><td>{methodePaiement}</td></tr>
|
||||
<tr><td>Date de paiement</td><td>{datePaiement}</td></tr>
|
||||
<tr><td>Montant</td><td><span class="amount">{montant} XOF</span></td></tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<p>Conservez cet email comme justificatif de paiement.</p>
|
||||
<p>Cordialement,<br>L'équipe UnionFlow</p>
|
||||
</div>
|
||||
<div class="footer">UnionFlow © 2026 — Lions Tech SARL — Abidjan, Côte d'Ivoire</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,45 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Rappel de cotisation</title>
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; background: #f4f6f9; margin: 0; padding: 0; }
|
||||
.container { max-width: 600px; margin: 30px auto; background: #fff; border-radius: 8px; overflow: hidden; box-shadow: 0 2px 8px rgba(0,0,0,.1); }
|
||||
.header { background: #e65100; color: #fff; padding: 28px 32px; }
|
||||
.header h1 { margin: 0; font-size: 22px; }
|
||||
.body { padding: 28px 32px; color: #333; line-height: 1.6; }
|
||||
.alert { background: #fff3e0; border-left: 4px solid #e65100; padding: 14px 18px; border-radius: 4px; margin: 18px 0; }
|
||||
.btn { display: inline-block; margin-top: 16px; padding: 12px 28px; background: #e65100; color: #fff; text-decoration: none; border-radius: 5px; font-weight: bold; }
|
||||
.footer { background: #f4f6f9; text-align: center; padding: 16px; font-size: 12px; color: #999; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<h1>⚠️ Rappel de cotisation</h1>
|
||||
</div>
|
||||
<div class="body">
|
||||
<p>Bonjour <strong>{prenom} {nom}</strong>,</p>
|
||||
|
||||
<div class="alert">
|
||||
<strong>Votre cotisation pour la période {periode} est en attente de paiement.</strong>
|
||||
</div>
|
||||
|
||||
<p>Organisation : <strong>{nomOrganisation}</strong></p>
|
||||
<p>Montant dû : <strong>{montant} XOF</strong></p>
|
||||
<p>Date limite : <strong>{dateLimite}</strong></p>
|
||||
|
||||
{#if lienPaiement}
|
||||
<p>
|
||||
<a href="{lienPaiement}" class="btn">Payer ma cotisation</a>
|
||||
</p>
|
||||
{/if}
|
||||
|
||||
<p>Si vous avez déjà effectué ce paiement, veuillez ignorer ce message ou contacter votre trésorier.</p>
|
||||
<p>Cordialement,<br>L'équipe UnionFlow</p>
|
||||
</div>
|
||||
<div class="footer">UnionFlow © 2026 — Lions Tech SARL — Abidjan, Côte d'Ivoire</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,50 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Souscription confirmée</title>
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; background: #f4f6f9; margin: 0; padding: 0; }
|
||||
.container { max-width: 600px; margin: 30px auto; background: #fff; border-radius: 8px; overflow: hidden; box-shadow: 0 2px 8px rgba(0,0,0,.1); }
|
||||
.header { background: #1A568C; color: #fff; padding: 28px 32px; }
|
||||
.header h1 { margin: 0; font-size: 22px; }
|
||||
.body { padding: 28px 32px; color: #333; line-height: 1.6; }
|
||||
.plan-card { background: #e8f0fe; border-radius: 8px; padding: 20px; margin: 18px 0; text-align: center; }
|
||||
.plan-name { font-size: 20px; font-weight: bold; color: #1A568C; }
|
||||
.plan-price { font-size: 28px; font-weight: bold; color: #1A568C; margin: 8px 0; }
|
||||
.features { margin: 16px 0; }
|
||||
.features li { padding: 4px 0; }
|
||||
.badge { display: inline-block; background: #e6f4ea; color: #2e7d32; padding: 4px 12px; border-radius: 20px; font-size: 13px; font-weight: bold; }
|
||||
.footer { background: #f4f6f9; text-align: center; padding: 16px; font-size: 12px; color: #999; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<h1>✅ Souscription activée</h1>
|
||||
</div>
|
||||
<div class="body">
|
||||
<p>Bonjour <strong>{nomAdministrateur}</strong>,</p>
|
||||
<p>La souscription de votre organisation <strong>{nomOrganisation}</strong> a été activée avec succès. <span class="badge">ACTIF</span></p>
|
||||
|
||||
<div class="plan-card">
|
||||
<div class="plan-name">Plan {nomFormule}</div>
|
||||
<div class="plan-price">{montant} XOF / {periodicite}</div>
|
||||
</div>
|
||||
|
||||
<p><strong>Détails de la souscription :</strong></p>
|
||||
<ul class="features">
|
||||
<li>Date d'activation : {dateActivation}</li>
|
||||
<li>Date d'expiration : {dateExpiration}</li>
|
||||
<li>Membres maximum : {maxMembres}</li>
|
||||
<li>Stockage : {maxStockageMo} Mo</li>
|
||||
{#if apiAccess}<li>✓ Accès API REST</li>{/if}
|
||||
{#if supportPrioritaire}<li>✓ Support prioritaire</li>{/if}
|
||||
</ul>
|
||||
|
||||
<p>Cordialement,<br>L'équipe UnionFlow</p>
|
||||
</div>
|
||||
<div class="footer">UnionFlow © 2026 — Lions Tech SARL — Abidjan, Côte d'Ivoire</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user