[Backend] Cartes rose soin_enfant — 3 sous-types, instant T, correction 24 h #206

Open
opened 2026-10-05 20:44:26 +00:00 by jmartin · 0 comments
Owner

Contexte

Epic #165 — file d'attention Cartes. Front associé : #207.
Intention produit figée dans docs/32_MINI-SPEC-BULLES-CARTES.md §5.

La nounou signale un événement concernant l'enfant (fièvre, bobo, médicament donné) vers les parents, qui acquittent. Ce n'est pas que de la messagerie : c'est loggé dans la file, lié à l'agenda, et acquittable sans noyer le chat.

Cadrage commun (à ne pas perdre de vue)

L'outil n'a aucune valeur juridique, comme le module contrat : la vraie vie prévaut. Une carte soin vaut un message WhatsApp du genre « l'enfant a de la fièvre, je lui donne du paracétamol ». C'est informatif, ce n'est ni un circuit de validation ni une autorisation.

  • L'absence de carte n'engage pas la responsabilité de l'AM : elle reste libre d'agir et de ne rien saisir.
  • L'outil cadre et facilite la transmission, il ne la contraint pas.
  • Aucune fonctionnalité ne doit être présentée comme une preuve, une autorisation ou une obligation.

Ce qui rend ces cartes différentes des précédentes

  1. Elles portent un sous-type (une seule famille soin_enfant, trois formulaires distincts).
  2. Elles décrivent un instant T et non une période négociable.

On les conserve malgré tout dans evenements_agenda : c'est fait pour se souvenir. La période y est dégénérée (date_debut == date_fin) et l'instant précis vit dans heure_releve.

Sous-types retenus

sous_type Modale Réponse
fievre Fièvre ack
bobo Bobo ack
medicament Médicament ack

Pas de 4e sous-type « autre » : autre reste une valeur de la liste des natures de bobo.

Règle anti-ambiguïté : fièvre → carte fievre (le médicament éventuel y est un champ satellite) ; médicament pour toute autre raison → carte medicament. Un seul chemin par fait consigné, sinon l'historique devient incohérent.

Plusieurs cartes par jour sont normales : une fièvre se mesure plusieurs fois. Chaque relevé est une carte, pas de regroupement en V1.

Contrat de payload

Commun aux trois sous-types : sous_type, date_debut / date_fin (même jour en V1), heure_releve (HH:mm).
L'heure est indispensable : 38,2 à 9 h et 38,2 à 16 h ne racontent pas la même histoire, et sans elle les parents la redemandent dans le chat.

fievre

Champ Règle
temperature_c décimal ; saisie 3 chiffres avec virgule automatique (365 → 36,5)
bornes 34,0 – 43,0 ; alerte visuelle hors 36,0 – 38,0, sans blocage
medicament_donne texte libre, facultatif

On ne bloque pas une observation de santé : une hypothermie réelle doit pouvoir être consignée, et les mesures axillaires ou frontales descendent bas. Un formulaire qui refuse la réalité pousse à ne rien saisir.

Pas de champ poids : c'est une donnée stable qui appartient à la fiche enfant, et l'AM n'a pas de balance. Voir #208.

bobo

Champ Règle
bobo_nature chute | coup | egratignure | piqure | brulure | autre
description texte, max 120 caractères

La limite est volontaire : le fait suffit dans la carte, le contexte va dans la messagerie.

medicament

Champ Règle
medicament_nom obligatoire
contexte texte, max 120 caractères

À faire

1. Seed du type

À ajouter à la liste de database/migrations/2026_cards_system.sql (lignes 88-120, avec son ON CONFLICT DO UPDATE) :

