feat(#158): attach/detach enfant au niveau foyer (pivot + co-parent).
POST/DELETE /parents/:id/enfants/:enfantId propagent les liens à tous les responsables du foyer (co-parent bidirectionnel + même dossier). Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
925b6d5cd4
commit
93912d1374
@ -114,36 +114,86 @@ export class ParentsService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rattacher un enfant existant à un parent (enfants_parents). Ticket #115 / doc 28 §6.2.
|
* Membres du foyer (user ids) pour affiliation enfant.
|
||||||
|
* Pivot + co-parent (A→B et B→A) + même numero_dossier. Ticket #158.
|
||||||
*/
|
*/
|
||||||
async attachEnfant(parentUserId: string, enfantId: string): Promise<Parents> {
|
private async resolveFoyerParentUserIds(parent: Parents): Promise<string[]> {
|
||||||
await this.findOne(parentUserId);
|
const ids = new Set<string>([parent.user_id]);
|
||||||
|
|
||||||
const existing = await this.parentsChildrenRepository.findOne({
|
if (parent.co_parent?.id) {
|
||||||
where: { parentId: parentUserId, enfantId },
|
ids.add(parent.co_parent.id);
|
||||||
});
|
|
||||||
if (existing) {
|
|
||||||
throw new ConflictException('Cet enfant est déjà rattaché à ce parent');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const child = await this.parentsRepository.manager.findOne(Children, { where: { id: enfantId } });
|
// Sens inverse : parents qui déclarent ce user comme co-parent
|
||||||
|
const reverseLinks = await this.parentsRepository.find({
|
||||||
|
where: { co_parent: { id: parent.user_id } },
|
||||||
|
relations: ['co_parent'],
|
||||||
|
});
|
||||||
|
for (const p of reverseLinks) {
|
||||||
|
ids.add(p.user_id);
|
||||||
|
if (p.co_parent?.id) ids.add(p.co_parent.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
const dossier = parent.numero_dossier?.trim();
|
||||||
|
if (dossier) {
|
||||||
|
const sameDossier = await this.parentsRepository.find({
|
||||||
|
where: { numero_dossier: dossier },
|
||||||
|
relations: ['co_parent'],
|
||||||
|
});
|
||||||
|
for (const p of sameDossier) {
|
||||||
|
ids.add(p.user_id);
|
||||||
|
if (p.co_parent?.id) ids.add(p.co_parent.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [...ids];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rattacher un enfant au foyer du parent (tous les responsables). Ticket #158.
|
||||||
|
* Un seul POST suffit : liens créés pour pivot + co-parent / même dossier.
|
||||||
|
*/
|
||||||
|
async attachEnfant(parentUserId: string, enfantId: string): Promise<Parents> {
|
||||||
|
const parent = await this.findOne(parentUserId);
|
||||||
|
|
||||||
|
const child = await this.parentsRepository.manager.findOne(Children, {
|
||||||
|
where: { id: enfantId },
|
||||||
|
});
|
||||||
if (!child) {
|
if (!child) {
|
||||||
throw new NotFoundException('Enfant introuvable');
|
throw new NotFoundException('Enfant introuvable');
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.parentsChildrenRepository.save(
|
const foyerIds = await this.resolveFoyerParentUserIds(parent);
|
||||||
this.parentsChildrenRepository.create({ parentId: parentUserId, enfantId }),
|
let created = 0;
|
||||||
);
|
|
||||||
|
for (const memberId of foyerIds) {
|
||||||
|
const existing = await this.parentsChildrenRepository.findOne({
|
||||||
|
where: { parentId: memberId, enfantId },
|
||||||
|
});
|
||||||
|
if (existing) continue;
|
||||||
|
|
||||||
|
await this.parentsChildrenRepository.save(
|
||||||
|
this.parentsChildrenRepository.create({
|
||||||
|
parentId: memberId,
|
||||||
|
enfantId,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
created += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (created === 0) {
|
||||||
|
throw new ConflictException('Cet enfant est déjà rattaché à ce foyer');
|
||||||
|
}
|
||||||
|
|
||||||
return this.findOne(parentUserId);
|
return this.findOne(parentUserId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Détacher un enfant d'un parent sans supprimer l'enfant.
|
* Détacher un enfant du foyer du parent (tous les responsables). Ticket #158.
|
||||||
* Autorise le détachement du dernier responsable (#157) — l'enfant reste listé
|
* Si plus aucun lien ensuite → enfant orphelin (#157).
|
||||||
* via GET /enfants avec parentLinks vides (alerte front).
|
|
||||||
*/
|
*/
|
||||||
async detachEnfant(parentUserId: string, enfantId: string): Promise<Parents> {
|
async detachEnfant(parentUserId: string, enfantId: string): Promise<Parents> {
|
||||||
await this.findOne(parentUserId);
|
const parent = await this.findOne(parentUserId);
|
||||||
|
|
||||||
const link = await this.parentsChildrenRepository.findOne({
|
const link = await this.parentsChildrenRepository.findOne({
|
||||||
where: { parentId: parentUserId, enfantId },
|
where: { parentId: parentUserId, enfantId },
|
||||||
@ -152,7 +202,12 @@ export class ParentsService {
|
|||||||
throw new NotFoundException('Lien parent-enfant introuvable');
|
throw new NotFoundException('Lien parent-enfant introuvable');
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.parentsChildrenRepository.delete({ parentId: parentUserId, enfantId });
|
const foyerIds = await this.resolveFoyerParentUserIds(parent);
|
||||||
|
await this.parentsChildrenRepository.delete({
|
||||||
|
parentId: In(foyerIds),
|
||||||
|
enfantId,
|
||||||
|
});
|
||||||
|
|
||||||
return this.findOne(parentUserId);
|
return this.findOne(parentUserId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user