From 0e6e473e84c93f788138d5eaa2721d105fd6a897 Mon Sep 17 00:00:00 2001 From: Julien Martin Date: Wed, 25 Mar 2026 10:58:29 +0100 Subject: [PATCH] feat: pending-families ajoute parents[] (email/tel/ville/cp) + ordre stable Made-with: Cursor --- .../src/routes/dossiers/dossiers.service.ts | 4 +- .../dossiers/dto/dossier-am-complet.dto.ts | 3 -- .../routes/parents/dto/pending-family.dto.ts | 23 ++++++++++++ backend/src/routes/parents/parents.service.ts | 37 ++++++++++++++++++- 4 files changed, 59 insertions(+), 8 deletions(-) diff --git a/backend/src/routes/dossiers/dossiers.service.ts b/backend/src/routes/dossiers/dossiers.service.ts index abb47b6..59d9f47 100644 --- a/backend/src/routes/dossiers/dossiers.service.ts +++ b/backend/src/routes/dossiers/dossiers.service.ts @@ -42,14 +42,12 @@ export class DossiersService { relations: ['user'], }); if (am?.user) { - const bio = am.biography; const dossier: DossierAmCompletDto = { numero_dossier: num, user: this.toDossierAmUserDto(am.user), numero_agrement: am.approval_number, nir: am.nir, - texte_motivation: bio, - biographie: bio, + biographie: am.biography, disponible: am.available, ville_residence: am.residence_city, date_agrement: am.agreement_date, diff --git a/backend/src/routes/dossiers/dto/dossier-am-complet.dto.ts b/backend/src/routes/dossiers/dto/dossier-am-complet.dto.ts index e43b5c9..0ddc8a1 100644 --- a/backend/src/routes/dossiers/dto/dossier-am-complet.dto.ts +++ b/backend/src/routes/dossiers/dto/dossier-am-complet.dto.ts @@ -39,9 +39,6 @@ export class DossierAmCompletDto { numero_agrement?: string; @ApiProperty({ required: false }) nir?: string; - /** Même contenu que biographie — alias pour aligner le front avec le dossier famille (texte_motivation). */ - @ApiProperty({ required: false, description: 'Texte de présentation / motivation (alias biographie)' }) - texte_motivation?: string; @ApiProperty({ required: false }) biographie?: string; @ApiProperty({ required: false }) diff --git a/backend/src/routes/parents/dto/pending-family.dto.ts b/backend/src/routes/parents/dto/pending-family.dto.ts index ab996dd..86389b2 100644 --- a/backend/src/routes/parents/dto/pending-family.dto.ts +++ b/backend/src/routes/parents/dto/pending-family.dto.ts @@ -1,5 +1,22 @@ import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +export class ParentPendingSummaryDto { + @ApiProperty({ description: 'UUID utilisateur' }) + id: string; + + @ApiProperty() + email: string; + + @ApiPropertyOptional({ nullable: true }) + telephone?: string | null; + + @ApiPropertyOptional({ nullable: true }) + code_postal?: string | null; + + @ApiPropertyOptional({ nullable: true }) + ville?: string | null; +} + export class PendingFamilyDto { @ApiProperty({ example: 'Famille Dupont', description: 'Libellé affiché pour la famille' }) libelle: string; @@ -37,4 +54,10 @@ export class PendingFamilyDto { description: 'Emails des parents du groupe (ordre stable : nom, prénom)', }) emails?: string[]; + + @ApiPropertyOptional({ + type: [ParentPendingSummaryDto], + description: 'Résumé des parents (ordre stable, aligné sur parentIds/emails)', + }) + parents?: ParentPendingSummaryDto[]; } diff --git a/backend/src/routes/parents/parents.service.ts b/backend/src/routes/parents/parents.service.ts index 93f4325..f4d6f53 100644 --- a/backend/src/routes/parents/parents.service.ts +++ b/backend/src/routes/parents/parents.service.ts @@ -94,6 +94,7 @@ export class ParentsService { date_soumission: Date | string | null; nombre_enfants: string | number | null; emails: unknown; + parents: unknown; }[]; try { raw = await this.parentsRepository.query(` @@ -121,7 +122,7 @@ export class ParentsService { ) SELECT 'Famille ' || string_agg(u.nom, ' - ' ORDER BY u.nom, u.prenom) AS libelle, - array_agg(DISTINCT p.id_utilisateur ORDER BY p.id_utilisateur) AS "parentIds", + array_agg(p.id_utilisateur ORDER BY u.nom, u.prenom, u.id) AS "parentIds", (array_agg(p.numero_dossier))[1] AS numero_dossier, MIN(u.cree_le) AS date_soumission, COALESCE(( @@ -131,7 +132,17 @@ export class ParentsService { SELECT frx.id FROM family_rep frx WHERE frx.rep = fr.rep ) ), 0) AS nombre_enfants, - array_agg(u.email ORDER BY u.nom, u.prenom) AS emails + array_agg(u.email ORDER BY u.nom, u.prenom, u.id) AS emails, + json_agg( + json_build_object( + 'id', u.id::text, + 'email', u.email, + 'telephone', u.telephone, + 'code_postal', u.code_postal, + 'ville', u.ville + ) + ORDER BY u.nom, u.prenom, u.id + ) AS parents FROM family_rep fr JOIN parents p ON p.id_utilisateur = fr.id JOIN utilisateurs u ON u.id = p.id_utilisateur @@ -150,6 +161,7 @@ export class ParentsService { date_soumission: this.toIsoDateTimeOrNull(r.date_soumission), nombre_enfants: this.normalizeNombreEnfants(r.nombre_enfants), emails: this.normalizeEmails(r.emails), + parents: this.normalizeParents(r.parents), })); } @@ -175,6 +187,27 @@ export class ParentsService { return []; } + private normalizeParents(parents: unknown): { id: string; email: string; telephone: string | null; code_postal: string | null; ville: string | null }[] { + if (Array.isArray(parents)) { + return parents.map((p: any) => ({ + id: String(p?.id ?? ''), + email: String(p?.email ?? ''), + telephone: p?.telephone != null ? String(p.telephone) : null, + code_postal: p?.code_postal != null ? String(p.code_postal) : null, + ville: p?.ville != null ? String(p.ville) : null, + })); + } + if (typeof parents === 'string') { + try { + const parsed = JSON.parse(parents); + return this.normalizeParents(parsed); + } catch { + return []; + } + } + return []; + } + /** Convertit parentIds (array ou chaîne PG) en string[] pour éviter 500 si le driver renvoie une chaîne. */ private normalizeParentIds(parentIds: unknown): string[] { if (Array.isArray(parentIds)) return parentIds.map(String);