(
  'soin_enfant', true, 'Soins enfant',
  ARRAY['assistante_maternelle'], ARRAY['parent'],
  'couple_parents', 'ack', 7, 'pink'
)
  • audience_resolver = couple_parents, comme conge_am et arret_maladie_am : c'est le resolver des cartes émises par l'AM, qui ajoute tous les parents de l'enfant. (couple_am est l'inverse, pour les cartes émises par un parent.) → aucun code d'audience à écrire, buildAudience branche déjà sur ce champ.
  • retention_days = 7, aligné sur la famille informative (absence_enfant est à 7 ; les 14 jours de conge_am / arret_maladie_am servent une décision sur une période à venir). Un relevé de la semaine dernière est du bruit dans le feed, l'historique vit dans l'agenda.
    À savoir : purgeAt applique max(retention, 15) tant que la carte est OUVERTE, donc une carte non acquittée reste 15 jours quoi qu'il arrive.
  • La colonne couleur existe encore en base sans être exposée par l'API ; on la renseigne par cohérence avec ses voisines.

Le statut initial de la carte sera OUVERTE puisque response_mode = ack : rien à changer.

2. Enum agenda — trois endroits à tenir synchrones

TypeEvenementAgendaType ne contient que absence_enfant, conge_am, arret_maladie_am. Pas de contrainte de prod, donc on modifie librement, mais il faut penser au schéma de référence sinon une base recréée repart sans la valeur :

  • database/BDD.sql lignes 38-42 : ajouter soin_enfant à la création de l'enum (synchronize: false côté TypeORM, rien n'est déduit des entités) ;
  • un fichier incrémental dans database/migrations/ : ALTER TYPE type_evenement_agenda_type ADD VALUE 'soin_enfant' pour les bases dev déjà en place ;
  • la valeur dans l'enum TS.

Puis étendre mapAbsenceType dans cards.service.ts, qui lève une exception pour tout type non mappé.

3. Deux branches dans evenements-agenda.service.ts

  • statutInitial : seul ABSENCE_ENFANT renvoie ACCEPTE aujourd'hui, tout le reste tombe en EN_ATTENTE. SOIN_ENFANT doit renvoyer ACCEPTE — il n'y a rien à négocier. Effet de bord voulu : expireAtFor renvoie alors le sentinel EXPIRE_ACCEPTE (9999-12-31), donc la ligne d'agenda ne se purge jamais.
  • assertCanCreateType : l'AM est limitée à CONGE_AM / ARRET_MALADIE_AM ; ajouter SOIN_ENFANT pour l'AM, et surtout ne pas l'ouvrir aux parents.

Attention : il existe deux fonctions statutInitial homonymes, une pour la carte et une pour l'agenda, dans deux services différents.

4. Payload structuré

Le payload est aujourd'hui figé en dur à date_debut / date_fin / motif dans creer. Le rendre extensible pour accueillir les champs par sous-type, sans casser les types existants.

Le motif est dérivé côté back à partir du payload (ex. 36,5 °C à 14:20) ; le front ne l'envoie pas. C'est lui qui alimente le motif de la ligne d'agenda, il ne doit donc pas dépendre d'une chaîne construite par le client.

5. Correction encadrée — fenêtre de 24 h

L'erreur de saisie est inévitable (38,5 tapé au lieu de 36,5). On autorise donc la correction, mais bornée et tracée : corrigeable brièvement, inaltérable ensuite.

  • Qui : l'AM auteure de la carte, personne d'autre.
  • Quand : moins de 24 h après cree_le. Contrôle côté back ; le front se contente de masquer le bouton.
  • Quoi : les champs de payload du sous-type. Pas le sous_type, pas le placement, pas l'enfant.
  • Mauvais sous-type : géré par la suppression, elle aussi ouverte 24 h à l'auteure, et qui retire alors carte + ligne d'agenda.
  • Après 24 h : ni modification ni suppression. Une correction tardive se fait par une nouvelle carte, jamais par réécriture.

Trace : conserver la valeur précédente dans un historique[] horodaté du payload, avec son auteur, et poser corrige_le. Une correction ne doit pas être silencieuse.

