Apparence
Bibliothèque Postes
La bibliothèque de postes d'un encadrant (/postes, Bibliothèque ▸ Postes) : un catalogue de modèles d'horaires réutilisables pour bâtir les roulements.
Prérequis communs : make seed-demo, puis make seed-postes — qui donne au compte de démonstration une bibliothèque couvrant les cinq catégories.
Ce qui distingue cet écran des autres référentiels
Un poste n'est pas partagé. Il appartient au compte qui l'a créé, comme une liste de favoris personnelle — là où les établissements ou les types de contrat sont communs à toute l'association. Deux conséquences à vérifier, parce qu'aucune n'est visible sur un seul écran :
- personne ne voit la bibliothèque d'un autre,
is_staffcompris ; - il n'y a pas de portée par établissement : un encadrant qui construit des roulements sur plusieurs établissements garde une seule bibliothèque.
Le Code (JM, AM, JS, NS, JO) n'est pas saisi : il découle de la catégorie. Rien à taper, rien qui puisse entrer en collision.
1. Lire la bibliothèque et filtrer par catégorie
Tickets : APAJH-114 · Rôle : encadrant (compte de démonstration) · Prérequis : make seed-postes
Ouvrez Bibliothèque ▸ Postes.
✅ Résultat attendu : un tableau à six colonnes — Code, Libellé, Horaires, Pause, Durée nette, et les actions de ligne. Dix postes — deux par catégorie — dont les deux du matin portent tous deux le code JM. Une des deux Journée a deux créneaux : « 07:30–12:00 · 17:00–20:30 », 8h00 nettes.
Cliquez sur la puce de catégorie Matin, puis sur Tous.
✅ Résultat attendu : la liste se réduit aux seuls postes du matin, puis revient complète.
Créez un onzième poste (scénario 2), puis descendez en bas du tableau — la pagination ne se déclenche qu'au-delà de dix.
✅ Résultat attendu : dix lignes par page, la mention Page 1 sur N, et Page précédente désactivée sur la première page comme Page suivante sur la dernière.
2. Créer un poste de nuit
Tickets : APAJH-114 · Rôle : encadrant · Prérequis : aucun
C'est le scénario qui compte : un poste de nuit franchit minuit, et la maquette d'origine avait oublié la catégorie « Nuit » dans son formulaire alors que sa propre bibliothèque affichait déjà un poste NS. L'omission n'a pas été reproduite.
Créer un poste → catégorie Nuit, début 21:00, fin 07:00.
✅ Résultat attendu : l'encart de synthèse affiche Temps travaillé 10h00 — avant même d'enregistrer, et surtout pas une durée négative.
Enregistrer.
✅ Résultat attendu : une nouvelle ligne Nuit, code NS, durée nette 10h00.
Rouvrez le formulaire et donnez à un poste la même heure de début et de fin.
✅ Résultat attendu : refus avec « L'heure de fin doit différer de l'heure de début. » Un horaire qui franchit minuit reste, lui, parfaitement légitime.
Sur le poste de nuit 21:00 → 07:00, saisissez une pause de 08:30 à 08:30, puis de 20:00 à 20:30, puis de 00:50 à 01:10.
✅ Résultat attendu : la première pause est refusée sous les champs avec « L'heure de fin de pause doit différer de l'heure de début de pause. », la deuxième avec « La pause doit tenir à l'intérieur d'un créneau. ». Dans les deux cas Enregistrer reste désactivé et la synthèse affiche — au lieu d'une durée : plus de pause de 1440 min. La troisième, après minuit, est acceptée : 20 min. Une pause qui commence pile à l'heure de début du poste, ou qui finit pile à son heure de fin, est refusée elle aussi.
3. Éditer, dupliquer, supprimer
Tickets : APAJH-114 · Rôle : encadrant · Prérequis : make seed-postes
Sur la ligne Matin, Éditer, passez la catégorie à Soir, puis Enregistrer.
✅ Résultat attendu : la ligne devient Soir et son code passe de JM à JS. Le nombre de lignes ne change pas.
Dupliquer un poste.
✅ Résultat attendu : le panneau s'intitule Créer un poste — c'est bien une création — et il arrive pré-rempli avec la catégorie et les horaires du poste d'origine. Après Enregistrer, l'original est toujours là, à côté de son doublon.
Supprimer le poste, puis Annuler dans la confirmation.
✅ Résultat attendu : rien n'est supprimé. Confirmer, en revanche, retire la ligne.
4. Vérifier le cloisonnement entre bibliothèques
Tickets : APAJH-114 · Rôle : deux comptes différents, dont un is_staff · Prérequis : make seed-postes sur le compte de démonstration
Aucun test end-to-end ne couvre ce scénario — les specs tournent sur un seul compte — et c'est pourtant la règle centrale du domaine.
Connectez-vous avec un autre compte que celui qui a joué
make seed-postes, et ouvrez Bibliothèque ▸ Postes.✅ Résultat attendu : une bibliothèque vide. Les postes de l'autre compte n'apparaissent pas.
Recommencez avec un compte
is_staff(par exemple depuis le back-office).✅ Résultat attendu : vide également. Le statut
is_staffest le droit d'écriture sur les référentiels partagés ; il ne donne aucune vue sur la bibliothèque personnelle d'autrui.
Couverture automatique : e2e/postes.spec.ts couvre la liste, le filtre, la pagination, la création d'un poste de nuit, le refus d'une pause hors du poste sur mobile, l'édition, la duplication, la suppression avec confirmation, et le tableau sur mobile. Côté serveur, backend/tests/test_postes.py couvre le calcul de la durée nette, le franchissement de minuit, la pause comprise dans le poste et le cloisonnement par compte — c'est-à-dire la moitié serveur du scénario 4.
5. Créer un poste de plusieurs créneaux
Tickets : APAJH-300 · Rôle : encadrant · Prérequis : make seed-postes, une trame et un salarié planifiés (make seed-all)
Critères d'acceptation du ticket
- Un poste peut être créé avec N créneaux sur la même journée.
- Ce poste est utilisable dans une trame, et la durée de la journée correspond à la somme des créneaux.
- L'option de pause non rémunérée n'est plus proposée à la création d'un poste.
- La pause rémunérée reste facultative et éditable.
Créer un poste → catégorie Journée, premier créneau 08:30 → 12:30, puis Ajouter un créneau et 13:30 → 17:30.
✅ Résultat attendu : la synthèse affiche Temps travaillé 8h00 — l'heure entre les deux créneaux n'est pas travaillée. Le formulaire ne propose aucun choix « Rémunérée / Non rémunérée » : la pause est seulement « Pause rémunérée », et facultative.
Saisissez une pause 12:45 → 13:00, puis 15:00 → 15:15.
✅ Résultat attendu : la première est refusée avec « La pause doit tenir à l'intérieur d'un créneau. » (elle tombe dans l'écart, qui n'est déjà pas travaillé) et Enregistrer reste désactivé. La seconde est acceptée : Pause rémunérée 15 min, et le temps travaillé reste 8h00.
Donnez au second créneau 11:00 → 17:30, puis remettez 13:30.
✅ Résultat attendu : « Chaque créneau doit commencer après la fin du précédent. » tant qu'ils se chevauchent ; un créneau Retirer (poubelle) se supprime tant qu'il en reste un autre.
Enregistrer, puis Éditer le poste.
✅ Résultat attendu : la ligne affiche Horaires « 08:30–12:30 · 13:30–17:30 », Pause « Rémunérée · 15 min », Durée nette 8h00. Le formulaire rouvre les deux créneaux et la pause ; la pause se modifie ou s'efface.
Dans Bibliothèque ▸ Trames, affectez ce poste à une case, enregistrez, puis affectez la trame à un salarié.
✅ Résultat attendu : la case affiche « 08:30–12:30 · 13:30–17:30 ». Dans Planning, la journée montre deux blocs — plus de présence continue de 08:30 à 17:30 — et le Total de la ligne compte 8h. L'infobulle du second bloc nomme la pause.
Ouvrez la journée, Modifier la journée, et choisissez ce poste dans le Poste de la carte 1. Rejouez sur téléphone.
✅ Résultat attendu : la carte prend le premier créneau et une carte 2 s'ajoute avec le second, pause comprise ; chacune reste modifiable.
Sur une ligne Poste à pourvoir, cliquez une journée libre, choisissez un salarié, puis ce poste dans le Poste de la carte 1, et Enregistrez. Rejouez sur téléphone.
✅ Résultat attendu : le poste se déplie de même en deux cartes, pause sur la seconde ; une fois enregistrée, la journée du salarié montre les deux blocs.
Ouvrez un poste qui portait, avant la mise à jour, une pause non rémunérée (par exemple la « Journée » 07:30 → 20:30, pause 12:00 → 17:00 de la démo).
✅ Résultat attendu : il a été repris en deux créneaux autour de cette pause — 07:30–12:00 et 17:00–20:30 —, sans pause, avec la même durée nette. Un poste qui avait une pause rémunérée la garde, sur un seul créneau.
Couverture automatique : e2e/postes.spec.ts → « créer un poste de deux créneaux » (bureau et mobile) ; e2e/trames.spec.ts → « une case porte un poste de deux créneaux » (bureau et mobile) ; e2e/planning.spec.ts → « un poste de deux créneaux déplie deux cartes » et « « Poste à pourvoir » déplie un poste de deux créneaux et les enregistre » (bureau et mobile). Côté serveur, backend/tests/test_postes.py (créneaux, pause, API), backend/tests/test_postes_slots_migration.py (la reprise de l'étape 8), backend/tests/test_planning_calendar.py (deux blocs pour un poste), backend/tests/trames/test_trames_alerts.py (l'écart vu comme une pause par les règles) et backend/tests/shift_changes/test_shift_change_filing.py.
6. Donner une couleur à un poste dès la bibliothèque
Tickets : APAJH-328 · Rôle : encadrant · Prérequis : une trame existante
Dans Bibliothèque ▸ Postes, cliquez sur Créer un poste.
✅ Résultat attendu : le formulaire propose Couleur du poste, avec Sans couleur sélectionné par défaut et les dix pastels des trames.
Choisissez une catégorie, des horaires, une couleur, puis Enregistrer.
✅ Résultat attendu : la ligne du poste porte une pastille de cette couleur à côté de son code, qui garde la teinte de sa catégorie.
Ouvrez une trame avec Éditer, affectez ce poste à une case et enregistrez la case.
✅ Résultat attendu : la case prend la couleur du poste, et non plus la teinte de sa catégorie.
Rouvrez la case, choisissez une autre couleur et enregistrez, puis rouvrez-la et choisissez Sans couleur.
✅ Résultat attendu : la couleur de la case l'emporte sur celle du poste ; « Sans couleur » rend à la case la couleur du poste.
Revenez à la bibliothèque, éditez le poste et choisissez Sans couleur.
✅ Résultat attendu : la pastille disparaît de la ligne, et les cases de ce poste sans couleur propre reprennent la teinte de sa catégorie.
Couverture automatique : e2e/postes.spec.ts → « donner puis retirer une couleur à un poste » et e2e/trames.spec.ts → « une case prend la couleur de son poste, sauf la sienne » (bureau et mobile) ; backend/tests/test_postes.py (couleur enregistrée, retirée, hors palette refusée).
Points d'attention
- Le code n'est jamais saisi. S'il ne correspond pas à la catégorie, le défaut est dans la table de correspondance, pas dans la saisie.
- La maquette se contredit sur ces codes : sa bibliothèque affiche
AMpour Après-midi, son gestionnaire d'enregistrement en dériveAP. Ce sont les codes affichés qui font foi. - Les tests end-to-end tournent sans backend : ils prouvent que l'interface se comporte comme l'API le lui annonce, pas que l'API applique le cloisonnement. Les deux volets sont à vérifier — le scénario 4 est là pour ça.