fix(cards): audience absence_enfant = AM + co-parents du foyer

Le resolver couple_am n’incluait que le créateur et l’AM ; le co-parent
ne voyait pas la notification. Docs : V1 réponses + modifs via agenda.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-10-05 12:19:30 +02:00
co-authored by Cursor
parent 6c8ef32a11
commit 0c736f5605
4 changed files with 103 additions and 5 deletions
@@ -155,4 +155,75 @@ describe('CardsService (#194)', () => {
}), }),
).rejects.toBeInstanceOf(ForbiddenException); ).rejects.toBeInstanceOf(ForbiddenException);
}); });
it('parent crée absence_enfant → audience AM + co-parent', async () => {
typesRepo.findOne.mockResolvedValue({
code: 'absence_enfant',
system: true,
titre: 'Absence enfant',
emitter_roles: [RoleType.PARENT],
recipient_roles: [RoleType.ASSISTANTE_MATERNELLE],
audience_resolver: 'couple_am',
response_mode: CardResponseModeType.NONE,
retention_days: 7,
});
amChildrenRepo.findOne.mockResolvedValue({
id: 'pl-1',
amId: 'am-1',
enfantId: 'e-1',
date_fin: null,
});
parentsChildrenRepo.findOne.mockResolvedValue({
parentId: 'p-1',
enfantId: 'e-1',
});
parentsChildrenRepo.find.mockResolvedValue([
{ parentId: 'p-1', enfantId: 'e-1' },
{ parentId: 'p-2', enfantId: 'e-1' },
]);
evenementsService.creer.mockResolvedValue({
id: 'abs-1',
type: TypeEvenementAgendaType.ABSENCE_ENFANT,
statut: StatutEvenementAgendaType.ACCEPTE,
});
cardsRepo.findOne.mockResolvedValue({
id: 'card-abs',
type_code: 'absence_enfant',
id_placement: 'pl-1',
id_evenement: 'abs-1',
cree_par: 'p-1',
operation: CardOperationType.CREATE,
statut: CardInstanceStatutType.OUVERTE,
payload: { date_debut: '2026-10-28', date_fin: '2026-10-29' },
purge_at: new Date(),
cree_le: new Date(),
modifie_le: new Date(),
type: {
titre: 'Absence enfant',
response_mode: CardResponseModeType.NONE,
retention_days: 7,
},
responses: [],
audience: [
{ id_utilisateur: 'p-1' },
{ id_utilisateur: 'am-1' },
{ id_utilisateur: 'p-2' },
],
});
await service.creer('p-1', RoleType.PARENT, {
type_code: 'absence_enfant',
id_placement: 'pl-1',
date_debut: '2026-10-28',
date_fin: '2026-10-29',
});
const saved = audienceRepo.save.mock.calls[0][0] as Array<{
id_utilisateur: string;
is_creator: boolean;
}>;
const ids = saved.map((m) => m.id_utilisateur).sort();
expect(ids).toEqual(['am-1', 'p-1', 'p-2']);
expect(saved.find((m) => m.id_utilisateur === 'p-2')?.is_creator).toBe(false);
});
}); });
@@ -395,12 +395,25 @@ export class CardsService {
]; ];
if (type.audience_resolver === 'couple_am') { if (type.audience_resolver === 'couple_am') {
// Destinataire métier = AM + miroir foyer : tous les co-parents de l’enfant
members.push({ members.push({
id_card: card.id, id_card: card.id,
id_utilisateur: placement.amId, id_utilisateur: placement.amId,
role_snapshot: RoleType.ASSISTANTE_MATERNELLE, role_snapshot: RoleType.ASSISTANTE_MATERNELLE,
is_creator: false, is_creator: false,
}); });
const liensAm = await this.parentsChildrenRepo.find({
where: { enfantId: placement.enfantId },
});
for (const l of liensAm) {
if (l.parentId === creatorId) continue;
members.push({
id_card: card.id,
id_utilisateur: l.parentId,
role_snapshot: RoleType.PARENT,
is_creator: false,
});
}
} else if (type.audience_resolver === 'couple_parents') { } else if (type.audience_resolver === 'couple_parents') {
const liens = await this.parentsChildrenRepo.find({ const liens = await this.parentsChildrenRepo.find({
where: { enfantId: placement.enfantId }, where: { enfantId: placement.enfantId },
+6
View File
@@ -587,6 +587,12 @@ POST /repos/jmartin/petitspas/pulls/{index}/merge
**Flux V1** : BDD agenda → API liste/CRUD → module Cartes SYSTEM → front bulles (séparé). **Flux V1** : BDD agenda → API liste/CRUD → module Cartes SYSTEM → front bulles (séparé).
**V1 cartes (réponses)** :
- `absence_enfant` → info (`none`) — pas d’ack AM ; audience **AM + tous les parents du foyer** (co-parent inclus)
- `arret_maladie_am` → ack parent (« Bien reçu »)
- `conge_am` → accept / refuse (+ motivation refus)
- **Modifs d’absence** → via **agenda / calendrier**, pas via bulle `absence_enfant_modif` (reporté au ticket calendrier)
**Réf. détail** : `docs/32_MINI-SPEC-BULLES-CARTES.md` **Réf. détail** : `docs/32_MINI-SPEC-BULLES-CARTES.md`
--- ---
+13 -5
View File
@@ -61,7 +61,7 @@ La teinte pastel est **100 % front** : table de mapping `type_code` → asset (`
- Libellés UI : - Libellés UI :
- `conge_am` → « Demande de congés » - `conge_am` → « Demande de congés »
- `arret_maladie_am` → « Arrêt maladie » - `arret_maladie_am` → « Arrêt maladie »
- `absence_enfant` (+ `_modif`) → « Notification d’absence » - `absence_enfant` → « Notification d’absence »
### 3.2 Carte **congés** (`conge_am`) ### 3.2 Carte **congés** (`conge_am`)
@@ -93,10 +93,13 @@ Clé = **choix front**, pas une valeur API.
|------------|---------|--------| |------------|---------|--------|
| **`aqua`** | Congés AM | `conge_am` | | **`aqua`** | Congés AM | `conge_am` |
| **`blue`** | Arrêt maladie AM | `arret_maladie_am` | | **`blue`** | Arrêt maladie AM | `arret_maladie_am` |
| **`peach`** | Absence enfant / info pratique | `absence_enfant`, `absence_enfant_modif` | | **`peach`** | Absence enfant / info pratique | `absence_enfant` |
| **`pink`** | Soins / bobo / problème **enfant** (AM → parents) | `soin_enfant` *(nouveau — voir §5)* | | **`pink`** | Soins / bobo / problème **enfant** (AM → parents) | `soin_enfant` *(nouveau — voir §5)* |
| **`lavender`** | Admin / divers | futurs types admin | | **`lavender`** | Admin / divers | futurs types admin |
**Hors scope V1 cartes** : `absence_enfant_modif` (ack AM sur modif).
Les **modifications d’absence** se font via l’**agenda / calendrier**, pas via une bulle dédiée. On rebranchera une notif éventuelle quand le calendrier sera implémenté.
**Principes** **Principes**
- Couleur = famille visuelle (lisible sans tout lire). - Couleur = famille visuelle (lisible sans tout lire).
@@ -138,6 +141,7 @@ Pourquoi **pas seulement la messagerie** ?
| Famille | Couleur UI | Exemples | `response_mode` | | Famille | Couleur UI | Exemples | `response_mode` |
|---------|------------|----------|-----------------| |---------|------------|----------|-----------------|
| Soins (détail) | `pink` | `soin_temperature`, `soin_medicament`, `soin_bobo` — ou sous-types dans payload | `ack` | | Soins (détail) | `pink` | `soin_temperature`, `soin_medicament`, `soin_bobo` — ou sous-types dans payload | `ack` |
| Modif absence | `peach` | `absence_enfant_modif` — **via calendrier** d’abord ; bulle ack AM éventuelle ensuite | `ack` |
| Sortie | `peach` / `lavender` | `sortie_a_valider` | `accept_refuse` | | Sortie | `peach` / `lavender` | `sortie_a_valider` | `accept_refuse` |
| Admin | `lavender` | `info_admin`, `document_a_fournir` | `ack` / `none` | | Admin | `lavender` | `info_admin`, `document_a_fournir` | `ack` / `none` |
@@ -150,7 +154,9 @@ Variante soins : un seul fond rose ; l’icône / sous-type change.
- Créateur : pas de boutons réponse ; éventuel « En attente de réponse ». - Créateur : pas de boutons réponse ; éventuel « En attente de réponse ».
- Refus : afficher `last_refuse_comment`. - Refus : afficher `last_refuse_comment`.
- Congé : `accept_refuse` côté destinataire. - Congé : `accept_refuse` côté destinataire.
- Absence enfant : info / ack — pas de veto AM en V1. - Absence enfant : **info seule** (`response_mode: none`) — pas de veto ni ack AM en V1.
- Absence enfant : audience = **AM + tous les parents du foyer** (créateur + co-parent), pas seulement le déclarant.
- Modif / édition d’absence : **agenda**, pas les cartes (V1).
- File courte : `purge_at` ; l’**agenda** conserve l’historique métier. - File courte : `purge_at` ; l’**agenda** conserve l’historique métier.
--- ---
@@ -167,11 +173,13 @@ Variante soins : un seul fond rose ; l’icône / sous-type change.
## 9. Recettes ## 9. Recettes
- [ ] API cartes **sans** champ `couleur` (ni feed ni `/types`). - [ ] API cartes **sans** champ `couleur` (ni feed ni `/types`).
- [ ] Front mappe `conge_am` → aqua, `arret_maladie_am` → blue, `absence_*` → peach. - [ ] Front mappe `conge_am` → aqua, `arret_maladie_am` → blue, `absence_enfant` → peach.
- [ ] Accept / refuse congé OK. - [ ] Accept / refuse congé OK.
- [ ] Arrêt traité + période en cours → reste visible. - [ ] Arrêt : ack parent « Bien reçu » ; traité + période en cours → reste visible.
- [ ] Absence enfant : pas de bouton (info).
- [ ] Filtre couple / placement. - [ ] Filtre couple / placement.
- [ ] (Plus tard) `soin_enfant` rose AM → parents + ligne agenda. - [ ] (Plus tard) `soin_enfant` rose AM → parents + ligne agenda.
- [ ] (Plus tard / calendrier) modifs d’absence + éventuel `absence_enfant_modif`.
--- ---