feat(#119): dossier famille = 1 par famille, N enfants; retrait repas/type_contrat/budget; adresse dans API parents

Made-with: Cursor
This commit is contained in:
MARTIN Julien 2026-03-18 01:52:28 +01:00
parent 5465117238
commit 1772744c81
6 changed files with 138 additions and 15 deletions

View File

@ -0,0 +1,65 @@
import {
Entity,
PrimaryGeneratedColumn,
Column,
ManyToOne,
OneToMany,
CreateDateColumn,
UpdateDateColumn,
JoinColumn,
} from 'typeorm';
import { Parents } from './parents.entity';
import { Children } from './children.entity';
import { StatutDossierType } from './dossiers.entity';
/** Un dossier = une famille, N enfants (texte de motivation unique, liste d'enfants). */
@Entity('dossier_famille')
export class DossierFamille {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column({ name: 'numero_dossier', length: 20 })
numero_dossier: string;
@ManyToOne(() => Parents, { onDelete: 'CASCADE', nullable: false })
@JoinColumn({ name: 'id_parent', referencedColumnName: 'user_id' })
parent: Parents;
@Column({ type: 'text', nullable: true })
presentation?: string;
@Column({
type: 'enum',
enum: StatutDossierType,
enumName: 'statut_dossier_type',
default: StatutDossierType.ENVOYE,
name: 'statut',
})
statut: StatutDossierType;
@CreateDateColumn({ name: 'cree_le', type: 'timestamptz' })
cree_le: Date;
@UpdateDateColumn({ name: 'modifie_le', type: 'timestamptz' })
modifie_le: Date;
@OneToMany(() => DossierFamilleEnfant, (dfe) => dfe.dossier_famille)
enfants: DossierFamilleEnfant[];
}
@Entity('dossier_famille_enfants')
export class DossierFamilleEnfant {
@Column({ name: 'id_dossier_famille', primary: true })
id_dossier_famille: string;
@Column({ name: 'id_enfant', primary: true })
id_enfant: string;
@ManyToOne(() => DossierFamille, (df) => df.enfants, { onDelete: 'CASCADE' })
@JoinColumn({ name: 'id_dossier_famille' })
dossier_famille: DossierFamille;
@ManyToOne(() => Children, { onDelete: 'CASCADE' })
@JoinColumn({ name: 'id_enfant' })
enfant: Children;
}

View File

@ -15,6 +15,12 @@ export class DossierFamilleParentDto {
nom?: string; nom?: string;
@ApiProperty({ required: false }) @ApiProperty({ required: false })
telephone?: string; telephone?: string;
@ApiProperty({ required: false })
adresse?: string;
@ApiProperty({ required: false })
ville?: string;
@ApiProperty({ required: false })
code_postal?: string;
@ApiProperty({ enum: StatutUtilisateurType }) @ApiProperty({ enum: StatutUtilisateurType })
statut: StatutUtilisateurType; statut: StatutUtilisateurType;
@ApiProperty({ required: false, description: 'Id du co-parent si couple' }) @ApiProperty({ required: false, description: 'Id du co-parent si couple' })
@ -39,7 +45,7 @@ export class DossierFamilleEnfantDto {
status: StatutEnfantType; status: StatutEnfantType;
} }
/** Dossier (parent+enfant) avec presentation */ /** Dossier (parent+enfant) avec presentation inscription famille, pas dossier de garde */
export class DossierFamillePresentationDto { export class DossierFamillePresentationDto {
@ApiProperty() @ApiProperty()
id: string; id: string;
@ -47,14 +53,8 @@ export class DossierFamillePresentationDto {
id_parent: string; id_parent: string;
@ApiProperty() @ApiProperty()
id_enfant: string; id_enfant: string;
@ApiProperty({ required: false, description: 'Texte de présentation' }) @ApiProperty({ required: false, description: 'Texte de motivation' })
presentation?: string; presentation?: string;
@ApiProperty({ required: false })
type_contrat?: string;
@ApiProperty()
repas: boolean;
@ApiProperty({ required: false })
budget?: number;
@ApiProperty({ enum: StatutDossierType }) @ApiProperty({ enum: StatutDossierType })
statut: StatutDossierType; statut: StatutDossierType;
} }

View File

@ -3,6 +3,7 @@ import { TypeOrmModule } from '@nestjs/typeorm';
import { ConfigModule, ConfigService } from '@nestjs/config'; import { ConfigModule, ConfigService } from '@nestjs/config';
import { JwtModule } from '@nestjs/jwt'; import { JwtModule } from '@nestjs/jwt';
import { Parents } from 'src/entities/parents.entity'; import { Parents } from 'src/entities/parents.entity';
import { DossierFamille, DossierFamilleEnfant } from 'src/entities/dossier_famille.entity';
import { ParentsController } from './parents.controller'; import { ParentsController } from './parents.controller';
import { ParentsService } from './parents.service'; import { ParentsService } from './parents.service';
import { Users } from 'src/entities/users.entity'; import { Users } from 'src/entities/users.entity';
@ -10,7 +11,7 @@ import { UserModule } from '../user/user.module';
@Module({ @Module({
imports: [ imports: [
TypeOrmModule.forFeature([Parents, Users]), TypeOrmModule.forFeature([Parents, Users, DossierFamille, DossierFamilleEnfant]),
forwardRef(() => UserModule), forwardRef(() => UserModule),
JwtModule.registerAsync({ JwtModule.registerAsync({
imports: [ConfigModule], imports: [ConfigModule],

View File

@ -7,6 +7,7 @@ import {
import { InjectRepository } from '@nestjs/typeorm'; import { InjectRepository } from '@nestjs/typeorm';
import { In, Repository } from 'typeorm'; import { In, Repository } from 'typeorm';
import { Parents } from 'src/entities/parents.entity'; import { Parents } from 'src/entities/parents.entity';
import { DossierFamille } from 'src/entities/dossier_famille.entity';
import { RoleType, Users } from 'src/entities/users.entity'; import { RoleType, Users } from 'src/entities/users.entity';
import { CreateParentDto } from '../user/dto/create_parent.dto'; import { CreateParentDto } from '../user/dto/create_parent.dto';
import { UpdateParentsDto } from '../user/dto/update_parent.dto'; import { UpdateParentsDto } from '../user/dto/update_parent.dto';
@ -25,6 +26,8 @@ export class ParentsService {
private readonly parentsRepository: Repository<Parents>, private readonly parentsRepository: Repository<Parents>,
@InjectRepository(Users) @InjectRepository(Users)
private readonly usersRepository: Repository<Users>, private readonly usersRepository: Repository<Users>,
@InjectRepository(DossierFamille)
private readonly dossierFamilleRepository: Repository<DossierFamille>,
) {} ) {}
// Création dun parent // Création dun parent
@ -165,7 +168,29 @@ export class ParentsService {
relations: ['user', 'co_parent', 'parentChildren', 'parentChildren.child', 'dossiers', 'dossiers.child'], relations: ['user', 'co_parent', 'parentChildren', 'parentChildren.child', 'dossiers', 'dossiers.child'],
}); });
const enfantsMap = new Map<string, DossierFamilleEnfantDto>(); const enfantsMap = new Map<string, DossierFamilleEnfantDto>();
const presentationList: DossierFamillePresentationDto[] = []; let presentationList: DossierFamillePresentationDto[] = [];
// Un dossier = une famille : priorité à dossier_famille (un texte, N enfants)
const dossierFamille = await this.dossierFamilleRepository.findOne({
where: { numero_dossier: num },
relations: ['parent', 'enfants', 'enfants.enfant'],
});
if (dossierFamille?.enfants?.length) {
const idParent = dossierFamille.parent?.user_id ?? familyUserIds[0];
for (const dfe of dossierFamille.enfants) {
const idEnfant = dfe.enfant?.id ?? dfe.id_enfant;
if (idEnfant) {
presentationList.push({
id: dossierFamille.id,
id_parent: idParent,
id_enfant: idEnfant,
presentation: dossierFamille.presentation,
statut: dossierFamille.statut,
});
}
}
}
for (const p of parents) { for (const p of parents) {
// Enfants via parentChildren // Enfants via parentChildren
if (p.parentChildren) { if (p.parentChildren) {
@ -183,17 +208,14 @@ export class ParentsService {
} }
} }
} }
// Dossiers (présentation) // Si pas de dossier_famille : fallback sur anciens dossiers (parent+enfant)
if (p.dossiers) { if (presentationList.length === 0 && p.dossiers) {
for (const d of p.dossiers) { for (const d of p.dossiers) {
presentationList.push({ presentationList.push({
id: d.id, id: d.id,
id_parent: p.user_id, id_parent: p.user_id,
id_enfant: d.child?.id ?? '', id_enfant: d.child?.id ?? '',
presentation: d.presentation, presentation: d.presentation,
type_contrat: d.type_contrat,
repas: d.meals,
budget: d.budget != null ? Number(d.budget) : undefined,
statut: d.status, statut: d.status,
}); });
} }
@ -205,6 +227,9 @@ export class ParentsService {
prenom: p.user.prenom, prenom: p.user.prenom,
nom: p.user.nom, nom: p.user.nom,
telephone: p.user.telephone, telephone: p.user.telephone,
adresse: p.user.adresse,
ville: p.user.ville,
code_postal: p.user.code_postal,
statut: p.user.statut, statut: p.user.statut,
co_parent_id: p.co_parent?.id, co_parent_id: p.co_parent?.id,
})); }));

View File

@ -0,0 +1,27 @@
-- Un dossier = une famille, N enfants. Ticket #119 évolution.
-- Table: un enregistrement par famille (lien via numero_dossier / id_parent).
CREATE TABLE IF NOT EXISTS dossier_famille (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
numero_dossier VARCHAR(20) NOT NULL,
id_parent UUID NOT NULL REFERENCES parents(id_utilisateur) ON DELETE CASCADE,
presentation TEXT,
type_contrat VARCHAR(50),
repas BOOLEAN NOT NULL DEFAULT false,
budget NUMERIC(10,2),
planning_souhaite JSONB,
statut statut_dossier_type NOT NULL DEFAULT 'envoye',
cree_le TIMESTAMPTZ NOT NULL DEFAULT now(),
modifie_le TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX IF NOT EXISTS idx_dossier_famille_numero ON dossier_famille(numero_dossier);
CREATE INDEX IF NOT EXISTS idx_dossier_famille_id_parent ON dossier_famille(id_parent);
-- Enfants concernés par ce dossier famille (N par dossier).
CREATE TABLE IF NOT EXISTS dossier_famille_enfants (
id_dossier_famille UUID NOT NULL REFERENCES dossier_famille(id) ON DELETE CASCADE,
id_enfant UUID NOT NULL REFERENCES enfants(id) ON DELETE CASCADE,
PRIMARY KEY (id_dossier_famille, id_enfant)
);
CREATE INDEX IF NOT EXISTS idx_dossier_famille_enfants_enfant ON dossier_famille_enfants(id_enfant);

View File

@ -0,0 +1,5 @@
-- Dossier famille = inscription uniquement, pas les données de dossier de garde (repas, type_contrat, budget, etc.)
ALTER TABLE dossier_famille DROP COLUMN IF EXISTS repas;
ALTER TABLE dossier_famille DROP COLUMN IF EXISTS type_contrat;
ALTER TABLE dossier_famille DROP COLUMN IF EXISTS budget;
ALTER TABLE dossier_famille DROP COLUMN IF EXISTS planning_souhaite;