Re-acquittement : si des parents avaient déjà acquitté, la carte repasse OUVERTE et doit être acquittée à nouveau. Une température corrigée de 36,5 à 38,5 est médicalement signifiante, un badge discret dans un feed déjà traité se perdrait. Effets de bord utiles : le tri place OUVERTE en tête donc la carte remonte d'elle-même, purge_at est recalculé, et repondre redevient possible puisqu'il exige statut === OUVERTE.

Les lignes card_responses antérieures sont conservées (c'est de l'historique). « Ce parent a-t-il acquitté la version corrigée ? » se détermine en comparant la date de sa réponse à corrige_le — à prévoir explicitement, sinon un ancien acquittement passera pour un acquittement de la correction.

Implémentation : le chemin existant majEnAttente exige un statut en attente ; pour soin_enfant la garde devient la règle des 24 h, la ligne d'agenda étant mise à jour en parallèle via maj. supprimer reçoit une branche dédiée : fenêtre 24 h au lieu du test OUVERTE | REFUSEE actuel.

6. Contraintes à transmettre à la purge TTL

Pour #196, pas encore implémenté :

  • ne pas toucher aux lignes d'agenda acceptées ;
  • ne pas supprimer physiquement les card_instances de type soin_enfant. L'agenda ne stocke que la période et le motif en texte : toute la donnée structurée (temperature_c, heure_releve, bobo_nature, historique[]) ne vit que dans le payload de la carte. Une suppression physique rendrait l'historique inexploitable autrement qu'en relisant une phrase. Les cartes soin sortent du feed par le filtre purge_at, mais leur ligne reste.

7. Tests

Création des trois sous-types, validations (bornes, longueurs, enum, format heure), dérivation du motif, correction dans les 24 h, refus au-delà, re-acquittement après correction.

Hors scope

  • Photos, ordonnances, documents médicaux
  • Sous-types comme type_code séparés (soin_temperature…) : un seul type + payload
  • Regroupement de plusieurs relevés d'une même journée
  • Champ couleur dans l'API (la teinte est un mapping front)
  • Poids de l'enfant → ticket dédié

Done when

  • Une AM crée les trois sous-types sur un placement ; les parents du foyer les voient dans GET /cards
  • Un parent acquitte → carte traitée, miroir AM cohérent
  • id_evenement renseigné, ligne d'agenda en accepte et non purgeable
  • Correction possible dans les 24 h, tracée, avec re-acquittement ; refusée au-delà
  • Un parent ne peut pas créer de carte soin

Réf.

Évolution notée (pas de ticket)

L'AM pourra plus tard partager son agenda au gestionnaire, qui disposerait alors de droits de modification sur les cartes verrouillées. D'où deux précautions dès maintenant : garder la fenêtre de 24 h comme règle de service contournable par un rôle habilité (pas une contrainte figée en base), et tracer l'auteur dans historique[].

