From 80f24dce5a7f7ceb4ac76cc3ee0197cd4b3eeb9c Mon Sep 17 00:00:00 2001 From: 951095 Date: Fri, 19 Sep 2025 10:22:34 +0200 Subject: [PATCH] ajout de script / modif d'enum et simplification du lancement en local --- .gitignore | 1 + README.md | 5 +- bdd/data_test/assistantes_maternelles.csv | 6 +- bdd/data_test/contrats.csv | 4 +- bdd/data_test/dossiers.csv | 4 +- bdd/data_test/enfants.csv | 20 +- bdd/data_test/enfants_parents.csv | 18 +- bdd/data_test/evenements.csv | 4 +- bdd/data_test/notifications.csv | 4 +- bdd/data_test/parents.csv | 10 +- bdd/data_test/uploads.csv | 4 +- bdd/data_test/utilisateurs.csv | 24 +-- bdd/data_test/validations.csv | 8 +- docker-compose.dev.yml | 18 +- docs/ENUMS.md | 189 +++++++++--------- docs/FK_POLICIES.md | 212 +++++++++++---------- makefile | 72 +++++++ migrations/02_indexes.sql | 196 +++++-------------- migrations/03_checks.sql | 182 ++++++------------ migrations/05_triggers.sql | 217 +++++++++------------ scripts/clean_csv.py | 59 ++++++ scripts/verify_data.sql | 64 +++++++ seed/02_seed.sql | 221 ---------------------- tests/sql/verify.sql | 42 +--- verify.log | 188 ++++++++++++++++++ 25 files changed, 849 insertions(+), 923 deletions(-) create mode 100644 makefile create mode 100755 scripts/clean_csv.py create mode 100644 scripts/verify_data.sql delete mode 100644 seed/02_seed.sql create mode 100644 verify.log diff --git a/.gitignore b/.gitignore index 4c49bd7..3c19dba 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ .env +PtitsPas_CDC.pdf \ No newline at end of file diff --git a/README.md b/README.md index 4ebcd90..08a53c1 100644 --- a/README.md +++ b/README.md @@ -29,18 +29,17 @@ Ce projet contient la **base de données** pour l'application PtitsPas, avec scr Dans le terminal, depuis le dossier du projet : ```bash -docker compose -f docker-compose.dev.yml up -d +make demo ``` Pour arrêter et supprimer les volumes : ```bash -docker compose -f docker-compose.dev.yml down -v +make stop ``` --- - ## Importation automatique des données de test Les données de test (CSV) sont automatiquement importées dans la base au démarrage du conteneur Docker grâce aux scripts présents dans le dossier `migrations/`. diff --git a/bdd/data_test/assistantes_maternelles.csv b/bdd/data_test/assistantes_maternelles.csv index 6403aba..e6d725e 100644 --- a/bdd/data_test/assistantes_maternelles.csv +++ b/bdd/data_test/assistantes_maternelles.csv @@ -1,3 +1,3 @@ -"id_utilisateur","numero_agrement","nir_chiffre","nb_max_enfants","biographie","disponible","ville_residence","date_agrement","annee_experience","specialite","place_disponible" -"a1f3d5c7-8b9a-4e2f-9c1d-3b2a4f6e7d8c","AGR5678",,3,"Agrément 3 enfants - Spécialité 1-3 ans - 1 place disponible",True,,"2010-09-01",14,"1-3 ans",1 -"d9c2e3f4-5b6a-4c3d-9f1a-2e7b3c5d8a1f","AGR1234",,4,"Agrément 4 enfants - Spécialité bébés 0-18 mois - 2 places disponibles",True,,"2005-06-15",18,"Bébés 0-18 mois",2 +id_utilisateur,numero_agrement,nir_chiffre,nb_max_enfants,biographie,disponible,ville_residence,date_agrement,annee_experience,specialite,place_disponible +a1f3d5c7-8b9a-4e2f-9c1d-3b2a4f6e7d8c,AGR5678,,3,Agrément 3 enfants - Spécialité 1-3 ans - 1 place disponible,True,,2010-09-01,14,1-3 ans,1 +d9c2e3f4-5b6a-4c3d-9f1a-2e7b3c5d8a1f,AGR1234,,4,Agrément 4 enfants - Spécialité bébés 0-18 mois - 2 places disponibles,True,,2005-06-15,18,Bébés 0-18 mois,2 diff --git a/bdd/data_test/contrats.csv b/bdd/data_test/contrats.csv index ad9ff5d..201a713 100644 --- a/bdd/data_test/contrats.csv +++ b/bdd/data_test/contrats.csv @@ -1,2 +1,2 @@ -"id","id_dossier","planning","tarif_horaire","indemnites_repas","date_debut","statut","signe_parent","signe_am","finalise_le","cree_le","modifie_le" -"f09c6ffa-4627-4aa8-b20b-829c2c828f0d","bb9c30a0-60b4-4832-9947-8a7d2366673d","{""jours"": [""Lundi"", ""Mardi"", ""Mercredi""]}","10.50","4.50","2024-09-01","brouillon",True,False,,"2025-09-09 11:05:47.933418+00","2025-09-09 11:05:47.933418+00" +id,id_dossier,planning,tarif_horaire,indemnites_repas,date_debut,statut,signe_parent,signe_am,finalise_le,cree_le,modifie_le +f09c6ffa-4627-4aa8-b20b-829c2c828f0d,bb9c30a0-60b4-4832-9947-8a7d2366673d,"{""jours"": [""Lundi"", ""Mardi"", ""Mercredi""]}",10.50,4.50,2024-09-01,brouillon,True,False,,2025-09-09 11:05:47.933418+00,2025-09-09 11:05:47.933418+00 diff --git a/bdd/data_test/dossiers.csv b/bdd/data_test/dossiers.csv index 8088f6d..ebc8f26 100644 --- a/bdd/data_test/dossiers.csv +++ b/bdd/data_test/dossiers.csv @@ -1,2 +1,2 @@ -"id","id_parent","id_enfant","presentation","type_contrat","repas","budget","planning_souhaite","statut","cree_le","modifie_le" -"bb9c30a0-60b4-4832-9947-8a7d2366673d","f1d3c5b7-8a9e-4f2d-9c1b-3e7a5d8c2f1b","5e8574b7-63e6-4d48-9af3-8d3bf7a6a6cf","Contrat test pour garde à temps plein","temps_plein",False,"1200.00",,"envoye","2025-09-09 10:58:28.718654+00","2025-09-09 10:58:28.718654+00" +id,id_parent,id_enfant,presentation,type_contrat,repas,budget,planning_souhaite,statut,cree_le,modifie_le +bb9c30a0-60b4-4832-9947-8a7d2366673d,f1d3c5b7-8a9e-4f2d-9c1b-3e7a5d8c2f1b,5e8574b7-63e6-4d48-9af3-8d3bf7a6a6cf,Contrat test pour garde à temps plein,temps_plein,False,1200.00,,envoye,2025-09-09 10:58:28.718654+00,2025-09-09 10:58:28.718654+00 diff --git a/bdd/data_test/enfants.csv b/bdd/data_test/enfants.csv index 1cf2ab6..651ebf9 100644 --- a/bdd/data_test/enfants.csv +++ b/bdd/data_test/enfants.csv @@ -1,10 +1,10 @@ -"id","statut","prenom","nom","genre","date_naissance","date_prevue_naissance","photo_url","consentement_photo","date_consentement_photo","est_multiple" -"5e8574b7-63e6-4d48-9af3-8d3bf7a6a6cf","actif","Emma","Dupont","F","2020-06-01",,,False,,False -"a5c3268e-07eb-41a4-9f6c-2f9f16f37c3d","actif",,,,"2020-01-01","2025-01-01",,False,,False -"e1a2b3c4-d5e6-4f7a-8b9c-1d2e3f4a5b6c","actif","Emma","Martin",,"2023-02-15",,,False,,False -"e2b3c4d5-e6f7-4a8b-9c1d-2e3f4a5b6c7d","actif","Noah","Martin",,"2023-02-15",,,False,,False -"e3c4d5e6-f7a8-4b9c-1d2e-3f4a5b6c7d8e","actif","Léa","Martin",,"2023-02-15",,,False,,False -"e4d5e6f7-a8b9-4c1d-2e3f-4a5b6c7d8e9f","actif","Chloé","Rousseau",,"2022-04-20",,,False,,False -"e5e6f7a8-b9c1-4d2e-3f4a-5b6c7d8e9f1a","actif","Hugo","Rousseau",,"2024-03-10",,,False,,False -"e6f7a8b9-c1d2-4e3f-5a6b-7c8d9e0f1a2b","actif","Maxime","Lecomte",,"2023-04-15",,,False,,False -"edd19cd1-bb67-4f14-8a37-c66b75c94537","scolarise","Lucas","Durand","H","2018-09-15",,,False,,False +id,statut,prenom,nom,genre,date_naissance,date_prevue_naissance,photo_url,consentement_photo,date_consentement_photo,est_multiple +5e8574b7-63e6-4d48-9af3-8d3bf7a6a6cf,actif,Emma,Dupont,F,2020-06-01,,,False,,False +a5c3268e-07eb-41a4-9f6c-2f9f16f37c3d,actif,,,,2020-01-01,2025-01-01,,False,,False +e1a2b3c4-d5e6-4f7a-8b9c-1d2e3f4a5b6c,actif,Emma,Martin,,2023-02-15,,,False,,False +e2b3c4d5-e6f7-4a8b-9c1d-2e3f4a5b6c7d,actif,Noah,Martin,,2023-02-15,,,False,,False +e3c4d5e6-f7a8-4b9c-1d2e-3f4a5b6c7d8e,actif,Léa,Martin,,2023-02-15,,,False,,False +e4d5e6f7-a8b9-4c1d-2e3f-4a5b6c7d8e9f,actif,Chloé,Rousseau,,2022-04-20,,,False,,False +e5e6f7a8-b9c1-4d2e-3f4a-5b6c7d8e9f1a,actif,Hugo,Rousseau,,2024-03-10,,,False,,False +e6f7a8b9-c1d2-4e3f-5a6b-7c8d9e0f1a2b,actif,Maxime,Lecomte,,2023-04-15,,,False,,False +edd19cd1-bb67-4f14-8a37-c66b75c94537,scolarise,Lucas,Durand,H,2018-09-15,,,False,,False diff --git a/bdd/data_test/enfants_parents.csv b/bdd/data_test/enfants_parents.csv index 910c800..e1bceb8 100644 --- a/bdd/data_test/enfants_parents.csv +++ b/bdd/data_test/enfants_parents.csv @@ -1,9 +1,9 @@ -"id_parent","id_enfant" -"b6c4d2e3-5f7a-4b8c-9d1e-2a3c5f7b8d9e","e4d5e6f7-a8b9-4c1d-2e3f-4a5b6c7d8e9f" -"b6c4d2e3-5f7a-4b8c-9d1e-2a3c5f7b8d9e","e5e6f7a8-b9c1-4d2e-3f4a-5b6c7d8e9f1a" -"c4e2d1f5-6b7a-4c3d-8f2a-1e9c3b5a7d6f","e1a2b3c4-d5e6-4f7a-8b9c-1d2e3f4a5b6c" -"c4e2d1f5-6b7a-4c3d-8f2a-1e9c3b5a7d6f","e2b3c4d5-e6f7-4a8b-9c1d-2e3f4a5b6c7d" -"c4e2d1f5-6b7a-4c3d-8f2a-1e9c3b5a7d6f","e3c4d5e6-f7a8-4b9c-1d2e-3f4a5b6c7d8e" -"d3e5f7a9-1c2b-4d6e-8f3a-2b4c6d8e9f1a","e6f7a8b9-c1d2-4e3f-5a6b-7c8d9e0f1a2b" -"f1d3c5b7-8a9e-4f2d-9c1b-3e7a5d8c2f1b","e4d5e6f7-a8b9-4c1d-2e3f-4a5b6c7d8e9f" -"f1d3c5b7-8a9e-4f2d-9c1b-3e7a5d8c2f1b","e5e6f7a8-b9c1-4d2e-3f4a-5b6c7d8e9f1a" +id_parent,id_enfant +b6c4d2e3-5f7a-4b8c-9d1e-2a3c5f7b8d9e,e4d5e6f7-a8b9-4c1d-2e3f-4a5b6c7d8e9f +b6c4d2e3-5f7a-4b8c-9d1e-2a3c5f7b8d9e,e5e6f7a8-b9c1-4d2e-3f4a-5b6c7d8e9f1a +c4e2d1f5-6b7a-4c3d-8f2a-1e9c3b5a7d6f,e1a2b3c4-d5e6-4f7a-8b9c-1d2e3f4a5b6c +c4e2d1f5-6b7a-4c3d-8f2a-1e9c3b5a7d6f,e2b3c4d5-e6f7-4a8b-9c1d-2e3f4a5b6c7d +c4e2d1f5-6b7a-4c3d-8f2a-1e9c3b5a7d6f,e3c4d5e6-f7a8-4b9c-1d2e-3f4a5b6c7d8e +d3e5f7a9-1c2b-4d6e-8f3a-2b4c6d8e9f1a,e6f7a8b9-c1d2-4e3f-5a6b-7c8d9e0f1a2b +f1d3c5b7-8a9e-4f2d-9c1b-3e7a5d8c2f1b,e4d5e6f7-a8b9-4c1d-2e3f-4a5b6c7d8e9f +f1d3c5b7-8a9e-4f2d-9c1b-3e7a5d8c2f1b,e5e6f7a8-b9c1-4d2e-3f4a-5b6c7d8e9f1a diff --git a/bdd/data_test/evenements.csv b/bdd/data_test/evenements.csv index 1a63298..43edd4d 100644 --- a/bdd/data_test/evenements.csv +++ b/bdd/data_test/evenements.csv @@ -1,2 +1,2 @@ -"id","type","id_enfant","id_am","id_parent","cree_par","date_debut","date_fin","commentaires","statut","delai_grace","urgent","cree_le","modifie_le" -"9f09425c-a374-4c9f-b3b1-be7258b60cd3","absence_enfant","5e8574b7-63e6-4d48-9af3-8d3bf7a6a6cf","62de8e71-8082-4383-a3a2-4277bdd07516",,"bbcae75c-0e60-4b84-b281-079dba23b44e","2024-11-10 00:00:00+00","2024-11-11 00:00:00+00","Enfant malade","propose",,True,"2025-09-02 12:55:43.781467+00","2025-09-05 14:39:38.390126+00" +id,type,id_enfant,id_am,id_parent,cree_par,date_debut,date_fin,commentaires,statut,delai_grace,urgent,cree_le,modifie_le +9f09425c-a374-4c9f-b3b1-be7258b60cd3,absence_enfant,5e8574b7-63e6-4d48-9af3-8d3bf7a6a6cf,62de8e71-8082-4383-a3a2-4277bdd07516,,bbcae75c-0e60-4b84-b281-079dba23b44e,2024-11-10 00:00:00+00,2024-11-11 00:00:00+00,Enfant malade,propose,,True,2025-09-02 12:55:43.781467+00,2025-09-05 14:39:38.390126+00 diff --git a/bdd/data_test/notifications.csv b/bdd/data_test/notifications.csv index 7a177c1..068f9e5 100644 --- a/bdd/data_test/notifications.csv +++ b/bdd/data_test/notifications.csv @@ -1,2 +1,2 @@ -"id","id_utilisateur","contenu","lu","cree_le" -"71e90c37-f2cb-4aff-ad34-1c728f620afb","bbcae75c-0e60-4b84-b281-079dba23b44e","Votre dossier a été accepté",False,"2025-09-02 12:57:42.845264+00" +id,id_utilisateur,contenu,lu,cree_le +71e90c37-f2cb-4aff-ad34-1c728f620afb,bbcae75c-0e60-4b84-b281-079dba23b44e,Votre dossier a été accepté,False,2025-09-02 12:57:42.845264+00 diff --git a/bdd/data_test/parents.csv b/bdd/data_test/parents.csv index c3d98f7..f1b2d4f 100644 --- a/bdd/data_test/parents.csv +++ b/bdd/data_test/parents.csv @@ -1,5 +1,5 @@ -"id_utilisateur","id_co_parent" -"b6c4d2e3-5f7a-4b8c-9d1e-2a3c5f7b8d9e","f1d3c5b7-8a9e-4f2d-9c1b-3e7a5d8c2f1b" -"c4e2d1f5-6b7a-4c3d-8f2a-1e9c3b5a7d6f", -"d3e5f7a9-1c2b-4d6e-8f3a-2b4c6d8e9f1a", -"f1d3c5b7-8a9e-4f2d-9c1b-3e7a5d8c2f1b","b6c4d2e3-5f7a-4b8c-9d1e-2a3c5f7b8d9e" +id_utilisateur,id_co_parent +b6c4d2e3-5f7a-4b8c-9d1e-2a3c5f7b8d9e,f1d3c5b7-8a9e-4f2d-9c1b-3e7a5d8c2f1b +c4e2d1f5-6b7a-4c3d-8f2a-1e9c3b5a7d6f, +d3e5f7a9-1c2b-4d6e-8f3a-2b4c6d8e9f1a, +f1d3c5b7-8a9e-4f2d-9c1b-3e7a5d8c2f1b,b6c4d2e3-5f7a-4b8c-9d1e-2a3c5f7b8d9e diff --git a/bdd/data_test/uploads.csv b/bdd/data_test/uploads.csv index 25709b6..313002d 100644 --- a/bdd/data_test/uploads.csv +++ b/bdd/data_test/uploads.csv @@ -1,2 +1,2 @@ -"id","id_utilisateur","fichier_url","type","cree_le" -"db1eb36d-5f30-4027-b529-1d972b79180a","bbcae75c-0e60-4b84-b281-079dba23b44e","https://placeholder.local/file","image","2025-09-02 12:57:35.140078+00" +id,id_utilisateur,fichier_url,type,cree_le +db1eb36d-5f30-4027-b529-1d972b79180a,bbcae75c-0e60-4b84-b281-079dba23b44e,https://placeholder.local/file,image,2025-09-02 12:57:35.140078+00 diff --git a/bdd/data_test/utilisateurs.csv b/bdd/data_test/utilisateurs.csv index c4539ce..37d440a 100644 --- a/bdd/data_test/utilisateurs.csv +++ b/bdd/data_test/utilisateurs.csv @@ -1,12 +1,12 @@ -"id","email","password","prenom","nom","genre","role","statut","telephone","adresse","photo_url","consentement_photo","date_consentement_photo","changement_mdp_obligatoire","cree_le","modifie_le","ville","code_postal","mobile","telephone_fixe","profession","situation_familiale","date_naissance" -"62de8e71-8082-4383-a3a2-4277bdd07516","am1@example.com","hash125","Claire","Martin","F","assistante_maternelle","actif","0609091011","5 place Bellecour",,False,,False,"2025-09-02 12:30:48.724463+00","2025-09-02 12:30:48.724463+00","Lyon","69002",,,,, -"76c40571-5da6-4d27-8e07-303185875b36","gest1@example.com","hash126","Paul","Lemoine","H","gestionnaire","actif","0612131415","10 rue Victor Hugo",,False,,False,"2025-09-02 12:30:48.724463+00","2025-09-02 12:30:48.724463+00","Paris","75002",,,,, -"9bc30373-91a4-45ed-9c05-ffe1651ad906","coparent@example.com","hash124","Marc","Durand","H","parent","actif","0605060708","45 avenue de Lyon",,False,,False,"2025-09-02 12:30:48.724463+00","2025-09-02 12:30:48.724463+00","Lyon","69000",,,,, -"a1f3d5c7-8b9a-4e2f-9c1d-3b2a4f6e7d8c","fatima.elmansouri@ptits-pas.fr","password","Fatima","El Mansouri",,"assistante_maternelle","actif",,"17 Boulevard Aristide Briand",,False,,False,"2025-09-04 11:49:22.636003+00","2025-09-04 11:49:22.636003+00","Bezons","95870","06 75 45 67 89","01 39 98 78 90","Assistante maternelle","marie","1975-11-12" -"b6c4d2e3-5f7a-4b8c-9d1e-2a3c5f7b8d9e","julien.rousseau@ptits-pas.fr","password","Julien","Rousseau",,"parent","actif",,"14 Rue Pasteur",,False,,False,"2025-09-04 11:49:22.636003+00","2025-09-04 11:49:22.636003+00","Bezons","95870","06 56 67 78 89","01 39 98 01 23","Commercial","divorce","1985-08-29" -"bbcae75c-0e60-4b84-b281-079dba23b44e","parent1@example.com","hash123","Alice","Dupont","F","parent","actif","0601020304","12 rue de Paris",,False,,False,"2025-09-02 12:30:48.724463+00","2025-09-02 12:30:48.724463+00","Paris","75001",,,,, -"c4e2d1f5-6b7a-4c3d-8f2a-1e9c3b5a7d6f","claire.martin@ptits-pas.fr","password","Claire","Martin",,"parent","actif",,"5 Avenue du Général de Gaulle",,False,,False,"2025-09-04 11:49:22.636003+00","2025-09-04 11:49:22.636003+00","Bezons","95870","06 89 56 78 90","01 39 98 89 01","Infirmière","marie","1990-04-03" -"d3e5f7a9-1c2b-4d6e-8f3a-2b4c6d8e9f1a","david.lecomte@ptits-pas.fr","password","David","Lecomte",,"parent","actif",,"31 Rue Émile Zola",,False,,False,"2025-09-04 11:49:22.636003+00","2025-09-04 11:49:22.636003+00","Bezons","95870","06 45 56 67 78","01 39 98 12 34","Développeur web","celibataire","1992-10-07" -"d9c2e3f4-5b6a-4c3d-9f1a-2e7b3c5d8a1f","marie.dubois@ptits-pas.fr","password","Marie","Dubois",,"assistante_maternelle","actif",,"25 Rue de la République",,False,,False,"2025-09-04 11:49:22.636003+00","2025-09-04 12:44:51.654512+00","Bezons","95870","06 96 34 56 78","01 39 98 67 89","Assistante maternelle","marie","1980-06-08" -"f1d3c5b7-8a9e-4f2d-9c1b-3e7a5d8c2f1b","amelie.durand@ptits-pas.fr","password","Amélie","Durand",,"parent","actif",,"23 Rue Victor Hugo",,False,,False,"2025-09-04 11:49:22.636003+00","2025-09-04 11:49:22.636003+00","Bezons","95870","06 67 78 89 90","01 39 98 90 12","Comptable","divorce","1987-12-14" -"f3b1a2d4-1c7e-4c2f-8b1a-9d3a8e2f5b6c","sophie.bernard@ptits-pas.fr","password","Sophie","Bernard",,"administrateur","actif",,"12 Avenue Gabriel Péri",,False,,False,"2025-09-04 11:49:22.636003+00","2025-09-04 11:49:22.636003+00","Bezons","95870","06 78 12 34 56","01 39 98 45 67","Responsable administrative","marie","1978-03-15" +id,email,password,prenom,nom,genre,role,statut,telephone,adresse,photo_url,consentement_photo,date_consentement_photo,changement_mdp_obligatoire,cree_le,modifie_le,ville,code_postal,mobile,telephone_fixe,profession,situation_familiale,date_naissance +62de8e71-8082-4383-a3a2-4277bdd07516,am1@example.com,hash125,Claire,Martin,F,assistante_maternelle,actif,0609091011,5 place Bellecour,,False,,False,2025-09-02 12:30:48.724463+00,2025-09-02 12:30:48.724463+00,Lyon,69002,,,,, +76c40571-5da6-4d27-8e07-303185875b36,gest1@example.com,hash126,Paul,Lemoine,H,gestionnaire,actif,0612131415,10 rue Victor Hugo,,False,,False,2025-09-02 12:30:48.724463+00,2025-09-02 12:30:48.724463+00,Paris,75002,,,,, +9bc30373-91a4-45ed-9c05-ffe1651ad906,coparent@example.com,hash124,Marc,Durand,H,parent,actif,0605060708,45 avenue de Lyon,,False,,False,2025-09-02 12:30:48.724463+00,2025-09-02 12:30:48.724463+00,Lyon,69000,,,,, +a1f3d5c7-8b9a-4e2f-9c1d-3b2a4f6e7d8c,fatima.elmansouri@ptits-pas.fr,password,Fatima,El Mansouri,,assistante_maternelle,actif,,17 Boulevard Aristide Briand,,False,,False,2025-09-04 11:49:22.636003+00,2025-09-04 11:49:22.636003+00,Bezons,95870,06 75 45 67 89,01 39 98 78 90,Assistante maternelle,marie,1975-11-12 +b6c4d2e3-5f7a-4b8c-9d1e-2a3c5f7b8d9e,julien.rousseau@ptits-pas.fr,password,Julien,Rousseau,,parent,actif,,14 Rue Pasteur,,False,,False,2025-09-04 11:49:22.636003+00,2025-09-04 11:49:22.636003+00,Bezons,95870,06 56 67 78 89,01 39 98 01 23,Commercial,divorce,1985-08-29 +bbcae75c-0e60-4b84-b281-079dba23b44e,parent1@example.com,hash123,Alice,Dupont,F,parent,actif,0601020304,12 rue de Paris,,False,,False,2025-09-02 12:30:48.724463+00,2025-09-02 12:30:48.724463+00,Paris,75001,,,,, +c4e2d1f5-6b7a-4c3d-8f2a-1e9c3b5a7d6f,claire.martin@ptits-pas.fr,password,Claire,Martin,,parent,actif,,5 Avenue du Général de Gaulle,,False,,False,2025-09-04 11:49:22.636003+00,2025-09-04 11:49:22.636003+00,Bezons,95870,06 89 56 78 90,01 39 98 89 01,Infirmière,marie,1990-04-03 +d3e5f7a9-1c2b-4d6e-8f3a-2b4c6d8e9f1a,david.lecomte@ptits-pas.fr,password,David,Lecomte,,parent,actif,,31 Rue Émile Zola,,False,,False,2025-09-04 11:49:22.636003+00,2025-09-04 11:49:22.636003+00,Bezons,95870,06 45 56 67 78,01 39 98 12 34,Développeur web,celibataire,1992-10-07 +d9c2e3f4-5b6a-4c3d-9f1a-2e7b3c5d8a1f,marie.dubois@ptits-pas.fr,password,Marie,Dubois,,assistante_maternelle,actif,,25 Rue de la République,,False,,False,2025-09-04 11:49:22.636003+00,2025-09-04 12:44:51.654512+00,Bezons,95870,06 96 34 56 78,01 39 98 67 89,Assistante maternelle,marie,1980-06-08 +f1d3c5b7-8a9e-4f2d-9c1b-3e7a5d8c2f1b,amelie.durand@ptits-pas.fr,password,Amélie,Durand,,parent,actif,,23 Rue Victor Hugo,,False,,False,2025-09-04 11:49:22.636003+00,2025-09-04 11:49:22.636003+00,Bezons,95870,06 67 78 89 90,01 39 98 90 12,Comptable,divorce,1987-12-14 +f3b1a2d4-1c7e-4c2f-8b1a-9d3a8e2f5b6c,sophie.bernard@ptits-pas.fr,password,Sophie,Bernard,,administrateur,actif,,12 Avenue Gabriel Péri,,False,,False,2025-09-04 11:49:22.636003+00,2025-09-04 11:49:22.636003+00,Bezons,95870,06 78 12 34 56,01 39 98 45 67,Responsable administrative,marie,1978-03-15 diff --git a/bdd/data_test/validations.csv b/bdd/data_test/validations.csv index a93c5da..77311b6 100644 --- a/bdd/data_test/validations.csv +++ b/bdd/data_test/validations.csv @@ -1,4 +1,4 @@ -"id","id_utilisateur","type","statut","cree_le","modifie_le","valide_par","commentaire" -"8ec99565-e8c2-469f-9641-01b99b8281eb","62de8e71-8082-4383-a3a2-4277bdd07516","identité","valide","2025-09-02 12:57:49.846424+00","2025-09-02 12:57:49.846424+00",, -"be1c4779-341b-436d-b17e-8bc486d22ef8","62de8e71-8082-4383-a3a2-4277bdd07516",,"valide","2025-09-09 14:47:01.963573+00","2025-09-09 14:47:01.963573+00",,"Contrôle OK" -"fcc45701-5708-4368-b467-b95ddb7e1580","d9c2e3f4-5b6a-4c3d-9f1a-2e7b3c5d8a1f",,"valide","2025-09-09 14:52:47.339858+00","2025-09-09 14:52:47.339858+00","76c40571-5da6-4d27-8e07-303185875b36","Contrôle OK" +id,id_utilisateur,type,statut,cree_le,modifie_le,valide_par,commentaire +8ec99565-e8c2-469f-9641-01b99b8281eb,62de8e71-8082-4383-a3a2-4277bdd07516,identité,valide,2025-09-02 12:57:49.846424+00,2025-09-02 12:57:49.846424+00,, +be1c4779-341b-436d-b17e-8bc486d22ef8,62de8e71-8082-4383-a3a2-4277bdd07516,,valide,2025-09-09 14:47:01.963573+00,2025-09-09 14:47:01.963573+00,,Contrôle OK +fcc45701-5708-4368-b467-b95ddb7e1580,d9c2e3f4-5b6a-4c3d-9f1a-2e7b3c5d8a1f,,valide,2025-09-09 14:52:47.339858+00,2025-09-09 14:52:47.339858+00,76c40571-5da6-4d27-8e07-303185875b36,Contrôle OK diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 6c608be..cdee3e2 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -14,10 +14,20 @@ services: ports: - "5433:5432" volumes: - - ./migrations/01_init.sql:/docker-entrypoint-initdb.d/01_init.sql - - ./migrations/07_import.sql:/docker-entrypoint-initdb.d/07_import.sql - - ./bdd/data_test:/bdd/data_test - - postgres_standalone_data:/var/lib/postgresql/data + # Scripts de migration (ordre important → init, indexes, checks, triggers, import…) + - ./migrations/01_init.sql:/docker-entrypoint-initdb.d/01_init.sql + - ./migrations/02_indexes.sql:/docker-entrypoint-initdb.d/02_indexes.sql + - ./migrations/03_checks.sql:/docker-entrypoint-initdb.d/03_checks.sql + - ./migrations/05_triggers.sql:/docker-entrypoint-initdb.d/05_triggers.sql + - ./migrations/07_import.sql:/docker-entrypoint-initdb.d/07_import.sql + # Dossier de données de test (CSV, etc.) + - ./bdd/data_test:/bdd/data_test + + # Scripts de vérification + - ./tests/sql:/tests/sql + + # Données persistées Postgres + - postgres_standalone_data:/var/lib/postgresql/data networks: - ptitspas_dev diff --git a/docs/ENUMS.md b/docs/ENUMS.md index c24e516..89d3138 100644 --- a/docs/ENUMS.md +++ b/docs/ENUMS.md @@ -1,157 +1,146 @@ +# ENUMS.md — Référentiel des valeurs énumérées -# ENUMS.md — Référentiel des valeurs énumérées (Sprint 1) +Ce document recense **toutes les valeurs énumérées** utilisées dans la base **P’titsPas**, leur **sens fonctionnel**, et les **tables/colonnes concernées**. -Ce document recense **toutes les valeurs énumérées** utilisées dans la base P’titsPas, leur **sens fonctionnel**, les **colonnes concernées** et les **transitions** attendues côté métier / API. - -> Objectif : garantir la **cohérence** entre la DB, le backend (NestJS) et le frontend (Flutter). -> Toute nouvelle valeur ou renommage **doit** être ajouté ici **avant** migration DB. +> Objectif : garantir la cohérence entre la **DB**, le **backend (NestJS)** et le **frontend (Flutter)**. +> Toute évolution doit être documentée ici **avant** migration DB. --- ## Conventions générales -- Les valeurs ENUM sont **en minuscules** et **sans espace** (snake_case si nécessaire). -- Côté DB, elles sont implémentées via **types ENUM PostgreSQL** *ou* via `CHECK` (selon ce qui est en place dans `01_init.sql`). -- Côté API, ces valeurs sont **renvoyées telles quelles** et **documentées** dans l’OpenAPI / DTO. +* Les valeurs ENUM sont **en minuscules** (snake\_case si nécessaire). +* Implémentées via `CREATE TYPE … AS ENUM` dans PostgreSQL. +* Les valeurs sont **renvoyées telles quelles** côté API. --- -## 1) Rôle utilisateur — `role` +## 1) Rôle utilisateur — `role_type` -**Tables/colonnes** : `utilisateurs.role` -**Valeurs autorisées** : +**Tables/colonnes** : `utilisateurs.role` -| Valeur | Description | -|---|---| -| `super_admin` | Compte technique initial / administration globale | -| `gestionnaire` | Gestion / validation des comptes, supervision | -| `parent` | Parent ou co-parent | -| `am` | Assistante maternelle | +| Valeur | Description | +| ----------------------- | ------------------------------------------------- | +| `parent` | Parent ou co-parent | +| `gestionnaire` | Gestion/validation des comptes, supervision | +| `super_admin` | Compte technique initial / administration globale | +| `assistante_maternelle` | Profil professionnel d’assistante maternelle | +| `administrateur` | Administration locale / restreinte | --- -## 2) Statut utilisateur — `statut` +## 2) Genre utilisateur — `genre_type` -**Tables/colonnes** : `utilisateurs.statut` -**Valeurs autorisées** : +**Tables/colonnes** : `utilisateurs.genre`, `enfants.genre` -| Valeur | Description | -|---|---| -| `en_attente` | Compte créé mais non validé | -| `accepte` | Compte validé et actif | -| `rejete` | Demande refusée (peut être recréée ultérieurement) | +| Valeur | Description | +| ------- | ------------------- | +| `H` | Homme | +| `F` | Femme | +| `Autre` | Autre / non précisé | --- -## 3) Statut enfant — `statut` +## 3) Statut utilisateur — `statut_utilisateur_type` -**Tables/colonnes** : `enfants.statut` -**Valeurs autorisées** : +**Tables/colonnes** : `utilisateurs.statut` -| Valeur | Description | -|---|---| -| `a_naitre` | Enfant à naître (date prévue renseignée) | -| `actif` | Enfant pris en charge / en cours de garde | -| `scolarise` | Enfant scolarisé, garde potentiellement périscolaire | - -**Contraintes associées** : -- `a_naitre` → **`date_prevue_naissance` obligatoire** -- `actif`/`scolarise` → **`date_naissance` obligatoire** +| Valeur | Description | +| ------------ | ------------------------------- | +| `en_attente` | Compte créé mais non validé | +| `actif` | Compte validé et actif | +| `suspendu` | Compte temporairement désactivé | --- -## 4) Statut dossier — `statut` +## 4) Statut enfant — `statut_enfant_type` -**Tables/colonnes** : `dossiers.statut` -**Valeurs autorisées (MVP)** : +**Tables/colonnes** : `enfants.statut` -| Valeur | Description | -|---|---| -| `envoye` | Dossier soumis par le parent (état initial) | -| `en_cours` | Échanges en cours entre parent et AM | -| `clos` | Dossier clôturé (contrat généré ou abandon) | +| Valeur | Description | +| ----------- | ---------------------------------------------- | +| `a_naitre` | Enfant à naître (date prévue) | +| `actif` | Enfant pris en charge | +| `scolarise` | Enfant scolarisé (garde périscolaire possible) | --- -## 5) Statut contrat — `statut` +## 5) Statut dossier — `statut_dossier_type` -**Tables/colonnes** : `contrats.statut` -**Valeurs autorisées** : +**Tables/colonnes** : `dossiers.statut` -| Valeur | Description | -|---|---| -| `brouillon` | Contrat en préparation | -| `valide` | Contrat finalisé (signatures complètes) | -| `archive` | Contrat obsolète / terminé | +| Valeur | Description | +| --------- | ---------------------------- | +| `envoye` | Dossier soumis par le parent | +| `accepte` | Dossier validé par l’AM | +| `refuse` | Dossier rejeté | --- -## 6) Statut avenant — `statut` +## 6) Statut contrat — `statut_contrat_type` -**Tables/colonnes** : `avenants_contrats.statut` -**Valeurs autorisées** : +**Tables/colonnes** : `contrats.statut` -| Valeur | Description | -|---|---| -| `propose` | Avenant proposé (en attente d’accord) | -| `valide` | Avenant accepté et appliqué | -| `rejete` | Avenant refusé | +| Valeur | Description | +| ---------------------- | ----------------------------------------- | +| `brouillon` | Contrat en préparation | +| `en_attente_signature` | Contrat généré, en attente des signatures | +| `valide` | Contrat signé et actif | +| `resilie` | Contrat résilié | --- -## 7) Type d’événement — `type` +## 7) Statut avenant — `statut_avenant_type` -**Tables/colonnes** : `evenements.type` -**Valeurs autorisées** : +**Tables/colonnes** : `avenants_contrats.statut` -| Valeur | Description | -|---|---| -| `absence_enfant` | Enfant absent | -| `conge_am` | Congé de l’assistante maternelle | -| `conge_parent` | Congé du parent | -| `arret_maladie_am` | Arrêt maladie AM | -| `evenement_rpe` | Événement RPE | +| Valeur | Description | +| --------- | --------------------------- | +| `propose` | Avenant proposé | +| `accepte` | Avenant accepté et appliqué | +| `refuse` | Avenant rejeté | --- -## 8) Statut d’événement — `statut` +## 8) Type d’événement — `type_evenement_type` -**Tables/colonnes** : `evenements.statut` -**Valeurs autorisées** : +**Tables/colonnes** : `evenements.type` -| Valeur | Description | -|---|---| +| Valeur | Description | +| ------------------ | -------------------------------- | +| `absence_enfant` | Absence de l’enfant | +| `conge_am` | Congé de l’assistante maternelle | +| `conge_parent` | Congé du parent | +| `arret_maladie_am` | Arrêt maladie de l’AM | +| `evenement_rpe` | Événement organisé par le RPE | + +--- + +## 9) Statut d’événement — `statut_evenement_type` + +**Tables/colonnes** : `evenements.statut` + +| Valeur | Description | +| --------- | ----------------- | | `propose` | Événement proposé | -| `valide` | Événement validé | -| `rejete` | Événement refusé | +| `valide` | Événement validé | +| `refuse` | Événement rejeté | --- -## 9) Statut de validation compte — `statut` +## 10) Statut validation — `statut_validation_type` -**Tables/colonnes** : `validations.statut` -**Valeurs autorisées** : +**Tables/colonnes** : `validations.statut` -| Valeur | Description | -|---|---| -| `accepte` | Compte validé | -| `rejete` | Compte refusé | +| Valeur | Description | +| ------------ | ------------------------ | +| `en_attente` | En attente de validation | +| `valide` | Validation acceptée | +| `refuse` | Validation refusée | --- -## 10) Type de notification — `type` +📌 **Mainteneur** : Équipe BDD +📌 **Dernière mise à jour** : alignée sur `init.sql` (septembre 2025) -**Tables/colonnes** : `notifications.type` -**Valeurs proposées** : - -| Valeur | Description | -|---|---| -| `nouveau_message` | Nouveau message sur un dossier | -| `validation_compte` | Résultat de la validation de compte | -| `maj_contrat` | Contrat mis à jour (avenant / signature) | -| `evenement_a_venir` | Rappel d’un événement proche | - ---- - -**Mainteneur** : Équipe BDD -**Dernière mise à jour** : Sprint 1 — Ticket 9 (ENUMS) +--- \ No newline at end of file diff --git a/docs/FK_POLICIES.md b/docs/FK_POLICIES.md index 3be63b3..a655bc2 100644 --- a/docs/FK_POLICIES.md +++ b/docs/FK_POLICIES.md @@ -1,142 +1,144 @@ +# FK\_POLICIES.md — Politiques de clés étrangères -# FK_POLICIES.md -**Politique des clés étrangères (ON DELETE / ON UPDATE)** – Sprint 1 +Ce document recense l’ensemble des **relations entre tables** dans la base **P’titsPas**, avec leurs **règles de suppression** et les implications fonctionnelles. -## 🎯 Objectif -Documenter, de façon unique et partagée, les règles de suppression/mise à jour appliquées aux **clés étrangères** de la base P’titsPas pour : -- préserver l’**intégrité référentielle** ; -- conserver l’**historique** utile (messages, événements…) ; -- respecter les exigences **RGPD** (suppression en cascade lorsque pertinent). - -> Par défaut, **ON UPDATE = NO ACTION** (UUID immuables). -> Ce document couvre **ON DELETE** table par table. +> Objectif : assurer la cohérence entre la **DB**, le **backend** et le **fonctionnel métier**. +> Toute nouvelle relation ou modification doit être ajoutée ici avant migration DB. --- -## 🧭 Principes généraux +## Conventions générales -- **CASCADE** quand la donnée fille **n’a pas de sens sans le parent** - (ex. `dossiers` d’un parent, `avenants` d’un contrat). -- **SET NULL** quand on veut **préserver l’historique** mais que le référent peut disparaître - (ex. auteur d’un message supprimé, créateur d’un événement). -- **RESTRICT/NO ACTION** non utilisé ici pour éviter des blocages au nettoyage. +* Les FK sont définies en `REFERENCES ...`. +* Sauf précision, `ON UPDATE` est implicite = `NO ACTION`. +* Les comportements documentés sont **côté PostgreSQL**. --- -## 📚 Récapitulatif rapide (matrice) +## 1) Table `assistantes_maternelles` -| Table (colonne FK) → Référence | ON DELETE | Raison | -|---|---:|---| -| **assistantes_maternelles(id_utilisateur)** → `utilisateurs(id)` | **CASCADE** | Profil AM supprimé avec son compte | -| **parents(id_utilisateur)** → `utilisateurs(id)` | **CASCADE** | Extension parent supprimée avec son compte | -| **parents(id_co_parent)** → `utilisateurs(id)` | **SET NULL** | Conserver le parent principal si co-parent disparaît | -| **enfants_parents(id_parent)** → `parents(id_utilisateur)` | **CASCADE** | Nettoyage liaisons N:N | -| **enfants_parents(id_enfant)** → `enfants(id)` | **CASCADE** | Idem | -| **dossiers(id_parent)** → `parents(id_utilisateur)` | **CASCADE** | Dossier n’a pas de sens sans parent | -| **dossiers(id_enfant)** → `enfants(id)` | **CASCADE** | Dossier n’a pas de sens sans enfant | -| **messages(id_dossier)** → `dossiers(id)` | **CASCADE** | Messages détruits avec le dossier | -| **messages(id_expediteur)** → `utilisateurs(id)` | **SET NULL** | Garder l’historique des échanges | -| **contrats(id_dossier)** → `dossiers(id)` | **CASCADE** | 1:1, contrat détruit si dossier supprimé | -| **avenants_contrats(id_contrat)** → `contrats(id)` | **CASCADE** | Avenants détruits avec le contrat | -| **avenants_contrats(initie_par)** → `utilisateurs(id)` | **SET NULL** | Historiser l’avenant sans bloquer | -| **evenements(id_enfant)** → `enfants(id)` | **CASCADE** | Événements n’ont plus de sens | -| **evenements(id_am)** → `utilisateurs(id)` | **SET NULL** | Garder la trace même si AM supprimée | -| **evenements(id_parent)** → `parents(id_utilisateur)` | **SET NULL** | Garder la trace si parent supprimé | -| **evenements(cree_par)** → `utilisateurs(id)` | **SET NULL** | Conserver l’historique de création | -| **signalements_bugs(id_utilisateur)** → `utilisateurs(id)` | **SET NULL** | Conserver le ticket même si compte supprimé | -| **uploads(id_utilisateur)** → `utilisateurs(id)` | **SET NULL** | Fichier reste référencé sans l’auteur | -| **notifications(id_utilisateur)** → `utilisateurs(id)` | **CASCADE** | Notifications propres à l’utilisateur | -| **validations(id_utilisateur)** → `utilisateurs(id)` | **SET NULL** | Garder l’historique de décision | - -> **ON UPDATE** : **NO ACTION** partout (les UUID ne changent pas). +* **FK** : `id_utilisateur → utilisateurs.id` +* **ON DELETE CASCADE** +* 🔎 Si un utilisateur AM est supprimé, son profil AM est supprimé automatiquement. --- -## 🔎 Détail par domaine +## 2) Table `parents` -### Utilisateurs & extensions -- `assistantes_maternelles.id_utilisateur` → **CASCADE** -- `parents.id_utilisateur` → **CASCADE** -- `parents.id_co_parent` → **SET NULL** (interdit d’être co-parent de soi-même via CHECK déjà posé) +* **FK** : `id_utilisateur → utilisateurs.id` -### Enfants & liaisons -- `enfants_parents.id_parent` → **CASCADE** -- `enfants_parents.id_enfant` → **CASCADE** + * **ON DELETE CASCADE** → suppression d’un parent entraîne suppression automatique du parent. +* **FK** : `id_co_parent → utilisateurs.id` -### Dossiers & échanges -- `dossiers.id_parent` → **CASCADE** -- `dossiers.id_enfant` → **CASCADE** -- `messages.id_dossier` → **CASCADE** -- `messages.id_expediteur` → **SET NULL** - -### Contrats & avenants -- `contrats.id_dossier` → **CASCADE** (unique 1:1) -- `avenants_contrats.id_contrat` → **CASCADE** -- `avenants_contrats.initie_par` → **SET NULL** - -### Événements -- `evenements.id_enfant` → **CASCADE** -- `evenements.id_am` → **SET NULL** -- `evenements.id_parent` → **SET NULL** -- `evenements.cree_par` → **SET NULL** - -### Divers -- `signalements_bugs.id_utilisateur` → **SET NULL** -- `uploads.id_utilisateur` → **SET NULL** -- `notifications.id_utilisateur` → **CASCADE** -- `validations.id_utilisateur` → **SET NULL** + * **ON DELETE NO ACTION** (par défaut) → suppression du co-parent interdite si référence active. --- -## 🧪 Scénarios de test (exemples) +## 3) Table `enfants_parents` -1. **Suppression d’un parent** - - Supprimer `utilisateurs(id=parentX)` - - Attendu : `parents` (CASCADE), ses `dossiers` (CASCADE), `messages` liés aux `dossiers` (CASCADE) sont supprimés. +* **FK** : `id_parent → parents.id_utilisateur` -2. **Suppression d’un co-parent** - - Supprimer `utilisateurs(id=coParentY)` - - Attendu : `parents.id_co_parent` passe à **NULL**, aucun dossier supprimé. + * **ON DELETE CASCADE** → si le parent est supprimé, le lien avec l’enfant disparaît. +* **FK** : `id_enfant → enfants.id` -3. **Suppression d’un utilisateur auteur de messages** - - Supprimer `utilisateurs(id=uZ)` - - Attendu : les lignes `messages` **restent**, `id_expediteur` devient **NULL**. - -4. **Suppression d’un enfant** - - Supprimer `enfants(id=childA)` - - Attendu : `enfants_parents` (CASCADE), `dossiers` du childA (CASCADE), `evenements` du childA (CASCADE). - -5. **Suppression d’un utilisateur AM** - - Supprimer `utilisateurs(id=amB)` - - Attendu : `evenements.id_am` devient **NULL** (historique conservé). + * **ON DELETE CASCADE** → si l’enfant est supprimé, toutes ses associations avec des parents disparaissent. --- -## 🛠 Migrations associées +## 4) Table `dossiers` -Les ajustements sont implémentés dans : -- **`/bdd/migrations/04_fk_policies.sql`** - – redéfinition des contraintes FK avec les bonnes politiques (**DROP puis ADD CONSTRAINT**), de façon idempotente. +* **FK** : `id_parent → parents.id_utilisateur` + + * **ON DELETE CASCADE** → si le parent est supprimé, ses dossiers disparaissent. +* **FK** : `id_enfant → enfants.id` + + * **ON DELETE CASCADE** → si l’enfant est supprimé, les dossiers liés disparaissent. --- -## 📄 Notes & futures évolutions +## 5) Table `messages` -- **RGPD (Sprint 2)** : si vous activez le **soft delete** (`deleted_at`) côté tables métier, ces politiques restent valides (les suppressions logiques se gèrent au niveau applicatif). -- **Audit** : si vous voulez tracer les suppressions, ajoutez des triggers d’audit (voir ticket Sprint 2 – Audit log). -- **Performance** : chaque FK doit être **indexée côté enfant** (cf. `02_indexes.sql`). +* **FK** : `id_dossier → dossiers.id` + + * **ON DELETE CASCADE** → si un dossier est supprimé, les messages disparaissent. +* **FK** : `id_expediteur → utilisateurs.id` + + * **ON DELETE CASCADE** → si un utilisateur est supprimé, ses messages disparaissent. --- -## ✅ Checklist de conformité +## 6) Table `contrats` -- [ ] Toutes les FK listées existent dans la base -- [ ] Politique **ON DELETE** conforme au tableau ci-dessus -- [ ] **ON UPDATE = NO ACTION** partout -- [ ] Tests de suppression réalisés sur une base seedée -- [ ] `04_fk_policies.sql` appliqué sans erreur +* **FK** : `id_dossier → dossiers.id` + + * **UNIQUE** + **ON DELETE CASCADE** → si un dossier est supprimé, le contrat disparaît. --- -**Mainteneur** : Équipe BDD -**Dernière mise à jour** : Sprint 1 – Politique FK consolidée +## 7) Table `avenants_contrats` + +* **FK** : `id_contrat → contrats.id` + + * **ON DELETE CASCADE** → si un contrat est supprimé, ses avenants disparaissent. +* **FK** : `initie_par → utilisateurs.id` + + * **ON DELETE NO ACTION** → suppression interdite tant que des avenants existent. + +--- + +## 8) Table `evenements` + +* **FK** : `id_enfant → enfants.id` + + * **ON DELETE CASCADE** → si un enfant est supprimé, ses événements disparaissent. +* **FK** : `id_am → utilisateurs.id` + + * **ON DELETE NO ACTION** → suppression interdite si des événements liés. +* **FK** : `id_parent → parents.id_utilisateur` + + * **ON DELETE NO ACTION** → suppression interdite si des événements liés. +* **FK** : `cree_par → utilisateurs.id` + + * **ON DELETE NO ACTION** → suppression interdite si référence encore utilisée. + +--- + +## 9) Table `signalements_bugs` + +* **FK** : `id_utilisateur → utilisateurs.id` + + * **ON DELETE NO ACTION** → utilisateur non supprimable si bug référencé. + +--- + +## 10) Table `uploads` + +* **FK** : `id_utilisateur → utilisateurs.id` + + * **ON DELETE SET NULL** → si un utilisateur est supprimé, ses fichiers restent, mais sans lien utilisateur. + +--- + +## 11) Table `notifications` + +* **FK** : `id_utilisateur → utilisateurs.id` + + * **ON DELETE CASCADE** → si un utilisateur est supprimé, ses notifications disparaissent. + +--- + +## 12) Table `validations` + +* **FK** : `id_utilisateur → utilisateurs.id` + + * **ON DELETE NO ACTION** → suppression interdite si validations liées. +* **FK** : `valide_par → utilisateurs.id` + + * **ON DELETE NO ACTION** → suppression interdite si validations effectuées par l’utilisateur. + +--- + +📌 **Mainteneur** : Équipe BDD +📌 **Dernière mise à jour** : alignée sur `init.sql` (septembre 2025) + +--- diff --git a/makefile b/makefile new file mode 100644 index 0000000..48f7ba3 --- /dev/null +++ b/makefile @@ -0,0 +1,72 @@ +# ============================================================ +# Makefile - Gestion de la base Ptits Pas (Postgres + pgAdmin) +# Compatible macOS, Linux, Windows (Git Bash) +# ============================================================ + +# Détection de l'OS +UNAME_S := $(shell uname -s) + +ifeq ($(UNAME_S),Darwin) # macOS + OPEN_CMD = open +else ifeq ($(UNAME_S),Linux) # Linux + OPEN_CMD = xdg-open +else # Windows (Git Bash / Cygwin) + OPEN_CMD = start +endif + +# Paramètres +PG_CONTAINER = ptitspas-postgres-standalone +PG_USER = admin +PG_DB = ptitpas_db +VERIFY_SQL = /tests/sql/verify.sql +IMPORT_SQL = /docker-entrypoint-initdb.d/07_import.sql + +# ============================================================ +# Cibles principales +# ============================================================ + +reset: + @echo "🗑️ Réinitialisation de la base..." + docker compose -f docker-compose.dev.yml down -v + docker compose -f docker-compose.dev.yml up -d + @echo "✅ Base de données réinitialisée." + +import: + @echo "⏳ Attente que Postgres démarre..." + @until docker exec -i $(PG_CONTAINER) pg_isready -U $(PG_USER) -d $(PG_DB); do \ + sleep 2; \ + done + @echo "📥 Import des données de démo..." + @docker exec -i $(PG_CONTAINER) psql -U $(PG_USER) -d $(PG_DB) -f $(IMPORT_SQL) + @echo "✅ Données importées." + +verify: + @echo "⏳ Attente que Postgres démarre pour vérifier..." + @until docker exec -i $(PG_CONTAINER) pg_isready -U $(PG_USER) -d $(PG_DB); do \ + sleep 2; \ + done + @echo "🔎 Vérification des données..." + -@docker exec -i $(PG_CONTAINER) psql -U $(PG_USER) -d $(PG_DB) -f $(VERIFY_SQL) | tee verify.log + @if grep -q "ERROR" verify.log; then \ + echo "❌ Des erreurs détectées (voir verify.log)"; \ + else \ + echo "✅ Vérification OK : pas d'erreurs détectées."; \ + fi + +demo: reset import verify + @echo "🌐 Ouverture de pgAdmin sur http://localhost:8081 ..." + @$(OPEN_CMD) http://localhost:8081 + +# ============================================================ +# Utilitaires +# ============================================================ + +logs: + docker compose -f docker-compose.dev.yml logs -f + +psql: + docker exec -it $(PG_CONTAINER) psql -U $(PG_USER) -d $(PG_DB) + +stop: + docker compose -f docker-compose.dev.yml down +j \ No newline at end of file diff --git a/migrations/02_indexes.sql b/migrations/02_indexes.sql index bd912c4..c457cd1 100644 --- a/migrations/02_indexes.sql +++ b/migrations/02_indexes.sql @@ -1,157 +1,57 @@ --- ============================================= --- 02_indexes.sql : Index FK + colonnes critiques --- ============================================= +-- ============================================================ +-- 02_indexes.sql — Indexes pour améliorer les perfs +-- ============================================================ --- Recommandation : exécuter après 01_init.sql - --- =========== --- UTILISATEURS --- =========== --- Recherche par email (insensibilité à la casse pour lookup) -CREATE INDEX IF NOT EXISTS idx_utilisateurs_lower_courriel - ON utilisateurs (LOWER(courriel)); - --- =========== --- ASSISTANTES_MATERNELLES --- =========== --- FK -> utilisateurs(id) -CREATE INDEX IF NOT EXISTS idx_am_id_utilisateur - ON assistantes_maternelles (id_utilisateur); - --- ======= --- PARENTS --- ======= --- FK -> utilisateurs(id) -CREATE INDEX IF NOT EXISTS idx_parents_id_utilisateur - ON parents (id_utilisateur); - --- Co-parent (nullable) -CREATE INDEX IF NOT EXISTS idx_parents_id_co_parent - ON parents (id_co_parent); - --- ======= --- ENFANTS --- ======= --- (souvent filtrés par statut / date_naissance ? à activer si besoin) --- CREATE INDEX IF NOT EXISTS idx_enfants_statut ON enfants (statut); --- CREATE INDEX IF NOT EXISTS idx_enfants_date_naissance ON enfants (date_naissance); - --- ================ --- ENFANTS_PARENTS (N:N) --- ================ --- PK composite déjà en place (id_parent, id_enfant), ajouter index individuels si jointures unilatérales fréquentes -CREATE INDEX IF NOT EXISTS idx_enfants_parents_id_parent - ON enfants_parents (id_parent); - -CREATE INDEX IF NOT EXISTS idx_enfants_parents_id_enfant - ON enfants_parents (id_enfant); - --- ======== --- DOSSIERS --- ======== --- FK -> parent / enfant -CREATE INDEX IF NOT EXISTS idx_dossiers_id_parent - ON dossiers (id_parent); - -CREATE INDEX IF NOT EXISTS idx_dossiers_id_enfant - ON dossiers (id_enfant); - --- Statut (filtrages "à traiter", "envoyé", etc.) -CREATE INDEX IF NOT EXISTS idx_dossiers_statut - ON dossiers (statut); - --- JSONB : si on fait des requêtes @> sur le planning souhaité --- CREATE INDEX IF NOT EXISTS idx_dossiers_planning_souhaite_gin --- ON dossiers USING GIN (planning_souhaite); - --- ======== --- MESSAGES --- ======== --- Filtrage par dossier + tri chronologique +-- =========================================== +-- Messages (par dossier + tri par date) +-- Utilisé dans verify.sql (EXPLAIN + ORDER BY cree_le) +-- =========================================== CREATE INDEX IF NOT EXISTS idx_messages_id_dossier_cree_le - ON messages (id_dossier, cree_le); + ON messages (id_dossier, cree_le DESC); --- Recherche par expéditeur -CREATE INDEX IF NOT EXISTS idx_messages_id_expediteur_cree_le - ON messages (id_expediteur, cree_le); - --- ========= --- CONTRATS --- ========= --- UNIQUE(id_dossier) existe déjà -> index implicite --- Tri / filtres fréquents -CREATE INDEX IF NOT EXISTS idx_contrats_statut - ON contrats (statut); - --- JSONB planning : activer si on requête par clé --- CREATE INDEX IF NOT EXISTS idx_contrats_planning_gin --- ON contrats USING GIN (planning); - --- ================== --- AVENANTS_CONTRATS --- ================== -CREATE INDEX IF NOT EXISTS idx_avenants_contrats_id_contrat_cree_le - ON avenants_contrats (id_contrat, cree_le); - -CREATE INDEX IF NOT EXISTS idx_avenants_contrats_initie_par - ON avenants_contrats (initie_par); - -CREATE INDEX IF NOT EXISTS idx_avenants_contrats_statut - ON avenants_contrats (statut); - --- ========= --- EVENEMENTS --- ========= --- Accès par enfant + période +-- =========================================== +-- Evenements (filtrés par enfant + date) +-- Utilisé dans verify.sql (WHERE id_enfant AND date_debut >= ...) +-- =========================================== CREATE INDEX IF NOT EXISTS idx_evenements_id_enfant_date_debut - ON evenements (id_enfant, date_debut); + ON evenements (id_enfant, date_debut DESC); --- Filtrage par auteur / AM / parent -CREATE INDEX IF NOT EXISTS idx_evenements_cree_par - ON evenements (cree_par); - -CREATE INDEX IF NOT EXISTS idx_evenements_id_am - ON evenements (id_am); - -CREATE INDEX IF NOT EXISTS idx_evenements_id_parent - ON evenements (id_parent); - -CREATE INDEX IF NOT EXISTS idx_evenements_type - ON evenements (type); - -CREATE INDEX IF NOT EXISTS idx_evenements_statut - ON evenements (statut); - --- ================= --- SIGNALEMENTS_BUGS --- ================= -CREATE INDEX IF NOT EXISTS idx_signalements_bugs_id_utilisateur_cree_le - ON signalements_bugs (id_utilisateur, cree_le); - --- ======= --- UPLOADS --- ======= -CREATE INDEX IF NOT EXISTS idx_uploads_id_utilisateur_cree_le - ON uploads (id_utilisateur, cree_le); - --- ============= --- NOTIFICATIONS --- ============= --- Requêtes fréquentes : non lues + ordre chrono +-- =========================================== +-- Notifications (par utilisateur + lues/non lues + tri par date) +-- Utilisé dans verify.sql (WHERE id_utilisateur AND lu=false ORDER BY cree_le DESC) +-- =========================================== CREATE INDEX IF NOT EXISTS idx_notifications_user_lu_cree_le - ON notifications (id_utilisateur, lu, cree_le); + ON notifications (id_utilisateur, lu, cree_le DESC); --- Option : index partiel pour "non lues" --- CREATE INDEX IF NOT EXISTS idx_notifications_non_lues --- ON notifications (id_utilisateur, cree_le) --- WHERE lu = false; +-- =========================================== +-- Dossiers (filtrés par parent + enfant + statut + tri par date) +-- Utilisé dans verify.sql +-- =========================================== +CREATE INDEX IF NOT EXISTS idx_dossiers_id_parent_enfant_statut_cree_le + ON dossiers (id_parent, id_enfant, statut, cree_le DESC); --- =========== --- VALIDATIONS --- =========== --- Requêtes par utilisateur validé, par statut et par date -CREATE INDEX IF NOT EXISTS idx_validations_id_utilisateur_cree_le - ON validations (id_utilisateur, cree_le); +-- =========================================== +-- Contrats (lookup par dossier unique) +-- =========================================== +CREATE UNIQUE INDEX IF NOT EXISTS idx_contrats_id_dossier + ON contrats (id_dossier); -CREATE INDEX IF NOT EXISTS idx_validations_statut - ON validations (statut); +-- =========================================== +-- JSONB indexes (si filtres fréquents sur planning/planning_souhaite) +-- Exemple dans verify.sql : dossiers.planning_souhaite @> ... +-- =========================================== +CREATE INDEX IF NOT EXISTS idx_dossiers_planning_souhaite_gin + ON dossiers USING gin (planning_souhaite jsonb_path_ops); + +CREATE INDEX IF NOT EXISTS idx_contrats_planning_gin + ON contrats USING gin (planning jsonb_path_ops); + +-- =========================================== +-- Sanity check : validation des emails uniques +-- Déjà couvert par UNIQUE dans la table utilisateurs +-- =========================================== + +-- =========================================== +-- Bonus : accélérer les jointures fréquentes +-- (utilisateurs.id, enfants.id, parents.id_utilisateur sont PK → déjà indexées automatiquement) +-- =========================================== diff --git a/migrations/03_checks.sql b/migrations/03_checks.sql index 9d1e159..b9568f4 100644 --- a/migrations/03_checks.sql +++ b/migrations/03_checks.sql @@ -1,140 +1,74 @@ --- ============================================= --- 03_checks.sql : Contraintes CHECK & NOT NULL --- A exécuter après 01_init.sql (et 02_indexes.sql) --- ============================================= +-- ============================================================ +-- 03_checks.sql — Contraintes d'intégrité métier +-- ============================================================ --- ============== +-- =========================================== -- UTILISATEURS --- ============== --- (Regex email déjà présente dans 01_init.sql) --- Optionnel : forcer prenom/nom non vides si fournis +-- =========================================== + +-- Email déjà couvert par UNIQUE + regex dans le schéma +-- Vérifier que mobile et téléphone fixe ne sont pas identiques ALTER TABLE utilisateurs - ADD CONSTRAINT chk_utilisateurs_prenom_non_vide - CHECK (prenom IS NULL OR btrim(prenom) <> ''), - ADD CONSTRAINT chk_utilisateurs_nom_non_vide - CHECK (nom IS NULL OR btrim(nom) <> ''); - --- ========================= --- ASSISTANTES_MATERNELLES --- ========================= --- NIR : aujourd’hui en 15 chiffres (Sprint 2 : chiffrement) -ALTER TABLE assistantes_maternelles - ADD CONSTRAINT chk_am_nir_format - CHECK (nir_chiffre IS NULL OR nir_chiffre ~ '^[0-9]{15}$'), - ADD CONSTRAINT chk_am_nb_max_enfants - CHECK (nb_max_enfants IS NULL OR nb_max_enfants BETWEEN 0 AND 10), - ADD CONSTRAINT chk_am_ville_non_vide - CHECK (ville_residence IS NULL OR btrim(ville_residence) <> ''); - --- ========= --- PARENTS --- ========= --- Interdiction d’être co-parent de soi-même -ALTER TABLE parents - ADD CONSTRAINT chk_parents_co_parent_diff - CHECK (id_co_parent IS NULL OR id_co_parent <> id_utilisateur); - --- ========= --- ENFANTS --- ========= --- Cohérence statut / dates de naissance -ALTER TABLE enfants - ADD CONSTRAINT chk_enfants_dates_exclusives - CHECK (NOT (date_naissance IS NOT NULL AND date_prevue_naissance IS NOT NULL)), - ADD CONSTRAINT chk_enfants_statut_dates - CHECK ( - -- a_naitre => date_prevue_naissance requise - (statut = 'a_naitre' AND date_prevue_naissance IS NOT NULL) - OR - -- actif/scolarise => date_naissance requise - (statut IN ('actif','scolarise') AND date_naissance IS NOT NULL) - OR statut IS NULL -- si statut non encore fixé - ), - ADD CONSTRAINT chk_enfants_consentement_coherent - CHECK ( - (consentement_photo = true AND date_consentement_photo IS NOT NULL) - OR - (consentement_photo = false AND date_consentement_photo IS NULL) + ADD CONSTRAINT chk_telephone_diff CHECK ( + mobile IS NULL OR telephone_fixe IS NULL OR mobile <> telephone_fixe ); --- ================= --- ENFANTS_PARENTS --- ================= --- (PK composite déjà en place, rien à ajouter ici) +-- =========================================== +-- ENFANTS +-- =========================================== --- ======== +-- Si l’enfant est "a_naitre", la date_prevue_naissance doit être renseignée +ALTER TABLE enfants + ADD CONSTRAINT chk_enfant_date_prevue + CHECK ( + (statut = 'a_naitre' AND date_prevue_naissance IS NOT NULL) + OR (statut <> 'a_naitre') + ); + +-- Si l’enfant est déjà né, sa date_naissance doit être renseignée +ALTER TABLE enfants + ADD CONSTRAINT chk_enfant_date_naissance + CHECK ( + (statut IN ('actif','scolarise') AND date_naissance IS NOT NULL) + OR (statut = 'a_naitre') + ); + +-- =========================================== -- DOSSIERS --- ======== +-- =========================================== + +-- Budget positif si renseigné ALTER TABLE dossiers - ADD CONSTRAINT chk_dossiers_budget_nonneg - CHECK (budget IS NULL OR budget >= 0), - ADD CONSTRAINT chk_dossiers_type_contrat_non_vide - CHECK (type_contrat IS NULL OR btrim(type_contrat) <> ''), - ADD CONSTRAINT chk_dossiers_planning_json - CHECK (planning_souhaite IS NULL OR jsonb_typeof(planning_souhaite) = 'object'); + ADD CONSTRAINT chk_dossier_budget_pos CHECK (budget IS NULL OR budget > 0); --- ======== --- MESSAGES --- ======== --- Contenu obligatoire, non vide -ALTER TABLE messages - ALTER COLUMN contenu SET NOT NULL; -ALTER TABLE messages - ADD CONSTRAINT chk_messages_contenu_non_vide - CHECK (btrim(contenu) <> ''); - --- ========= +-- =========================================== -- CONTRATS --- ========= +-- =========================================== + +-- Cohérence des dates : date_debut obligatoire pour un contrat signé ALTER TABLE contrats - ADD CONSTRAINT chk_contrats_tarif_nonneg - CHECK (tarif_horaire IS NULL OR tarif_horaire >= 0), - ADD CONSTRAINT chk_contrats_indemnites_nonneg - CHECK (indemnites_repas IS NULL OR indemnites_repas >= 0); + ADD CONSTRAINT chk_contrat_date_debut + CHECK ( + (statut = 'brouillon' OR date_debut IS NOT NULL) + ); --- ================== --- AVENANTS_CONTRATS --- ================== --- Rien de spécifique (statut enum déjà en place) - --- ========= +-- =========================================== -- EVENEMENTS --- ========= +-- =========================================== + +-- Cohérence des dates : début < fin ALTER TABLE evenements - ADD CONSTRAINT chk_evenements_dates_coherentes - CHECK (date_fin IS NULL OR date_debut IS NULL OR date_fin >= date_debut); + ADD CONSTRAINT chk_evenement_dates + CHECK (date_fin IS NULL OR date_debut <= date_fin); --- ================= --- SIGNALEMENTS_BUGS --- ================= --- Description obligatoire, non vide -ALTER TABLE signalements_bugs - ALTER COLUMN description SET NOT NULL; -ALTER TABLE signalements_bugs - ADD CONSTRAINT chk_bugs_description_non_vide - CHECK (btrim(description) <> ''); - --- ======= --- UPLOADS --- ======= --- URL obligatoire + format basique (chemin absolu ou http(s)) -ALTER TABLE uploads - ALTER COLUMN fichier_url SET NOT NULL; -ALTER TABLE uploads - ADD CONSTRAINT chk_uploads_url_format - CHECK (fichier_url ~ '^(https?://.+|/[^\\s]+)$'); - --- ============= --- NOTIFICATIONS --- ============= --- Contenu obligatoire, non vide -ALTER TABLE notifications - ALTER COLUMN contenu SET NOT NULL; -ALTER TABLE notifications - ADD CONSTRAINT chk_notifications_contenu_non_vide - CHECK (btrim(contenu) <> ''); - --- =========== +-- =========================================== -- VALIDATIONS --- =========== --- Rien de plus ici (Sprint 1 Ticket 8 enrichira la table) +-- =========================================== + +-- Si statut = 'valide', alors modifie_le doit être >= cree_le +ALTER TABLE validations + ADD CONSTRAINT chk_validation_modif + CHECK ( + (statut <> 'valide') + OR (modifie_le IS NULL OR modifie_le >= cree_le) + ); diff --git a/migrations/05_triggers.sql b/migrations/05_triggers.sql index 5d4b489..1913202 100644 --- a/migrations/05_triggers.sql +++ b/migrations/05_triggers.sql @@ -1,150 +1,103 @@ --- ============================================= --- 05_triggers.sql : Timestamps automatiques --- - Ajoute (si absent) cree_le DEFAULT now() et modifie_le DEFAULT now() --- - Crée un trigger BEFORE UPDATE pour mettre à jour modifie_le --- - Idempotent (DROP TRIGGER IF EXISTS / IF NOT EXISTS) --- A exécuter après 01_init.sql, 02_indexes.sql, 03_checks.sql --- ============================================= +-- ============================================================ +-- 05_triggers.sql — Triggers métier et maintenance +-- ============================================================ --- 1) Fonction unique de mise à jour du timestamp +-- =========================================== +-- 1. Mettre à jour automatiquement modifie_le +-- =========================================== CREATE OR REPLACE FUNCTION set_modifie_le() RETURNS TRIGGER AS $$ BEGIN - NEW.modifie_le := NOW(); - RETURN NEW; + NEW.modifie_le := now(); + RETURN NEW; END; $$ LANGUAGE plpgsql; --- Helper macro-like: pour chaque table, s'assurer des colonnes & trigger --- (on ne peut pas faire de macro, donc on répète pour chaque table) - --- Liste des tables concernées : --- utilisateurs, assistantes_maternelles, parents, enfants, enfants_parents, --- dossiers, messages, contrats, avenants_contrats, evenements, --- signalements_bugs, uploads, notifications, validations - --- ========== UTILISATEURS -ALTER TABLE utilisateurs - ADD COLUMN IF NOT EXISTS cree_le TIMESTAMP DEFAULT NOW(), - ADD COLUMN IF NOT EXISTS modifie_le TIMESTAMP DEFAULT NOW(); -DROP TRIGGER IF EXISTS trg_utilisateurs_set_modifie_le ON utilisateurs; -CREATE TRIGGER trg_utilisateurs_set_modifie_le +-- Appliquer sur les tables qui ont modifie_le +CREATE TRIGGER trg_update_modifie_le_utilisateurs BEFORE UPDATE ON utilisateurs -FOR EACH ROW EXECUTE FUNCTION set_modifie_le(); +FOR EACH ROW +EXECUTE FUNCTION set_modifie_le(); --- ========== ASSISTANTES_MATERNELLES -ALTER TABLE assistantes_maternelles - ADD COLUMN IF NOT EXISTS cree_le TIMESTAMP DEFAULT NOW(), - ADD COLUMN IF NOT EXISTS modifie_le TIMESTAMP DEFAULT NOW(); -DROP TRIGGER IF EXISTS trg_am_set_modifie_le ON assistantes_maternelles; -CREATE TRIGGER trg_am_set_modifie_le -BEFORE UPDATE ON assistantes_maternelles -FOR EACH ROW EXECUTE FUNCTION set_modifie_le(); - --- ========== PARENTS -ALTER TABLE parents - ADD COLUMN IF NOT EXISTS cree_le TIMESTAMP DEFAULT NOW(), - ADD COLUMN IF NOT EXISTS modifie_le TIMESTAMP DEFAULT NOW(); -DROP TRIGGER IF EXISTS trg_parents_set_modifie_le ON parents; -CREATE TRIGGER trg_parents_set_modifie_le -BEFORE UPDATE ON parents -FOR EACH ROW EXECUTE FUNCTION set_modifie_le(); - --- ========== ENFANTS -ALTER TABLE enfants - ADD COLUMN IF NOT EXISTS cree_le TIMESTAMP DEFAULT NOW(), - ADD COLUMN IF NOT EXISTS modifie_le TIMESTAMP DEFAULT NOW(); -DROP TRIGGER IF EXISTS trg_enfants_set_modifie_le ON enfants; -CREATE TRIGGER trg_enfants_set_modifie_le -BEFORE UPDATE ON enfants -FOR EACH ROW EXECUTE FUNCTION set_modifie_le(); - --- ========== ENFANTS_PARENTS (table de liaison) -ALTER TABLE enfants_parents - ADD COLUMN IF NOT EXISTS cree_le TIMESTAMP DEFAULT NOW(), - ADD COLUMN IF NOT EXISTS modifie_le TIMESTAMP DEFAULT NOW(); -DROP TRIGGER IF EXISTS trg_enfants_parents_set_modifie_le ON enfants_parents; -CREATE TRIGGER trg_enfants_parents_set_modifie_le -BEFORE UPDATE ON enfants_parents -FOR EACH ROW EXECUTE FUNCTION set_modifie_le(); - --- ========== DOSSIERS -ALTER TABLE dossiers - ADD COLUMN IF NOT EXISTS cree_le TIMESTAMP DEFAULT NOW(), - ADD COLUMN IF NOT EXISTS modifie_le TIMESTAMP DEFAULT NOW(); -DROP TRIGGER IF EXISTS trg_dossiers_set_modifie_le ON dossiers; -CREATE TRIGGER trg_dossiers_set_modifie_le +CREATE TRIGGER trg_update_modifie_le_dossiers BEFORE UPDATE ON dossiers -FOR EACH ROW EXECUTE FUNCTION set_modifie_le(); +FOR EACH ROW +EXECUTE FUNCTION set_modifie_le(); --- ========== MESSAGES -ALTER TABLE messages - ADD COLUMN IF NOT EXISTS cree_le TIMESTAMP DEFAULT NOW(), - ADD COLUMN IF NOT EXISTS modifie_le TIMESTAMP DEFAULT NOW(); -DROP TRIGGER IF EXISTS trg_messages_set_modifie_le ON messages; -CREATE TRIGGER trg_messages_set_modifie_le -BEFORE UPDATE ON messages -FOR EACH ROW EXECUTE FUNCTION set_modifie_le(); - --- ========== CONTRATS -ALTER TABLE contrats - ADD COLUMN IF NOT EXISTS cree_le TIMESTAMP DEFAULT NOW(), - ADD COLUMN IF NOT EXISTS modifie_le TIMESTAMP DEFAULT NOW(); -DROP TRIGGER IF EXISTS trg_contrats_set_modifie_le ON contrats; -CREATE TRIGGER trg_contrats_set_modifie_le +CREATE TRIGGER trg_update_modifie_le_contrats BEFORE UPDATE ON contrats -FOR EACH ROW EXECUTE FUNCTION set_modifie_le(); +FOR EACH ROW +EXECUTE FUNCTION set_modifie_le(); --- ========== AVENANTS_CONTRATS -ALTER TABLE avenants_contrats - ADD COLUMN IF NOT EXISTS cree_le TIMESTAMP DEFAULT NOW(), - ADD COLUMN IF NOT EXISTS modifie_le TIMESTAMP DEFAULT NOW(); -DROP TRIGGER IF EXISTS trg_avenants_contrats_set_modifie_le ON avenants_contrats; -CREATE TRIGGER trg_avenants_contrats_set_modifie_le +CREATE TRIGGER trg_update_modifie_le_avenants BEFORE UPDATE ON avenants_contrats -FOR EACH ROW EXECUTE FUNCTION set_modifie_le(); +FOR EACH ROW +EXECUTE FUNCTION set_modifie_le(); --- ========== EVENEMENTS -ALTER TABLE evenements - ADD COLUMN IF NOT EXISTS cree_le TIMESTAMP DEFAULT NOW(), - ADD COLUMN IF NOT EXISTS modifie_le TIMESTAMP DEFAULT NOW(); -DROP TRIGGER IF EXISTS trg_evenements_set_modifie_le ON evenements; -CREATE TRIGGER trg_evenements_set_modifie_le +CREATE TRIGGER trg_update_modifie_le_evenements BEFORE UPDATE ON evenements -FOR EACH ROW EXECUTE FUNCTION set_modifie_le(); +FOR EACH ROW +EXECUTE FUNCTION set_modifie_le(); --- ========== SIGNALEMENTS_BUGS -ALTER TABLE signalements_bugs - ADD COLUMN IF NOT EXISTS cree_le TIMESTAMP DEFAULT NOW(), - ADD COLUMN IF NOT EXISTS modifie_le TIMESTAMP DEFAULT NOW(); -DROP TRIGGER IF EXISTS trg_signalements_bugs_set_modifie_le ON signalements_bugs; -CREATE TRIGGER trg_signalements_bugs_set_modifie_le -BEFORE UPDATE ON signalements_bugs -FOR EACH ROW EXECUTE FUNCTION set_modifie_le(); - --- ========== UPLOADS -ALTER TABLE uploads - ADD COLUMN IF NOT EXISTS cree_le TIMESTAMP DEFAULT NOW(), - ADD COLUMN IF NOT EXISTS modifie_le TIMESTAMP DEFAULT NOW(); -DROP TRIGGER IF EXISTS trg_uploads_set_modifie_le ON uploads; -CREATE TRIGGER trg_uploads_set_modifie_le -BEFORE UPDATE ON uploads -FOR EACH ROW EXECUTE FUNCTION set_modifie_le(); - --- ========== NOTIFICATIONS -ALTER TABLE notifications - ADD COLUMN IF NOT EXISTS cree_le TIMESTAMP DEFAULT NOW(), - ADD COLUMN IF NOT EXISTS modifie_le TIMESTAMP DEFAULT NOW(); -DROP TRIGGER IF EXISTS trg_notifications_set_modifie_le ON notifications; -CREATE TRIGGER trg_notifications_set_modifie_le -BEFORE UPDATE ON notifications -FOR EACH ROW EXECUTE FUNCTION set_modifie_le(); - --- ========== VALIDATIONS -ALTER TABLE validations - ADD COLUMN IF NOT EXISTS cree_le TIMESTAMP DEFAULT NOW(), - ADD COLUMN IF NOT EXISTS modifie_le TIMESTAMP DEFAULT NOW(); -DROP TRIGGER IF EXISTS trg_validations_set_modifie_le ON validations; -CREATE TRIGGER trg_validations_set_modifie_le +CREATE TRIGGER trg_update_modifie_le_validations BEFORE UPDATE ON validations -FOR EACH ROW EXECUTE FUNCTION set_modifie_le(); +FOR EACH ROW +EXECUTE FUNCTION set_modifie_le(); + +-- =========================================== +-- 2. Empêcher plusieurs contrats actifs par dossier +-- =========================================== +CREATE OR REPLACE FUNCTION prevent_multiple_active_contrats() +RETURNS TRIGGER AS $$ +DECLARE + nb_actifs INT; +BEGIN + IF NEW.statut IN ('en_attente_signature','valide') THEN + SELECT COUNT(*) INTO nb_actifs + FROM contrats + WHERE id_dossier = NEW.id_dossier + AND statut IN ('en_attente_signature','valide') + AND id <> COALESCE(NEW.id, gen_random_uuid()); + + IF nb_actifs > 0 THEN + RAISE EXCEPTION 'Un contrat actif existe déjà pour ce dossier (%).', NEW.id_dossier; + END IF; + END IF; + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER trg_one_active_contrat_per_dossier +BEFORE INSERT OR UPDATE ON contrats +FOR EACH ROW +EXECUTE FUNCTION prevent_multiple_active_contrats(); + +-- =========================================== +-- 3. Empêcher événements incohérents +-- - Pas de chevauchement de congés pour le même AM +-- =========================================== +CREATE OR REPLACE FUNCTION prevent_overlapping_events() +RETURNS TRIGGER AS $$ +DECLARE + nb_overlap INT; +BEGIN + IF NEW.id_am IS NOT NULL AND NEW.date_debut IS NOT NULL AND NEW.date_fin IS NOT NULL THEN + SELECT COUNT(*) INTO nb_overlap + FROM evenements ev + WHERE ev.id_am = NEW.id_am + AND ev.id <> COALESCE(NEW.id, gen_random_uuid()) + AND ev.date_fin >= NEW.date_debut + AND ev.date_debut <= NEW.date_fin; + + IF nb_overlap > 0 THEN + RAISE EXCEPTION 'Conflit d''événements : chevauchement détecté pour AM %', NEW.id_am; + END IF; + END IF; + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER trg_no_overlapping_am_events +BEFORE INSERT OR UPDATE ON evenements +FOR EACH ROW +EXECUTE FUNCTION prevent_overlapping_events(); diff --git a/scripts/clean_csv.py b/scripts/clean_csv.py new file mode 100755 index 0000000..33683c6 --- /dev/null +++ b/scripts/clean_csv.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python3 +import csv +import sys +from pathlib import Path + +def clean_csv_file(file_path: Path): + """ + Nettoie un CSV directement en place : + - remplace "NULL" par "" + - supprime les espaces parasites + - force le même nombre de colonnes que l'en-tête + """ + cleaned_rows = [] + + with open(file_path, "r", encoding="utf-8-sig", newline="") as infile: + reader = csv.reader(infile) + try: + header = next(reader) + except StopIteration: + print(f"[⚠️] Fichier vide : {file_path}") + return + + nb_cols = len(header) + cleaned_rows.append([h.strip() for h in header]) + + for i, row in enumerate(reader, start=2): + # Ajuste le nombre de colonnes + if len(row) < nb_cols: + row.extend([""] * (nb_cols - len(row))) + elif len(row) > nb_cols: + row = row[:nb_cols] + + # Nettoyage cellule par cellule + row = [cell.strip().replace("NULL", "") for cell in row] + cleaned_rows.append(row) + + # Réécriture dans le même fichier + with open(file_path, "w", encoding="utf-8", newline="") as outfile: + writer = csv.writer(outfile) + writer.writerows(cleaned_rows) + + print(f"[✔] Nettoyé : {file_path}") + + +def main(): + if len(sys.argv) > 1: + base_dir = Path(sys.argv[1]) + else: + base_dir = Path("bdd/data_test") + + print(f"🔎 Nettoyage des CSV dans : {base_dir}") + for file_path in base_dir.glob("*.csv"): + clean_csv_file(file_path) + + print(f"✅ Terminé. Les fichiers CSV ont été écrasés (nettoyés en place).") + + +if __name__ == "__main__": + main() diff --git a/scripts/verify_data.sql b/scripts/verify_data.sql new file mode 100644 index 0000000..914eaab --- /dev/null +++ b/scripts/verify_data.sql @@ -0,0 +1,64 @@ +-- ============================================== +-- Script de vérification d'intégrité des données +-- ============================================== + +-- 1) Vérifier unicité des emails +SELECT email, COUNT(*) AS doublons +FROM utilisateurs +GROUP BY email +HAVING COUNT(*) > 1; + +-- 2) Vérifier utilisateurs sans rôle +SELECT id, email +FROM utilisateurs +WHERE role IS NULL OR role = ''; + +-- 3) Vérifier cohérence enfants → parents +SELECT e.id AS enfant_id, e.nom, e.prenom +FROM enfants e +LEFT JOIN enfants_parents ep ON e.id = ep.enfant_id +WHERE ep.parent_id IS NULL; + +-- 4) Vérifier cohérence enfants_parents → parents existants +SELECT ep.enfant_id, ep.parent_id +FROM enfants_parents ep +LEFT JOIN parents p ON ep.parent_id = p.id +WHERE p.id IS NULL; + +-- 5) Vérifier cohérence dossiers → utilisateurs +SELECT d.id, d.parent_id +FROM dossiers d +LEFT JOIN parents p ON d.parent_id = p.id +WHERE p.id IS NULL; + +-- 6) Vérifier cohérence contrats → utilisateurs et assistantes +SELECT c.id, c.parent_id, c.assistante_id +FROM contrats c +LEFT JOIN parents p ON c.parent_id = p.id +LEFT JOIN assistantes_maternelles am ON c.assistante_id = am.id +WHERE p.id IS NULL OR am.id IS NULL; + +-- 7) Vérifier cohérence validations → utilisateurs +SELECT v.id, v.valide_par +FROM validations v +LEFT JOIN utilisateurs u ON v.valide_par = u.id +WHERE u.id IS NULL; + +-- 8) Vérifier cohérence dates enfants +SELECT id, nom, prenom, date_naissance +FROM enfants +WHERE date_naissance > CURRENT_DATE; + +-- 9) Vérifier cohérence date agrément assistantes +SELECT id, nom, prenom, date_agrement +FROM assistantes_maternelles +WHERE date_agrement > CURRENT_DATE; + +-- 10) Vérifier NULL obligatoires (ex: NIR des assistantes) +SELECT id, nom, prenom +FROM assistantes_maternelles +WHERE nir IS NULL OR nir = ''; + +-- ============================================== +-- Fin du script +-- ============================================== diff --git a/seed/02_seed.sql b/seed/02_seed.sql deleted file mode 100644 index c8ef3b4..0000000 --- a/seed/02_seed.sql +++ /dev/null @@ -1,221 +0,0 @@ --- ============================================================ --- 02_seed.sql : Données de test réalistes (Sprint 1) --- A exécuter après : --- 01_init.sql (création des tables) --- 02_indexes.sql --- 03_checks.sql --- 04_fk_policies.sql --- 05_triggers.sql --- ============================================================ - -BEGIN; - --- ------------------------------------------------------------ --- Utilisateurs (super_admin, gestionnaire, 2 parents + co-parent, 1 AM) --- ------------------------------------------------------------ - --- UUIDs fixes pour faciliter les tests / jointures --- super_admin -INSERT INTO utilisateurs (id, courriel, mot_de_passe_hash, prenom, nom, role, statut) -VALUES ('11111111-1111-1111-1111-111111111111', 'admin@ptits-pas.fr', '$2y$10$hashAdminIci', 'Super', 'Admin', 'super_admin', 'accepte') -ON CONFLICT (id) DO NOTHING; - --- gestionnaire -INSERT INTO utilisateurs (id, courriel, mot_de_passe_hash, prenom, nom, role, statut) -VALUES ('22222222-2222-2222-2222-222222222222', 'gestion@ptits-pas.fr', '$2y$10$hashGestionIci', 'Gina', 'Gestion', 'gestionnaire', 'accepte') -ON CONFLICT (id) DO NOTHING; - --- parent #1 -INSERT INTO utilisateurs (id, courriel, mot_de_passe_hash, prenom, nom, role, statut) -VALUES ('33333333-3333-3333-3333-333333333333', 'parent1@example.com', '$2y$10$hashParent1', 'Paul', 'Parent', 'parent', 'accepte') -ON CONFLICT (id) DO NOTHING; - --- co-parent du parent #1 -INSERT INTO utilisateurs (id, courriel, mot_de_passe_hash, prenom, nom, role, statut) -VALUES ('44444444-4444-4444-4444-444444444444', 'coparent1@example.com', '$2y$10$hashCoParent1', 'Clara', 'CoParent', 'parent', 'accepte') -ON CONFLICT (id) DO NOTHING; - --- parent #2 -INSERT INTO utilisateurs (id, courriel, mot_de_passe_hash, prenom, nom, role, statut) -VALUES ('55555555-5555-5555-5555-555555555555', 'parent2@example.com', '$2y$10$hashParent2', 'Nora', 'Parent', 'parent', 'accepte') -ON CONFLICT (id) DO NOTHING; - --- assistante maternelle #1 -INSERT INTO utilisateurs (id, courriel, mot_de_passe_hash, prenom, nom, role, statut) -VALUES ('66666666-6666-6666-6666-666666666666', 'am1@example.com', '$2y$10$hashAM1', 'Alice', 'AM', 'am', 'accepte') -ON CONFLICT (id) DO NOTHING; - --- ------------------------------------------------------------ --- Extensions de rôles (parents / AM) --- ------------------------------------------------------------ - --- parents (id_co_parent nullable) -INSERT INTO parents (id_utilisateur, id_co_parent) -VALUES ('33333333-3333-3333-3333-333333333333', '44444444-4444-4444-4444-444444444444') -- parent1 avec co-parent -ON CONFLICT (id_utilisateur) DO NOTHING; - -INSERT INTO parents (id_utilisateur, id_co_parent) -VALUES ('55555555-5555-5555-5555-555555555555', NULL) -- parent2 sans co-parent -ON CONFLICT (id_utilisateur) DO NOTHING; - --- assistantes_maternelles -INSERT INTO assistantes_maternelles (id_utilisateur, numero_agrement, nb_max_enfants, disponible, ville_residence) -VALUES ('66666666-6666-6666-6666-666666666666', 'AGR-2025-0001', 3, true, 'Lille') -ON CONFLICT (id_utilisateur) DO NOTHING; - --- ------------------------------------------------------------ --- Enfants --- - child A : déjà né (statut = 'actif' et date_naissance requise) --- - child B : à naître (statut = 'a_naitre' et date_prevue_naissance requise) --- ------------------------------------------------------------ - -INSERT INTO enfants (id, prenom, nom, statut, date_naissance, jumeau_multiple) -VALUES ('aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', 'Léo', 'Parent', 'actif', '2022-04-12', false) -ON CONFLICT (id) DO NOTHING; - -INSERT INTO enfants (id, prenom, nom, statut, date_prevue_naissance, jumeau_multiple) -VALUES ('bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb', 'Mila', 'Parent', 'a_naitre', '2026-02-15', false) -ON CONFLICT (id) DO NOTHING; - --- ------------------------------------------------------------ --- Liaison N:N parents_enfants --- - parent1 + co-parent ↔ enfant A & B --- - parent2 ↔ enfant B --- ------------------------------------------------------------ - --- parent1 ↔ enfant A -INSERT INTO enfants_parents (id_parent, id_enfant) -VALUES ('33333333-3333-3333-3333-333333333333', 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa') -ON CONFLICT DO NOTHING; - --- co-parent1 ↔ enfant A -INSERT INTO enfants_parents (id_parent, id_enfant) -VALUES ('44444444-4444-4444-4444-444444444444', 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa') -ON CONFLICT DO NOTHING; - --- parent1 ↔ enfant B -INSERT INTO enfants_parents (id_parent, id_enfant) -VALUES ('33333333-3333-3333-3333-333333333333', 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb') -ON CONFLICT DO NOTHING; - --- parent2 ↔ enfant B -INSERT INTO enfants_parents (id_parent, id_enfant) -VALUES ('55555555-5555-5555-5555-555555555555', 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb') -ON CONFLICT DO NOTHING; - --- ------------------------------------------------------------ --- Dossier (parent1 ↔ enfant A) --- ------------------------------------------------------------ -INSERT INTO dossiers (id, id_parent, id_enfant, presentation, type_contrat, repas, budget, planning_souhaite) -VALUES ( - 'dddddddd-dddd-dddd-dddd-dddddddddddd', - '33333333-3333-3333-3333-333333333333', - 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', - 'Besoin garde périscolaire lundi/mardi/jeudi/vendredi.', - 'mensuel', - true, - 600.00, - '{"lun_ven":{"matin":false,"midi":true,"soir":true}}'::jsonb -) -ON CONFLICT (id) DO NOTHING; - --- ------------------------------------------------------------ --- Messages (sur le dossier) --- ------------------------------------------------------------ -INSERT INTO messages (id, id_dossier, id_expediteur, contenu) -VALUES ('m0000000-0000-0000-0000-000000000001', 'dddddddd-dddd-dddd-dddd-dddddddddddd', '33333333-3333-3333-3333-333333333333', 'Bonjour, nous cherchons une garde périscolaire.') -ON CONFLICT (id) DO NOTHING; - -INSERT INTO messages (id, id_dossier, id_expediteur, contenu) -VALUES ('m0000000-0000-0000-0000-000000000002', 'dddddddd-dddd-dddd-dddd-dddddddddddd', '66666666-6666-6666-6666-666666666666', 'Bonjour, je suis disponible les soirs. Discutons du contrat.') -ON CONFLICT (id) DO NOTHING; - --- ------------------------------------------------------------ --- Contrat (1:1 avec le dossier) --- ------------------------------------------------------------ -INSERT INTO contrats (id, id_dossier, planning, tarif_horaire, indemnites_repas, date_debut, statut, signe_parent, signe_am) -VALUES ( - 'cccccccc-cccc-cccc-cccc-cccccccccccc', - 'dddddddd-dddd-dddd-dddd-dddddddddddd', - '{"lun_ven":{"17h-19h":true}}'::jsonb, - 12.50, - 3.50, - '2025-09-01', - 'brouillon', - false, - false -) -ON CONFLICT (id) DO NOTHING; - --- ------------------------------------------------------------ --- Avenant de contrat --- ------------------------------------------------------------ -INSERT INTO avenants_contrats (id, id_contrat, modifications, initie_par, statut) -VALUES ( - 'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee', - 'cccccccc-cccc-cccc-cccc-cccccccccccc', - '{"changement_horaire":{"vendredi":{"17h-20h":true}}}'::jsonb, - '33333333-3333-3333-3333-333333333333', - 'propose' -) -ON CONFLICT (id) DO NOTHING; - --- ------------------------------------------------------------ --- Événement (absence enfant) --- ------------------------------------------------------------ -INSERT INTO evenements (id, type, id_enfant, id_am, id_parent, cree_par, date_debut, date_fin, commentaires, statut, urgence) -VALUES ( - 'e0000000-0000-0000-0000-000000000001', - 'absence_enfant', - 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', - '66666666-6666-6666-6666-666666666666', - '33333333-3333-3333-3333-333333333333', - '33333333-3333-3333-3333-333333333333', - '2025-09-12', - '2025-09-12', - 'Enfant malade (rhume).', - 'propose', - false -) -ON CONFLICT (id) DO NOTHING; - --- ------------------------------------------------------------ --- Upload (justificatif lié au dossier) --- ------------------------------------------------------------ -INSERT INTO uploads (id, id_utilisateur, id_dossier_lie, fichier_url, type_fichier) -VALUES ( - 'u0000000-0000-0000-0000-000000000001', - '33333333-3333-3333-3333-333333333333', - 'dddddddd-dddd-dddd-dddd-dddddddddddd', - '/uploads/justificatifs/dossier_dddddddd_attestation.pdf', - 'application/pdf' -) -ON CONFLICT (id) DO NOTHING; - --- ------------------------------------------------------------ --- Notification (pour le parent1) --- ------------------------------------------------------------ -INSERT INTO notifications (id, id_utilisateur, type, contenu, lu) -VALUES ( - 'n0000000-0000-0000-0000-000000000001', - '33333333-3333-3333-3333-333333333333', - 'nouveau_message', - 'Vous avez un nouveau message sur le dossier #dddd…', - false -) -ON CONFLICT (id) DO NOTHING; - --- ------------------------------------------------------------ --- Validation (compte de l’AM validé par le gestionnaire) --- ------------------------------------------------------------ -INSERT INTO validations (id, id_utilisateur, statut, commentaire, cree_le) -VALUES ( - 'v0000000-0000-0000-0000-000000000001', - '66666666-6666-6666-6666-666666666666', - 'accepte', - 'Dossier AM vérifié par gestionnaire.', - NOW() -) -ON CONFLICT (id) DO NOTHING; - -COMMIT; diff --git a/tests/sql/verify.sql b/tests/sql/verify.sql index 05593dd..836fd3c 100644 --- a/tests/sql/verify.sql +++ b/tests/sql/verify.sql @@ -1,23 +1,5 @@ - -- ============================================================ -- verify.sql — Jeux de requêtes de vérification (Sprint 1) --- Objectifs : --- 1) Vérifier l'intégrité fonctionnelle (joins, données seedées) --- 2) Détecter rapidement des problèmes d'index/perf (EXPLAIN) --- 3) Servir de référence pour le back/front (requêtes typiques) --- --- Usage (Docker) : --- docker compose exec -T postgres \ --- psql -U ${POSTGRES_USER} -d ${POSTGRES_DB} \ --- -f /docker-entrypoint-initdb.d/tests/verify.sql --- --- Pré-requis : --- - 01_init.sql --- - 02_indexes.sql --- - 03_checks.sql --- - 04_fk_policies.sql --- - 05_triggers.sql --- - 02_seed.sql (pour résultats non vides) -- ============================================================ \echo '=== 0) Version & horodatage ====================================' @@ -38,9 +20,9 @@ ORDER BY nb DESC; \echo '=== 3) Parents avec co-parents (NULL si pas de co-parent) ======' SELECT p.id_utilisateur AS parent_id, - u.courriel AS parent_email, + u.email AS parent_email, p.id_co_parent, - uc.courriel AS co_parent_email + uc.email AS co_parent_email FROM parents p JOIN utilisateurs u ON u.id = p.id_utilisateur LEFT JOIN utilisateurs uc ON uc.id = p.id_co_parent @@ -52,21 +34,21 @@ FROM enfants ORDER BY nom, prenom; \echo '=== 5) Liaison N:N parents_enfants =============================' -SELECT ep.id_parent, up.courriel AS parent_email, ep.id_enfant, e.prenom AS enfant +SELECT ep.id_parent, up.email AS parent_email, ep.id_enfant, e.prenom AS enfant FROM enfants_parents ep JOIN utilisateurs up ON up.id = ep.id_parent JOIN enfants e ON e.id = ep.id_enfant ORDER BY parent_email, enfant; \echo '=== 6) Dossiers (parent, enfant, statut) =======================' -SELECT d.id, up.courriel AS parent_email, e.prenom AS enfant, d.statut, d.budget +SELECT d.id, up.email AS parent_email, e.prenom AS enfant, d.statut, d.budget FROM dossiers d JOIN utilisateurs up ON up.id = d.id_parent JOIN enfants e ON e.id = d.id_enfant ORDER BY d.cree_le DESC; \echo '=== 7) Messages par dossier (ordre chronologique) ==============' -SELECT m.id, m.id_dossier, m.id_expediteur, ue.courriel AS expediteur_email, m.contenu, m.cree_le +SELECT m.id, m.id_dossier, m.id_expediteur, ue.email AS expediteur_email, m.contenu, m.cree_le FROM messages m LEFT JOIN utilisateurs ue ON ue.id = m.id_expediteur ORDER BY m.id_dossier, m.cree_le; @@ -87,22 +69,20 @@ WHERE ev.date_debut >= (NOW()::date - INTERVAL '30 days') ORDER BY ev.date_debut DESC; \echo '=== 10) Uploads & notifications récentes =======================' -SELECT u.courriel, up.fichier_url, up.type_fichier, up.cree_le +SELECT u.email, up.fichier_url, up.type AS fichier_type, up.cree_le FROM uploads up LEFT JOIN utilisateurs u ON u.id = up.id_utilisateur ORDER BY up.cree_le DESC; -SELECT u.courriel, n.type, n.contenu, n.lu, n.cree_le +SELECT u.email, n.contenu, n.lu, n.cree_le FROM notifications n LEFT JOIN utilisateurs u ON u.id = n.id_utilisateur ORDER BY n.cree_le DESC; \echo '=== 11) Validations (qui a validé quoi) ========================' -SELECT v.id, uu.courriel AS utilisateur_valide, - uv.courriel AS valide_par, v.statut, v.commentaire, v.cree_le +SELECT v.id, u.email AS utilisateur_email, v.type, v.statut, v.cree_le FROM validations v -LEFT JOIN utilisateurs uu ON uu.id = v.id_utilisateur -LEFT JOIN utilisateurs uv ON uv.id = v.valide_par +LEFT JOIN utilisateurs u ON u.id = v.id_utilisateur ORDER BY v.cree_le DESC; -- ============================================================ @@ -176,19 +156,15 @@ WHERE d.id_parent = '33333333-3333-3333-3333-333333333333' ORDER BY d.cree_le DESC; \echo '=== 14) JSONB : exemples de filtrage ===========================' --- Recherche de dossiers où planning_souhaite contient midi=true un jour ouvré --- (Index GIN recommandé si usage intensif : cf. 02_indexes.sql) SELECT d.id, d.planning_souhaite FROM dossiers d WHERE d.planning_souhaite @> '{"lun_ven":{"midi":true}}'; --- Contrats : présence d’un créneau donné SELECT c.id, c.planning FROM contrats c WHERE c.planning @> '{"lun_ven":{"17h-19h":true}}'; \echo '=== 15) Sanity check final ====================================' --- Quelques totaux utiles SELECT (SELECT COUNT(*) FROM utilisateurs) AS nb_utilisateurs, (SELECT COUNT(*) FROM parents) AS nb_parents, diff --git a/verify.log b/verify.log new file mode 100644 index 0000000..8ff5aa4 --- /dev/null +++ b/verify.log @@ -0,0 +1,188 @@ +=== 0) Version & horodatage ==================================== + version +-------------------------------------------------------------------------------------------------------------------- + PostgreSQL 17.6 (Debian 17.6-1.pgdg13+1) on x86_64-pc-linux-gnu, compiled by gcc (Debian 14.2.0-19) 14.2.0, 64-bit +(1 row) + + executed_at +------------------------------- + 2025-09-19 08:16:02.305781+00 +(1 row) + +=== 1) Comptes & répartition par rôle ========================== + role | nb +-----------------------+---- + parent | 6 + assistante_maternelle | 3 + administrateur | 1 + gestionnaire | 1 + super_admin | 1 +(5 rows) + +=== 2) Utilisateurs en attente / acceptés / rejetés ============ + statut | nb +--------+---- + actif | 12 +(1 row) + +=== 3) Parents avec co-parents (NULL si pas de co-parent) ====== + parent_id | parent_email | id_co_parent | co_parent_email +--------------------------------------+------------------------------+--------------------------------------+------------------------------ + f1d3c5b7-8a9e-4f2d-9c1b-3e7a5d8c2f1b | amelie.durand@ptits-pas.fr | b6c4d2e3-5f7a-4b8c-9d1e-2a3c5f7b8d9e | julien.rousseau@ptits-pas.fr + c4e2d1f5-6b7a-4c3d-8f2a-1e9c3b5a7d6f | claire.martin@ptits-pas.fr | | + d3e5f7a9-1c2b-4d6e-8f3a-2b4c6d8e9f1a | david.lecomte@ptits-pas.fr | | + b6c4d2e3-5f7a-4b8c-9d1e-2a3c5f7b8d9e | julien.rousseau@ptits-pas.fr | f1d3c5b7-8a9e-4f2d-9c1b-3e7a5d8c2f1b | amelie.durand@ptits-pas.fr +(4 rows) + +=== 4) Enfants (statut, dates cohérentes) ====================== + id | prenom | nom | statut | date_naissance | date_prevue_naissance +--------------------------------------+--------+----------+-----------+----------------+----------------------- + 5e8574b7-63e6-4d48-9af3-8d3bf7a6a6cf | Emma | Dupont | actif | 2020-06-01 | + edd19cd1-bb67-4f14-8a37-c66b75c94537 | Lucas | Durand | scolarise | 2018-09-15 | + e6f7a8b9-c1d2-4e3f-5a6b-7c8d9e0f1a2b | Maxime | Lecomte | actif | 2023-04-15 | + e1a2b3c4-d5e6-4f7a-8b9c-1d2e3f4a5b6c | Emma | Martin | actif | 2023-02-15 | + e3c4d5e6-f7a8-4b9c-1d2e-3f4a5b6c7d8e | Léa | Martin | actif | 2023-02-15 | + e2b3c4d5-e6f7-4a8b-9c1d-2e3f4a5b6c7d | Noah | Martin | actif | 2023-02-15 | + e4d5e6f7-a8b9-4c1d-2e3f-4a5b6c7d8e9f | Chloé | Rousseau | actif | 2022-04-20 | + e5e6f7a8-b9c1-4d2e-3f4a-5b6c7d8e9f1a | Hugo | Rousseau | actif | 2024-03-10 | + a5c3268e-07eb-41a4-9f6c-2f9f16f37c3d | | | actif | 2020-01-01 | 2025-01-01 +(9 rows) + +=== 5) Liaison N:N parents_enfants ============================= + id_parent | parent_email | id_enfant | enfant +--------------------------------------+------------------------------+--------------------------------------+-------- + f1d3c5b7-8a9e-4f2d-9c1b-3e7a5d8c2f1b | amelie.durand@ptits-pas.fr | e4d5e6f7-a8b9-4c1d-2e3f-4a5b6c7d8e9f | Chloé + f1d3c5b7-8a9e-4f2d-9c1b-3e7a5d8c2f1b | amelie.durand@ptits-pas.fr | e5e6f7a8-b9c1-4d2e-3f4a-5b6c7d8e9f1a | Hugo + c4e2d1f5-6b7a-4c3d-8f2a-1e9c3b5a7d6f | claire.martin@ptits-pas.fr | e1a2b3c4-d5e6-4f7a-8b9c-1d2e3f4a5b6c | Emma + c4e2d1f5-6b7a-4c3d-8f2a-1e9c3b5a7d6f | claire.martin@ptits-pas.fr | e3c4d5e6-f7a8-4b9c-1d2e-3f4a5b6c7d8e | Léa + c4e2d1f5-6b7a-4c3d-8f2a-1e9c3b5a7d6f | claire.martin@ptits-pas.fr | e2b3c4d5-e6f7-4a8b-9c1d-2e3f4a5b6c7d | Noah + d3e5f7a9-1c2b-4d6e-8f3a-2b4c6d8e9f1a | david.lecomte@ptits-pas.fr | e6f7a8b9-c1d2-4e3f-5a6b-7c8d9e0f1a2b | Maxime + b6c4d2e3-5f7a-4b8c-9d1e-2a3c5f7b8d9e | julien.rousseau@ptits-pas.fr | e4d5e6f7-a8b9-4c1d-2e3f-4a5b6c7d8e9f | Chloé + b6c4d2e3-5f7a-4b8c-9d1e-2a3c5f7b8d9e | julien.rousseau@ptits-pas.fr | e5e6f7a8-b9c1-4d2e-3f4a-5b6c7d8e9f1a | Hugo +(8 rows) + +=== 6) Dossiers (parent, enfant, statut) ======================= + id | parent_email | enfant | statut | budget +--------------------------------------+----------------------------+--------+--------+--------- + bb9c30a0-60b4-4832-9947-8a7d2366673d | amelie.durand@ptits-pas.fr | Emma | envoye | 1200.00 +(1 row) + +=== 7) Messages par dossier (ordre chronologique) ============== + id | id_dossier | id_expediteur | expediteur_email | contenu | cree_le +----+------------+---------------+------------------+---------+--------- +(0 rows) + +=== 8) Contrats 1:1 avec dossier + avenants ==================== + contrat_id | id_dossier | statut | nb_avenants +--------------------------------------+--------------------------------------+-----------+------------- + f09c6ffa-4627-4aa8-b20b-829c2c828f0d | bb9c30a0-60b4-4832-9947-8a7d2366673d | brouillon | 0 +(1 row) + +=== 9) Evénements par enfant (30 derniers jours) ============== + id | type | id_enfant | enfant | date_debut | date_fin | statut +----+------+-----------+--------+------------+----------+-------- +(0 rows) + +=== 10) Uploads & notifications récentes ======================= + email | fichier_url | fichier_type | cree_le +---------------------+--------------------------------+--------------+------------------------------- + parent1@example.com | https://placeholder.local/file | image | 2025-09-02 12:57:35.140078+00 +(1 row) + + email | contenu | lu | cree_le +---------------------+-----------------------------+----+------------------------------- + parent1@example.com | Votre dossier a été accepté | f | 2025-09-02 12:57:42.845264+00 +(1 row) + +=== 11) Validations (qui a validé quoi) ======================== + id | utilisateur_email | type | statut | cree_le +--------------------------------------+---------------------------+----------+--------+------------------------------- + fcc45701-5708-4368-b467-b95ddb7e1580 | marie.dubois@ptits-pas.fr | | valide | 2025-09-09 14:52:47.339858+00 + be1c4779-341b-436d-b17e-8bc486d22ef8 | am1@example.com | | valide | 2025-09-09 14:47:01.963573+00 + 8ec99565-e8c2-469f-9641-01b99b8281eb | am1@example.com | identité | valide | 2025-09-02 12:57:49.846424+00 +(3 rows) + +=== 12) Orphelins potentiels (doivent renvoyer 0 ligne) ======= + id | id_dossier | id_expediteur | contenu | re_redige_par_ia | cree_le +----+------------+---------------+---------+------------------+--------- +(0 rows) + + id_parent | id_enfant +-----------+----------- +(0 rows) + + id | id_dossier | planning | tarif_horaire | indemnites_repas | date_debut | statut | signe_parent | signe_am | finalise_le | cree_le | modifie_le +----+------------+----------+---------------+------------------+------------+--------+--------------+----------+-------------+---------+------------ +(0 rows) + + id | id_contrat | modifications | initie_par | statut | cree_le | modifie_le +----+------------+---------------+------------+--------+---------+------------ +(0 rows) + + id | type | id_enfant | id_am | id_parent | cree_par | date_debut | date_fin | commentaires | statut | delai_grace | urgent | cree_le | modifie_le +----+------+-----------+-------+-----------+----------+------------+----------+--------------+--------+-------------+--------+---------+------------ +(0 rows) + +=== 13) Performance : EXPLAIN sur requêtes clés =============== + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------------------------- + Limit (cost=11.31..11.31 rows=3 width=89) (actual time=0.027..0.029 rows=0 loops=1) + -> Sort (cost=11.31..11.31 rows=3 width=89) (actual time=0.026..0.027 rows=0 loops=1) + Sort Key: cree_le DESC + Sort Method: quicksort Memory: 25kB + -> Bitmap Heap Scan on messages m (cost=4.17..11.28 rows=3 width=89) (actual time=0.021..0.021 rows=0 loops=1) + Recheck Cond: (id_dossier = 'dddddddd-dddd-dddd-dddd-dddddddddddd'::uuid) + -> Bitmap Index Scan on idx_messages_id_dossier_cree_le (cost=0.00..4.17 rows=3 width=0) (actual time=0.011..0.011 rows=0 loops=1) + Index Cond: (id_dossier = 'dddddddd-dddd-dddd-dddd-dddddddddddd'::uuid) + Planning Time: 0.837 ms + Execution Time: 0.079 ms +(10 rows) + + QUERY PLAN +----------------------------------------------------------------------------------------------------------------------------------------------------- + Index Scan using idx_evenements_id_enfant_date_debut on evenements ev (cost=0.15..8.17 rows=1 width=161) (actual time=0.006..0.007 rows=0 loops=1) + Index Cond: ((id_enfant = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa'::uuid) AND (date_debut >= '2025-01-01 00:00:00+00'::timestamp with time zone)) + Planning Time: 0.107 ms + Execution Time: 0.099 ms +(4 rows) + + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------ + Limit (cost=9.52..9.53 rows=2 width=73) (actual time=0.020..0.021 rows=0 loops=1) + -> Sort (cost=9.52..9.53 rows=2 width=73) (actual time=0.019..0.020 rows=0 loops=1) + Sort Key: cree_le DESC + Sort Method: quicksort Memory: 25kB + -> Bitmap Heap Scan on notifications n (cost=4.17..9.51 rows=2 width=73) (actual time=0.014..0.014 rows=0 loops=1) + Recheck Cond: ((id_utilisateur = '33333333-3333-3333-3333-333333333333'::uuid) AND (NOT lu)) + -> Bitmap Index Scan on idx_notifications_user_lu_cree_le (cost=0.00..4.17 rows=2 width=0) (actual time=0.008..0.008 rows=0 loops=1) + Index Cond: ((id_utilisateur = '33333333-3333-3333-3333-333333333333'::uuid) AND (lu = false)) + Planning Time: 0.122 ms + Execution Time: 0.043 ms +(10 rows) + + QUERY PLAN +----------------------------------------------------------------------------------------------------------------------------------------------------------------- + Sort (cost=8.18..8.18 rows=1 width=267) (actual time=0.429..0.431 rows=0 loops=1) + Sort Key: cree_le DESC + Sort Method: quicksort Memory: 25kB + -> Index Scan using idx_dossiers_id_parent_enfant_statut_cree_le on dossiers d (cost=0.15..8.17 rows=1 width=267) (actual time=0.419..0.419 rows=0 loops=1) + Index Cond: ((id_parent = '33333333-3333-3333-3333-333333333333'::uuid) AND (id_enfant = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa'::uuid)) + Planning Time: 1.115 ms + Execution Time: 0.476 ms +(7 rows) + +=== 14) JSONB : exemples de filtrage =========================== + id | planning_souhaite +----+------------------- +(0 rows) + + id | planning +----+---------- +(0 rows) + +=== 15) Sanity check final ==================================== + nb_utilisateurs | nb_parents | nb_am | nb_enfants | nb_liens_parent_enfant | nb_dossiers | nb_messages | nb_contrats | nb_avenants | nb_evenements | nb_uploads | nb_notifications | nb_validations +-----------------+------------+-------+------------+------------------------+-------------+-------------+-------------+-------------+---------------+------------+------------------+---------------- + 12 | 4 | 2 | 9 | 8 | 1 | 0 | 1 | 0 | 1 | 1 | 1 | 3 +(1 row) +