diff --git a/backend/src/modules/cards/cards.service.spec.ts b/backend/src/modules/cards/cards.service.spec.ts index dc029ae..6a43189 100644 --- a/backend/src/modules/cards/cards.service.spec.ts +++ b/backend/src/modules/cards/cards.service.spec.ts @@ -155,4 +155,75 @@ describe('CardsService (#194)', () => { }), ).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); + }); }); diff --git a/backend/src/modules/cards/cards.service.ts b/backend/src/modules/cards/cards.service.ts index 9d25769..de315df 100644 --- a/backend/src/modules/cards/cards.service.ts +++ b/backend/src/modules/cards/cards.service.ts @@ -395,12 +395,25 @@ export class CardsService { ]; if (type.audience_resolver === 'couple_am') { + // Destinataire métier = AM + miroir foyer : tous les co-parents de l’enfant members.push({ id_card: card.id, id_utilisateur: placement.amId, role_snapshot: RoleType.ASSISTANTE_MATERNELLE, 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') { const liens = await this.parentsChildrenRepo.find({ where: { enfantId: placement.enfantId }, diff --git a/docs/24_DECISIONS-PROJET.md b/docs/24_DECISIONS-PROJET.md index 44d5bbf..35397b8 100644 --- a/docs/24_DECISIONS-PROJET.md +++ b/docs/24_DECISIONS-PROJET.md @@ -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é). +**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` --- diff --git a/docs/32_MINI-SPEC-BULLES-CARTES.md b/docs/32_MINI-SPEC-BULLES-CARTES.md index 4179d49..ce7c523 100644 --- a/docs/32_MINI-SPEC-BULLES-CARTES.md +++ b/docs/32_MINI-SPEC-BULLES-CARTES.md @@ -61,7 +61,7 @@ La teinte pastel est **100 % front** : table de mapping `type_code` → asset (` - Libellés UI : - `conge_am` → « Demande de congés » - `arret_maladie_am` → « Arrêt maladie » - - `absence_enfant` (+ `_modif`) → « Notification d’absence » + - `absence_enfant` → « Notification d’absence » ### 3.2 Carte **congés** (`conge_am`) @@ -93,10 +93,13 @@ Clé = **choix front**, pas une valeur API. |------------|---------|--------| | **`aqua`** | Congés AM | `conge_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)* | | **`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** - Couleur = famille visuelle (lisible sans tout lire). @@ -138,6 +141,7 @@ Pourquoi **pas seulement la messagerie** ? | Famille | Couleur UI | Exemples | `response_mode` | |---------|------------|----------|-----------------| | 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` | | 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 ». - Refus : afficher `last_refuse_comment`. - 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. --- @@ -167,11 +173,13 @@ Variante soins : un seul fond rose ; l’icône / sous-type change. ## 9. Recettes - [ ] 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. -- [ ] 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. - [ ] (Plus tard) `soin_enfant` rose AM → parents + ligne agenda. +- [ ] (Plus tard / calendrier) modifs d’absence + éventuel `absence_enfant_modif`. ---