## Contexte Epic [#165](https://git.ptits-pas.fr/jmartin/petitspas/issues/165) — file d'attention **Cartes**. Front associé : [#207](https://git.ptits-pas.fr/jmartin/petitspas/issues/207). Intention produit figée dans [docs/32_MINI-SPEC-BULLES-CARTES.md](https://git.ptits-pas.fr/jmartin/petitspas/src/branch/develop/docs/32_MINI-SPEC-BULLES-CARTES.md) §5. La nounou signale un événement concernant l'enfant (fièvre, bobo, médicament donné) **vers les parents**, qui **acquittent**. Ce n'est pas que de la messagerie : c'est loggé dans la file, lié à l'agenda, et acquittable sans noyer le chat. ## Cadrage commun (à ne pas perdre de vue) **L'outil n'a aucune valeur juridique**, comme le module contrat : la vraie vie prévaut. Une carte soin vaut un message WhatsApp du genre « l'enfant a de la fièvre, je lui donne du paracétamol ». C'est **informatif**, ce n'est ni un circuit de validation ni une autorisation. - L'absence de carte **n'engage pas** la responsabilité de l'AM : elle reste libre d'agir et de ne rien saisir. - L'outil **cadre et facilite** la transmission, il ne la contraint pas. - Aucune fonctionnalité ne doit être présentée comme une preuve, une autorisation ou une obligation. ## Ce qui rend ces cartes différentes des précédentes 1. Elles portent un **sous-type** (une seule famille `soin_enfant`, trois formulaires distincts). 2. Elles décrivent un **instant T** et non une période négociable. On les conserve malgré tout dans `evenements_agenda` : **c'est fait pour se souvenir**. La période y est dégénérée (`date_debut == date_fin`) et l'instant précis vit dans `heure_releve`. ## Sous-types retenus | `sous_type` | Modale | Réponse | |---|---|---| | `fievre` | Fièvre | `ack` | | `bobo` | Bobo | `ack` | | `medicament` | Médicament | `ack` | Pas de 4e sous-type « autre » : `autre` reste une **valeur de la liste des natures de bobo**. **Règle anti-ambiguïté** : fièvre → carte `fievre` (le médicament éventuel y est un champ satellite) ; médicament pour **toute autre raison** → carte `medicament`. Un seul chemin par fait consigné, sinon l'historique devient incohérent. **Plusieurs cartes par jour sont normales** : une fièvre se mesure plusieurs fois. Chaque relevé est une carte, pas de regroupement en V1. ## Contrat de payload Commun aux trois sous-types : `sous_type`, `date_debut` / `date_fin` (même jour en V1), **`heure_releve`** (`HH:mm`). L'heure est indispensable : 38,2 à 9 h et 38,2 à 16 h ne racontent pas la même histoire, et sans elle les parents la redemandent dans le chat. **`fievre`** | Champ | Règle | |---|---| | `temperature_c` | décimal ; saisie 3 chiffres avec virgule automatique (`365` → `36,5`) | | | bornes **34,0 – 43,0** ; **alerte visuelle** hors 36,0 – 38,0, **sans blocage** | | `medicament_donne` | texte libre, facultatif | On ne bloque pas une observation de santé : une hypothermie réelle doit pouvoir être consignée, et les mesures axillaires ou frontales descendent bas. Un formulaire qui refuse la réalité pousse à ne rien saisir. **Pas de champ poids** : c'est une donnée stable qui appartient à la fiche enfant, et l'AM n'a pas de balance. Voir [#208](https://git.ptits-pas.fr/jmartin/petitspas/issues/208). **`bobo`** | Champ | Règle | |---|---| | `bobo_nature` | `chute` \| `coup` \| `egratignure` \| `piqure` \| `brulure` \| `autre` | | `description` | texte, **max 120 caractères** | La limite est volontaire : le fait suffit dans la carte, le contexte va dans la messagerie. **`medicament`** | Champ | Règle | |---|---| | `medicament_nom` | obligatoire | | `contexte` | texte, **max 120 caractères** | ## À faire ### 1. Seed du type À ajouter à la liste de [database/migrations/2026_cards_system.sql](https://git.ptits-pas.fr/jmartin/petitspas/src/branch/develop/database/migrations/2026_cards_system.sql) (lignes 88-120, avec son `ON CONFLICT DO UPDATE`) : ```sql ( 'soin_enfant', true, 'Soins enfant', ARRAY['assistante_maternelle'], ARRAY['parent'], 'couple_parents', 'ack', 7, 'pink' ) ``` - `audience_resolver` = **`couple_parents`**, comme `conge_am` et `arret_maladie_am` : c'est le resolver des cartes émises par l'AM, qui ajoute tous les parents de l'enfant. (`couple_am` est l'inverse, pour les cartes émises par un parent.) → **aucun code d'audience à écrire**, `buildAudience` branche déjà sur ce champ. - `retention_days` = **7**, aligné sur la famille informative (`absence_enfant` est à 7 ; les 14 jours de `conge_am` / `arret_maladie_am` servent une décision sur une période à venir). Un relevé de la semaine dernière est du bruit dans le feed, l'historique vit dans l'agenda. À savoir : `purgeAt` applique `max(retention, 15)` tant que la carte est `OUVERTE`, donc une carte non acquittée reste 15 jours quoi qu'il arrive. - La colonne `couleur` existe encore en base sans être exposée par l'API ; on la renseigne par cohérence avec ses voisines. Le statut initial de la **carte** sera `OUVERTE` puisque `response_mode = ack` : rien à changer. ### 2. Enum agenda — trois endroits à tenir synchrones `TypeEvenementAgendaType` ne contient que `absence_enfant`, `conge_am`, `arret_maladie_am`. Pas de contrainte de prod, donc on modifie librement, mais il faut penser au schéma de référence sinon une base recréée repart sans la valeur : - [database/BDD.sql](https://git.ptits-pas.fr/jmartin/petitspas/src/branch/develop/database/BDD.sql) lignes 38-42 : ajouter `soin_enfant` à la création de l'enum (`synchronize: false` côté TypeORM, rien n'est déduit des entités) ; - un fichier incrémental dans `database/migrations/` : `ALTER TYPE type_evenement_agenda_type ADD VALUE 'soin_enfant'` pour les bases dev déjà en place ; - la valeur dans l'enum TS. Puis étendre `mapAbsenceType` dans `cards.service.ts`, qui **lève une exception** pour tout type non mappé. ### 3. Deux branches dans `evenements-agenda.service.ts` - `statutInitial` : seul `ABSENCE_ENFANT` renvoie `ACCEPTE` aujourd'hui, tout le reste tombe en `EN_ATTENTE`. `SOIN_ENFANT` doit renvoyer **`ACCEPTE`** — il n'y a rien à négocier. Effet de bord voulu : `expireAtFor` renvoie alors le sentinel `EXPIRE_ACCEPTE` (`9999-12-31`), donc **la ligne d'agenda ne se purge jamais**. - `assertCanCreateType` : l'AM est limitée à `CONGE_AM` / `ARRET_MALADIE_AM` ; ajouter `SOIN_ENFANT` pour l'AM, et surtout **ne pas** l'ouvrir aux parents. Attention : il existe deux fonctions `statutInitial` homonymes, une pour la carte et une pour l'agenda, dans deux services différents. ### 4. Payload structuré Le payload est aujourd'hui figé en dur à `date_debut` / `date_fin` / `motif` dans `creer`. Le rendre extensible pour accueillir les champs par sous-type, sans casser les types existants. **Le `motif` est dérivé côté back** à partir du payload (ex. `36,5 °C à 14:20`) ; le front ne l'envoie pas. C'est lui qui alimente le `motif` de la ligne d'agenda, il ne doit donc pas dépendre d'une chaîne construite par le client. ### 5. Correction encadrée — fenêtre de 24 h L'erreur de saisie est inévitable (38,5 tapé au lieu de 36,5). On autorise donc la correction, mais **bornée et tracée** : corrigeable brièvement, inaltérable ensuite. - **Qui** : l'AM auteure de la carte, personne d'autre. - **Quand** : moins de **24 h** après `cree_le`. Contrôle **côté back** ; le front se contente de masquer le bouton. - **Quoi** : les champs de payload du sous-type. Pas le `sous_type`, pas le placement, pas l'enfant. - **Mauvais sous-type** : géré par la suppression, elle aussi ouverte 24 h à l'auteure, et qui retire alors **carte + ligne d'agenda**. - **Après 24 h** : ni modification ni suppression. Une correction tardive se fait par une **nouvelle carte**, jamais par réécriture. **Trace** : conserver la valeur précédente dans un `historique[]` horodaté du payload, **avec son auteur**, et poser `corrige_le`. Une correction ne doit pas être silencieuse. **Re-acquittement** : si des parents avaient déjà acquitté, la carte **repasse `OUVERTE`** et doit être acquittée à nouveau. Une température corrigée de 36,5 à 38,5 est médicalement signifiante, un badge discret dans un feed déjà traité se perdrait. Effets de bord utiles : le tri place `OUVERTE` en tête donc la carte remonte d'elle-même, `purge_at` est recalculé, et `repondre` redevient possible puisqu'il exige `statut === OUVERTE`. Les lignes `card_responses` antérieures sont **conservées** (c'est de l'historique). « Ce parent a-t-il acquitté la version corrigée ? » se détermine en comparant la date de sa réponse à `corrige_le` — à prévoir explicitement, sinon un ancien acquittement passera pour un acquittement de la correction. **Implémentation** : le chemin existant `majEnAttente` exige un statut en attente ; pour `soin_enfant` la garde devient la règle des 24 h, la ligne d'agenda étant mise à jour en parallèle via `maj`. `supprimer` reçoit une branche dédiée : fenêtre 24 h au lieu du test `OUVERTE | REFUSEE` actuel. ### 6. Contraintes à transmettre à la purge TTL Pour [#196](https://git.ptits-pas.fr/jmartin/petitspas/issues/196), pas encore implémenté : - ne pas toucher aux lignes d'**agenda** acceptées ; - ne pas **supprimer physiquement** les `card_instances` de type `soin_enfant`. L'agenda ne stocke que la période et le `motif` en texte : toute la donnée structurée (`temperature_c`, `heure_releve`, `bobo_nature`, `historique[]`) ne vit que dans le payload de la carte. Une suppression physique rendrait l'historique inexploitable autrement qu'en relisant une phrase. Les cartes soin sortent du feed par le filtre `purge_at`, mais leur ligne reste. ### 7. Tests Création des trois sous-types, validations (bornes, longueurs, enum, format heure), dérivation du `motif`, correction dans les 24 h, refus au-delà, re-acquittement après correction. ## Hors scope - Photos, ordonnances, documents médicaux - Sous-types comme `type_code` séparés (`soin_temperature`…) : un seul type + payload - Regroupement de plusieurs relevés d'une même journée - Champ `couleur` dans l'API (la teinte est un mapping front) - Poids de l'enfant → ticket dédié ## Done when - Une AM crée les trois sous-types sur un placement ; les parents du foyer les voient dans `GET /cards` - Un parent acquitte → carte traitée, miroir AM cohérent - `id_evenement` renseigné, ligne d'agenda en `accepte` et non purgeable - Correction possible dans les 24 h, tracée, avec re-acquittement ; refusée au-delà - Un parent ne peut pas créer de carte soin ## Réf. - Mini-spec bulles §4–§5 · [docs/24_DECISIONS-PROJET.md](https://git.ptits-pas.fr/jmartin/petitspas/src/branch/develop/docs/24_DECISIONS-PROJET.md) §32 (Cartes ≠ agenda) - Module existant `backend/src/modules/cards/` ([#194](https://git.ptits-pas.fr/jmartin/petitspas/issues/194)) · SSE ([#195](https://git.ptits-pas.fr/jmartin/petitspas/issues/195)) ### Évolution notée (pas de ticket) L'AM pourra plus tard **partager** son agenda au gestionnaire, qui disposerait alors de droits de modification sur les cartes verrouillées. D'où deux précautions dès maintenant : garder la fenêtre de 24 h comme **règle de service** contournable par un rôle habilité (pas une contrainte figée en base), et tracer l'auteur dans `historique[]`.
jmartin added this to the 0.2.0 milestone 2026-10-05 20:44:26 +00:00
jmartin added the enhancementbackendv0.2.0 labels 2026-10-05 20:44:26 +00:00
jmartin changed title from [Backend] Cartes rose — type soin_enfant (AM → parents, ack + agenda) to [Backend] Cartes rose soin_enfant — 3 sous-types, instant T, correction 24 h 2026-10-05 21:33:25 +00:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: jmartin/petitspas#206