-- Backfill #103 : attribuer un numero_dossier aux entrées existantes (NULL) -- Famille = lien co_parent OU partage d'au moins un enfant (même dossier). -- Ordre : par année, AM puis familles (une entrée par famille), séquence 000001, 000002... -- À exécuter après 2026_numero_dossier.sql DO $$ DECLARE yr INT; seq INT; num TEXT; r RECORD; family_user_ids UUID[]; BEGIN -- Réinitialiser pour rejouer le backfill (cohérence AM + familles) UPDATE parents SET numero_dossier = NULL; UPDATE utilisateurs SET numero_dossier = NULL WHERE role IN ('parent', 'assistante_maternelle'); UPDATE assistantes_maternelles SET numero_dossier = NULL; FOR yr IN SELECT DISTINCT EXTRACT(YEAR FROM u.cree_le)::INT FROM utilisateurs u WHERE ( (u.role = 'assistante_maternelle' AND u.numero_dossier IS NULL) OR EXISTS (SELECT 1 FROM parents p WHERE p.id_utilisateur = u.id AND p.numero_dossier IS NULL) ) ORDER BY 1 LOOP seq := 0; -- 1) AM : par ordre de création FOR r IN SELECT u.id FROM utilisateurs u WHERE u.role = 'assistante_maternelle' AND u.numero_dossier IS NULL AND EXTRACT(YEAR FROM u.cree_le) = yr ORDER BY u.cree_le LOOP seq := seq + 1; num := yr || '-' || LPAD(seq::TEXT, 6, '0'); UPDATE utilisateurs SET numero_dossier = num WHERE id = r.id; UPDATE assistantes_maternelles SET numero_dossier = num WHERE id_utilisateur = r.id; END LOOP; -- 2) Familles : une entrée par "dossier" (co_parent OU enfants partagés) -- family_rep = min(id) de la composante connexe (lien co_parent + partage d'enfants) FOR r IN WITH RECURSIVE links AS ( SELECT p.id_utilisateur AS p1, p.id_co_parent AS p2 FROM parents p WHERE p.id_co_parent IS NOT NULL UNION ALL SELECT p.id_co_parent AS p1, p.id_utilisateur AS p2 FROM parents p WHERE p.id_co_parent IS NOT NULL UNION ALL SELECT ep1.id_parent AS p1, ep2.id_parent AS p2 FROM enfants_parents ep1 JOIN enfants_parents ep2 ON ep2.id_enfant = ep1.id_enfant AND ep1.id_parent < ep2.id_parent UNION ALL SELECT ep2.id_parent AS p1, ep1.id_parent AS p2 FROM enfants_parents ep1 JOIN enfants_parents ep2 ON ep2.id_enfant = ep1.id_enfant AND ep1.id_parent < ep2.id_parent ), rec AS ( SELECT id_utilisateur AS id, id_utilisateur AS rep FROM parents UNION SELECT l.p2 AS id, LEAST(rec_alias.rep, l.p2) AS rep FROM links l JOIN rec rec_alias ON rec_alias.id = l.p1 ), family_rep AS ( SELECT id, MIN(rep::text)::uuid AS rep FROM rec GROUP BY id ), fam_ordered AS ( SELECT fr.rep AS family_rep, MIN(u.cree_le) AS cree_le FROM family_rep fr JOIN parents p ON p.id_utilisateur = fr.id JOIN utilisateurs u ON u.id = p.id_utilisateur WHERE p.numero_dossier IS NULL AND EXTRACT(YEAR FROM u.cree_le) = yr GROUP BY fr.rep ORDER BY MIN(u.cree_le) ) SELECT fo.family_rep FROM fam_ordered fo LOOP seq := seq + 1; num := yr || '-' || LPAD(seq::TEXT, 6, '0'); WITH RECURSIVE links AS ( SELECT p.id_utilisateur AS p1, p.id_co_parent AS p2 FROM parents p WHERE p.id_co_parent IS NOT NULL UNION ALL SELECT p.id_co_parent AS p1, p.id_utilisateur AS p2 FROM parents p WHERE p.id_co_parent IS NOT NULL UNION ALL SELECT ep1.id_parent AS p1, ep2.id_parent AS p2 FROM enfants_parents ep1 JOIN enfants_parents ep2 ON ep2.id_enfant = ep1.id_enfant AND ep1.id_parent < ep2.id_parent UNION ALL SELECT ep2.id_parent AS p1, ep1.id_parent AS p2 FROM enfants_parents ep1 JOIN enfants_parents ep2 ON ep2.id_enfant = ep1.id_enfant AND ep1.id_parent < ep2.id_parent ), rec AS ( SELECT id_utilisateur AS id, id_utilisateur AS rep FROM parents UNION SELECT l.p2 AS id, LEAST(rec_alias.rep, l.p2) AS rep FROM links l JOIN rec rec_alias ON rec_alias.id = l.p1 ), family_rep AS ( SELECT id, MIN(rep::text)::uuid AS rep FROM rec GROUP BY id ) SELECT array_agg(DISTINCT fr.id) INTO family_user_ids FROM family_rep fr WHERE fr.rep = r.family_rep; UPDATE utilisateurs SET numero_dossier = num WHERE id = ANY(family_user_ids); UPDATE parents SET numero_dossier = num WHERE id_utilisateur = ANY(family_user_ids); END LOOP; INSERT INTO numero_dossier_sequence (annee, prochain) VALUES (yr, seq + 1) ON CONFLICT (annee) DO UPDATE SET prochain = GREATEST(numero_dossier_sequence.prochain, seq + 1); END LOOP; END $$;