feat(#112): reprise après refus — dossier complet GET/PATCH

- GET reprise-dossier : parents[], enfants[], texte_motivation (famille) ou fiche AM (#119)
- PATCH reprise-resoumettre : co-parent, enfants (update par id), motivation, fiche AM
- Resoumission famille : tous les parents en en_attente + invalidation token groupée
- DTOs étendus + est_multiple sur enfants dossier famille
- Tests unitaires getRepriseDossier parent/AM

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-16 16:39:53 +02:00
co-authored by Cursor
parent 671da71752
commit d70577b1c3
9 changed files with 655 additions and 17 deletions
@@ -0,0 +1,11 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsNotEmpty, IsUUID } from 'class-validator';
import { EnfantInscriptionDto } from './enfant-inscription.dto';
/** Enfant existant modifiable lors de la resoumission reprise (#112). */
export class EnfantRepriseDto extends EnfantInscriptionDto {
@ApiProperty({ description: 'UUID enfant existant (obligatoire en reprise)' })
@IsUUID()
@IsNotEmpty()
id: string;
}
@@ -1,7 +1,11 @@
import { ApiProperty } from '@nestjs/swagger';
import { RoleType } from 'src/entities/users.entity';
import {
DossierFamilleEnfantDto,
DossierFamilleParentDto,
} from '../../parents/dto/dossier-famille-complet.dto';
/** Réponse GET /auth/reprise-dossier données dossier pour préremplir le formulaire reprise. Ticket #111 */
/** Réponse GET /auth/reprise-dossier dossier complet pour préremplir le wizard reprise. #111 + #112 */
export class RepriseDossierDto {
@ApiProperty()
id: string;
@@ -41,4 +45,47 @@ export class RepriseDossierDto {
@ApiProperty({ required: false })
situation_familiale?: string;
// --- Parent (dossier famille, aligné #119) ---
@ApiProperty({ type: [DossierFamilleParentDto], required: false })
parents?: DossierFamilleParentDto[];
@ApiProperty({ type: [DossierFamilleEnfantDto], required: false })
enfants?: DossierFamilleEnfantDto[];
@ApiProperty({ required: false, description: 'Motivation / présentation dossier famille' })
texte_motivation?: string;
// --- AM (aligné DossierAmCompletDto) ---
@ApiProperty({ required: false })
consentement_photo?: boolean;
@ApiProperty({ required: false })
date_naissance?: Date;
@ApiProperty({ required: false })
lieu_naissance_ville?: string;
@ApiProperty({ required: false })
lieu_naissance_pays?: string;
@ApiProperty({ required: false })
numero_agrement?: string;
@ApiProperty({ required: false })
nir?: string;
@ApiProperty({ required: false })
date_agrement?: Date;
@ApiProperty({ required: false })
nb_max_enfants?: number;
@ApiProperty({ required: false })
place_disponible?: number;
@ApiProperty({ required: false })
biographie?: string;
}
@@ -1,12 +1,29 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsOptional, IsString, MaxLength, IsUUID } from 'class-validator';
import {
IsOptional,
IsString,
MaxLength,
IsUUID,
IsBoolean,
IsArray,
ValidateNested,
IsInt,
Min,
Max,
IsDateString,
Matches,
} from 'class-validator';
import { Type } from 'class-transformer';
import { EnfantRepriseDto } from './enfant-reprise.dto';
/** Body PUT /auth/reprise-resoumettre token + champs modifiables. Ticket #111 */
/** Body PATCH /auth/reprise-resoumettre token + champs modifiables du wizard. #111 + #112 */
export class ResoumettreRepriseDto {
@ApiProperty({ description: 'Token reprise (reçu par email)' })
@IsUUID()
token: string;
// --- Identité titulaire (parent principal ou AM) ---
@ApiProperty({ required: false })
@IsOptional()
@IsString()
@@ -42,8 +59,147 @@ export class ResoumettreRepriseDto {
@MaxLength(10)
code_postal?: string;
@ApiProperty({ required: false, description: 'Pour AM' })
@ApiProperty({ required: false, description: 'Pour AM (URL photo existante)' })
@IsOptional()
@IsString()
photo_url?: string;
@ApiProperty({ required: false, description: 'Pour AM (nouvelle photo base64)' })
@IsOptional()
@IsString()
photo_base64?: string;
@ApiProperty({ required: false, description: 'Pour AM (nom fichier photo)' })
@IsOptional()
@IsString()
photo_filename?: string;
// --- Co-parent (reprise parent) ---
@ApiProperty({ required: false })
@IsOptional()
@IsString()
co_parent_email?: string;
@ApiProperty({ required: false })
@IsOptional()
@IsString()
co_parent_prenom?: string;
@ApiProperty({ required: false })
@IsOptional()
@IsString()
co_parent_nom?: string;
@ApiProperty({ required: false })
@IsOptional()
@IsString()
@Matches(/^(\+33|0)[1-9](\d{2}){4}$/, {
message: 'Le numéro de téléphone du co-parent doit être valide',
})
co_parent_telephone?: string;
@ApiProperty({ required: false })
@IsOptional()
@IsBoolean()
co_parent_meme_adresse?: boolean;
@ApiProperty({ required: false })
@IsOptional()
@IsString()
co_parent_adresse?: string;
@ApiProperty({ required: false })
@IsOptional()
@IsString()
co_parent_code_postal?: string;
@ApiProperty({ required: false })
@IsOptional()
@IsString()
co_parent_ville?: string;
// --- Motivation dossier famille ---
@ApiProperty({ required: false, description: 'Alias texte_motivation' })
@IsOptional()
@IsString()
@MaxLength(2000)
texte_motivation?: string;
@ApiProperty({ required: false, description: 'Alias presentation_dossier (inscription)' })
@IsOptional()
@IsString()
@MaxLength(2000)
presentation_dossier?: string;
@ApiProperty({ type: [EnfantRepriseDto], required: false })
@IsOptional()
@IsArray()
@ValidateNested({ each: true })
@Type(() => EnfantRepriseDto)
enfants?: EnfantRepriseDto[];
// --- AM ---
@ApiProperty({ required: false })
@IsOptional()
@IsBoolean()
consentement_photo?: boolean;
@ApiProperty({ required: false })
@IsOptional()
@IsDateString()
date_naissance?: string;
@ApiProperty({ required: false })
@IsOptional()
@IsString()
@MaxLength(100)
lieu_naissance_ville?: string;
@ApiProperty({ required: false })
@IsOptional()
@IsString()
@MaxLength(100)
lieu_naissance_pays?: string;
@ApiProperty({ required: false })
@IsOptional()
@IsString()
@MaxLength(50)
numero_agrement?: string;
@ApiProperty({ required: false })
@IsOptional()
@IsString()
@Matches(/^[1-3]\d{4}(?:2A|2B|\d{2})\d{6}\d{2}$/, {
message: 'Le NIR doit contenir 15 caractères (chiffres, ou 2A/2B pour la Corse)',
})
nir?: string;
@ApiProperty({ required: false })
@IsOptional()
@IsDateString()
date_agrement?: string;
@ApiProperty({ required: false })
@IsOptional()
@IsInt()
@Min(1)
@Max(10)
capacite_accueil?: number;
@ApiProperty({ required: false })
@IsOptional()
@IsInt()
@Min(0)
@Max(10)
places_disponibles?: number;
@ApiProperty({ required: false })
@IsOptional()
@IsString()
@MaxLength(2000)
biographie?: string;
}