Apparence
Planning manager
Le planning d'équipe (/planning, Activité ▸ Planning), vue Général : une ligne par salarié, une colonne par jour.
À lire avant de commencer
Ce chapitre se joue en deux modes, et chaque scénario dit lequel.
L'écran lit la base par défaut. La seed couvre désormais deux années de journées, nuits, journées coupées, absences et anomalies isolées. Le jeu embarqué reste le support des scénarios visuels et des tests end-to-end, notamment les astreintes et collisions de codes. Ses identifiants appartiennent à l'ancienne cohorte : utilisez le mode réel pour les liens vers une fiche et les écritures.
D'où le partage :
- les scénarios 1 à 12 se jouent en mode démonstration. Ils vérifient ce que l'écran affiche, et chacun dit en tête ce qu'aucun seed n'écrit à sa place ;
- les scénarios 13 à 21 se jouent sur les données réelles. Ils vérifient que l'écran lit et écrit vraiment la base — ce que la démonstration, par construction, ne peut pas montrer : elle ne connaît ni rôle, ni périmètre.
Quatre commandes écrivent : l'Enregistrer du formulaire Modifier la journée — qui s'appelle Affecter un poste sur une journée où rien n'est travaillé, et dont la poubelle du dernier créneau enregistre un repos après confirmation —, celui du panneau Ajouter un poste d'une ligne Poste à pourvoir, celui du wizard Déclarer une absence, et le Supprimer l'absence que le détail d'un jour d'absence offre sous ses autres boutons — les deux dernières recettées dans leur propre chapitre, Absences § 11. Tout le reste se lit. Deux comportements à connaître, sans quoi vous ouvrirez des anomalies pour rien :
- « Publier le planning » et les formats d'export sont grisés ;
- les trois actions du bouton + gardent leur aspect normal. Le clic ne fait rien, et une infobulle au survol affiche « Disponible avec l'API planning. »
Prérequis communs à tout le chapitre : la nouvelle base de recette, préparée par make seed-all, selon les instructions communes. Les deux managers ont leurs bibliothèques et tous les salariés sont planifiés. Avec --reference-date 2026-10-07, les dates ci-dessous couvrent 2025/2026.
Scénarios en mode démonstration
Les données de démonstration ne s'affichent pas toutes seules : il faut les activer, et c'est une manipulation sur le serveur. Si vous n'avez pas accès à la machine, demandez-la à un développeur avant de commencer. La commande à lui donner :
bash
# dans .env : VITE_ENABLE_MOCK=true
docker compose up -d --force-recreate frontendRechargez ensuite la page avec Ctrl + Maj + R (Cmd + Maj + R sur Mac) : un simple F5 ne suffit pas. Remettez VITE_ENABLE_MOCK=false avant de passer aux scénarios 13 et 14.
Dans ce mode, rien de ce que vous voyez ne vient de la base — et l'Enregistrer de « Modifier la journée », lui, y écrit quand même : la grille ne bougera donc pas sous vos yeux. C'est pour cela que l'enregistrement a son scénario ailleurs, sur données réelles.
1. Lire une semaine de planning
Tickets : APAJH-240 · APAJH-113 · Rôle : encadrant · Prérequis : mode démonstration activé
Ce que la base ne sait pas encore produire : les astreintes du bandeau (aucun seed n'en crée — elles se saisissent au back-office, Planning ▸ Astreintes). Les journées coupées existent aussi dans la nouvelle seed réelle.
Ouvrez Activité ▸ Planning.
✅ Résultat attendu : la première colonne s'intitule Utilisateurs, puis viennent les sept jours de la semaine, et la dernière colonne à droite s'intitule Total.
Faites défiler la grille vers la droite.
✅ Résultat attendu : la colonne des noms reste visible. La colonne Total, elle, défile avec le reste.
Regardez les bandeaux — les lignes pleine largeur qui coupent la grille pour annoncer une unité.
✅ Résultat attendu : Unité Soin annonce ses astreintes avec le numéro à appeler et « 7 utilisateurs ». Unité Éducative en annonce 7 également — une personne appartient aux deux équipes, et compte donc dans les deux (scénario 11).
Regardez les blocs colorés dans les cases.
✅ Résultat attendu : chaque bloc porte ses horaires, l'établissement et la durée. Un liseré vertical coloré le borde à gauche : c'est la couleur de l'établissement. Certains jours portent deux blocs empilés — une journée coupée.
Repérez la colonne du jour et celles du week-end.
✅ Résultat attendu : la colonne d'aujourd'hui est teintée en vert clair, et son en-tête est souligné d'un trait vert. Samedi et dimanche prennent un fond bleuté.
Cliquez sur le bandeau Unité Soin.
✅ Résultat attendu : les lignes de l'unité disparaissent et le chevron pivote. L'autre unité ne bouge pas. Un second clic rouvre.
Cliquez la flèche › pour passer à la semaine suivante, puis survolez l'en-tête de la colonne du jeudi — en démonstration, c'est le jour férié de la semaine affichée.
✅ Résultat attendu : la colonne du jeudi est grisée exactement comme un samedi, en-tête compris, et l'infobulle de l'en-tête nomme le férié (« Ascension »). La teinte ne dit pas que personne ne travaille : les blocs présents restent affichés. Sur les données réelles, ce sont les fériés du référentiel (section Jours fériés du back-office) qui se grisent ainsi — donc rien à voir la plupart des semaines.
Couverture automatique : e2e/planning.spec.ts vérifie la structure des colonnes, le pli et le dépli d'un bandeau, la journée coupée, le grisé du férié, et le fait que la grille défile dans sa boîte. Le reste est à regarder.
2. Changer de période et passer à la vue mois
Tickets : APAJH-240 · Rôle : encadrant · Prérequis : mode démonstration activé
Les codes de cette démonstration viennent des fixtures front. Sur les données réelles, la seed fournit aussi des absences et des repos ; leur lecture et les écritures se vérifient à partir du scénario 14.
Cliquez la flèche › à droite du titre de la semaine, puis le bouton Aujourd'hui.
✅ Résultat attendu : le titre change de semaine, puis revient exactement à celle de départ. Chaque semaine est peuplée, quelle que soit la date.
Basculez sur Mois.
✅ Résultat attendu : autant de colonnes que le mois a de jours. Les cases rétrécissent : une journée travaillée n'affiche plus que l'heure de début et celle de fin, l'une sous l'autre ; une absence ou un repos affiche son code (
CA,R…). La dernière colonne s'intitule Cumul et non Total.
Couverture automatique : e2e/planning.spec.ts vérifie le va-et-vient entre périodes et le passage en vue mois.
3. Restreindre l'effectif affiché
Tickets : APAJH-240 · Rôle : encadrant · Prérequis : mode démonstration activé
La barre d'outils propose deux filtres, établissement et équipe, et une recherche salarié (scénario 47).
Dans le filtre établissement, choisissez Siège social.
✅ Résultat attendu : seule la ligne de Claire Boyer reste, la seule personne rattachée à ce site. L'effectif annoncé sur son bandeau ne change pas — il continue d'annoncer « 7 utilisateurs » : il décrit l'équipe, pas l'écran.
Ajoutez le filtre équipe sur Unité Soin, où personne n'est rattaché à ce site.
✅ Résultat attendu : la grille disparaît et affiche « Aucun salarié ne correspond aux filtres. » Choisissez bien une paire vide : les deux filtres se cumulent, et une personne peut appartenir à deux équipes (scénario 11), si bien qu'une unité qui semble étrangère à un établissement peut tout de même y compter quelqu'un.
Couverture automatique : e2e/planning.spec.ts vérifie les deux filtres pris séparément. La combinaison qui vide la grille est à jouer à la main.
4. Ouvrir le panneau d'un salarié, puis celui d'un poste
Tickets : APAJH-240 · Rôle : encadrant · Prérequis : mode démonstration activé
Ce que la base ne sait pas encore produire : l'encart ASTREINTE DE LA SEMAINE de l'étape 2 (rien n'en crée). Le jour d'absence de l'étape 4 s'observe désormais sur données réelles, en le déclarant au back-office — c'est le scénario 16 de ce chapitre ; un jour de repos aussi.
Cliquez sur le nom Émilie Chen.
✅ Résultat attendu : un panneau s'ouvre à droite, titré UTILISATEUR SÉLECTIONNÉ. Il affiche son identité, IDE · 100%, son téléphone, son établissement principal, ses heures de la semaine sur 35h et son annualisation.
Elle est d'astreinte : regardez l'encart ASTREINTE DE LA SEMAINE.
✅ Résultat attendu : « Début : vendredi à 19h » et « Fin : 07h ». Le jour n'est pas répété sur la fin, l'astreinte ne durant qu'une nuit.
Cliquez Voir la fiche.
✅ Résultat attendu : une fiche salarié s'ouvre. En mode démonstration, les lignes du planning ne correspondent plus aux salariés de la seed de développement : la fiche ouverte est celle qui porte le même identifiant, pas forcément Émilie Chen.
Revenez, puis cliquez sur un bloc coloré dans une case.
✅ Résultat attendu : le panneau bascule sur DÉTAIL DE LA JOURNÉE — horaires, durée, pause, établissement de chaque créneau du jour. Aucune information du panneau précédent ne subsiste. Une journée d'absence ou de repos affiche un statut à la place des horaires.
Couverture automatique : e2e/planning.spec.ts vérifie les deux panneaux, le passage de l'un à l'autre et le jour d'absence.
5. Modifier un poste
Tickets : APAJH-240 · Rôle : encadrant · Prérequis : mode démonstration activé
Ce scénario vérifie la saisie. L'enregistrement, lui, a le sien : scénario 13. Depuis APAJH-291, le formulaire porte sur toute la journée : le scénario 37 en détaille les créneaux multiples.
Cliquez un bloc coloré, puis Modifier la journée au bas du panneau.
✅ Résultat attendu : le panneau garde sa place et change de contenu — un titre Modifier la journée, un lien Retour en haut, et une carte Créneau 1 qui porte déjà les horaires du jour, et la pause du poste. Même seule, la carte a sa poubelle : retirer le dernier créneau passe la journée en repos, après confirmation (scénario 37).
Ouvrez Motif, dans la carte.
✅ Résultat attendu : trois choix — Remplacement, Demande personnelle, Urgence. « Demande personnelle » est déjà sélectionné.
Tapez trois lettres dans Rechercher un poste…, puis choisissez une entrée.
✅ Résultat attendu : chaque suggestion porte la couleur de son poste. Le choix recopie les horaires du poste dans la carte — rien ne change en coulisse. Le pavé de comparaison apparaît : heures théoriques, heures demandées, écart compteur (vert au-dessus, rouge en dessous).
Choisissez un poste de nuit (21:00 → 07:00).
✅ Résultat attendu : il est compté 10h, pas une durée négative. La mention « Pause par défaut 20 min » n'existe plus : elle laissait croire à une pause que rien ne déduisait.
Cliquez + Ajouter un créneau.
✅ Résultat attendu : une carte Créneau 2 apparaît, qui commence à l'heure où finit la première et reprend son motif. Une fois ses horaires complétés, les deux créneaux s'additionnent dans Heures saisies. Chaque carte porte sa poubelle.
Dans une carte, dépliez Plus d'options.
✅ Résultat attendu : Établissement / Service, positionné sur le site du créneau, puis trois options à cocher — Télétravail, Hors établissement, Inclus temps de trajet — et un Commentaire. Le dépliant est déjà ouvert sur un créneau qui y dit quelque chose.
Cliquez Retour, puis rouvrez Modifier la journée.
✅ Résultat attendu : le panneau revient sur DÉTAIL DE LA JOURNÉE, à l'endroit d'où vous êtes parti — et le formulaire rouvert repart de la journée telle qu'elle est enregistrée : la saisie abandonnée, carte ajoutée comprise, n'y est plus. Annuler ferme le panneau. Depuis APAJH-291, une saisie ne survit pas au formulaire qui l'a ouverte : c'est ce qui empêche un créneau abandonné de partir avec l'enregistrement suivant (scénario 34).
Couverture automatique : e2e/planning.spec.ts vérifie l'ouverture du formulaire, le retour au détail qui abandonne la saisie, le remplissage des horaires par la bibliothèque et le cumul de plusieurs créneaux.
6. Pourvoir un poste en ajoutant un salarié à l'unité
Tickets : APAJH-240 · APAJH-267 · Rôle : encadrant · Prérequis : mode démonstration activé
Ce scénario vérifie la porte ouverte depuis l'en-tête de la ligne, qui nomme une unité et aucun jour : elle ajoute le salarié à l'équipe du bandeau, du même geste que Bibliothèque ▸ Équipes. Les journées, elles, se saisissent ensuite sur sa ligne, case par case — scénario 29.
Chaque unité se termine par une ligne Poste à pourvoir. Cliquez sur son en-tête, dans la colonne de gauche.
✅ Résultat attendu : un panneau Ajouter un salarié à l'équipe s'ouvre à droite, avec une carte rappelant l'unité et son établissement — « Unité Soin · FAM Les Cigales » — et une phrase qui dit ce que l'enregistrement fera : « Le salarié choisi rejoint l'équipe Unité Soin à partir d'aujourd'hui. »
Regardez ce que le panneau contient.
✅ Résultat attendu : la carte de l'unité, un seul champ — « Affecter à un salarié » — et les deux boutons. Ni motif, ni créneaux : des heures sans date seraient des heures sur rien. C'est la forme de la maquette.
Tapez trois lettres dans Rechercher un salarié…, puis choisissez un nom.
✅ Résultat attendu : la liste des suggestions s'ouvre à la largeur exacte du champ, réduite aux noms correspondants. Le nom choisi remplit le champ, avec une croix pour l'effacer.
Cherchez Boyer, sans choisir.
✅ Résultat attendu : la ligne porte une étiquette ambre Intérim — la même que la fiche salarié. Elle suit le type de contrat (« contrat non géré par l'APAJH »), pas le libellé : un type ajouté depuis le back-office la porte sans redéploiement. Les autres salariés n'en ont pas.
Cherchez un salarié de l'unité elle-même, par exemple Chen sur Unité Soin.
✅ Résultat attendu : aucune suggestion. Les membres de l'unité en sont déjà : il n'y aurait rien à enregistrer pour eux.
Cherchez Charef — quelqu'un qu'aucun bandeau de l'écran ne montre, parce qu'il n'a encore aucune équipe et travaille dans un autre établissement.
✅ Résultat attendu : il est proposé, et l'enregistrement le fait entrer dans l'unité comme n'importe qui d'autre. La recherche couvre les salariés du périmètre de votre rôle, et non les seules lignes affichées : un nouvel embauché sans affectation est précisément celui à qui un poste vacant s'adresse. Un salarié qui ne travaille dans aucun établissement de votre périmètre n'est jamais proposé. (Hors mode démonstration, prenez un salarié réel de votre périmètre qu'aucune équipe ne montre — le nom ci-dessus n'existe que dans l'effectif simulé, qui n'a pas de périmètre.)
Choisissez quelqu'un d'une autre unité, puis Enregistrer.
✅ Résultat attendu : un bandeau « Salarié ajouté à l'équipe », et la personne prend dans l'unité une ligne de membre ordinaire — son nom, son métier en dessous, comme ses voisins. L'effectif annoncé par le bandeau augmente d'une unité. Ses journées déjà planifiées ailleurs s'affichent sur cette ligne : c'est ce qui permet de voir ce qu'elle doit déjà avant de lui en ajouter.
Regardez le bas du bandeau.
✅ Résultat attendu : une ligne « Poste à pourvoir » vide subsiste. Un poste pourvu n'est pas le dernier, et la porte ne se referme pas derrière lui.
Cliquez une case vide de la ligne du salarié que vous venez d'ajouter.
✅ Résultat attendu : Affecter un poste s'ouvre directement sur cette personne — on ne redemande pas qui c'est. C'est tout l'intérêt de l'avoir ajoutée une fois.
Rechargez la page (F5).
✅ Résultat attendu : la ligne est toujours là. L'attribution est une affectation à l'équipe, enregistrée : la personne appartient à l'unité, et plus rien ne distingue sa ligne de celle des autres membres.
Ouvrez sa fiche : Utilisateurs ▸ son nom ▸ Lieux de travail & équipes.
✅ Résultat attendu : la nouvelle équipe y figure. C'est le même enregistrement que Bibliothèque ▸ Équipes, pas un second mécanisme.
Revenez sur une semaine déjà passée, avec les flèches de période.
✅ Résultat attendu : le salarié ajouté n'apparaît pas dans l'unité sur cette semaine-là. L'affectation commence aujourd'hui — c'est la convention des trois écrans qui en écrivent — et le planning n'affiche dans une équipe que les gens qui en font partie à la semaine affichée. Ce n'est pas une anomalie : il n'était pas de l'équipe la semaine dernière.
Couverture automatique : e2e/planning.spec.ts vérifie l'ouverture du panneau, la phrase qui annonce ce qui sera écrit, l'absence de motif et de créneaux, l'étiquette Intérim sur le seul salarié concerné, le refus des membres de l'unité, l'offre d'un salarié qu'aucun bandeau ne montre, et l'affectation envoyée — la liste active de l'équipe, augmentée du salarié choisi — puis la ligne vide qui subsiste. Que la ligne de membre naisse de cette écriture ne s'y voit pas : l'écran lit des données simulées en test, et c'est l'objet de l'étape 10 ci-dessus.
7. Lire la légende et les alertes
Tickets : APAJH-240 · APAJH-251 · APAJH-254 · Rôle : encadrant · Prérequis : mode démonstration activé
Ce scénario visuel conserve les exemples des fixtures. La nouvelle seed produit également des dépassements réels sur deux semaines isolées : voir le scénario 14 pour leurs dates et résultats.
Cliquez Légende.
✅ Résultat attendu : onze pastilles nommées — Matin, Après-midi, Soir, Journée, Nuit, Évènement, Absence / Congé, Repos, Demande en attente, Poste modifié, Alerte — puis la ligne des pictogrammes qu'une case peut porter.
Fermez la légende et repérez la case du lundi de Julien Dubois, puis celle d'Isabelle Aubert.
✅ Résultat attendu : les deux portent le contour en pointillé de la pastille « Poste modifié », et un ⟳ en bas à droite. Celle de Julien Dubois porte en plus une maison (télétravail), celle d'Isabelle Aubert une flèche sortante (hors établissement). Les autres cases de la grille n'ont ni pointillé ni pictogramme : seule une journée modulée en porte.
Survolez la case de Julien Dubois sans cliquer.
✅ Résultat attendu : l'infobulle épelle ce que les pictogrammes abrègent — « … · Poste modifié · Télétravail ».
Cliquez la case de Julien Dubois pour ouvrir son panneau, puis celle d'Isabelle Aubert.
✅ Résultat attendu : sous l'identité, le panneau de Julien Dubois porte un encart « Prévu initialement » nommant le poste que sa trame donnait — « JO · Journée · 07:30–20:30 » — alors que la journée affichée, elle, est celle de la modulation. Le panneau d'Isabelle Aubert n'a pas cet encart : sa modulation ne réécrit pas les horaires, elle dit seulement que la journée se fait hors établissement, donc rien n'a été prévu à la place. Les cases non modulées n'en portent pas non plus.
Passez en vue Mois.
✅ Résultat attendu : le pointillé subsiste, les pictogrammes non. Une colonne de mois fait 42 px : trois icônes y couvriraient les horaires qu'elles qualifient. Ce n'est pas une anomalie.
Revenez en vue Semaine, puis ouvrez le panneau Alertes.
✅ Résultat attendu : les infractions aux règles de durée du travail de la période (référentiel
apps.rules_engine), classées par gravité, chacune nommant le salarié, la date et le motif. Le point rouge du bouton et la pastille Alerte de la légende sont de la même couleur.Cherchez un total supérieur à 44h dans la colonne Total.
✅ Résultat attendu : il s'affiche en rouge. En vue Mois, aucun total n'est signalé : un cumul mensuel ne se compare pas à un plafond hebdomadaire.
Couverture automatique : e2e/planning.spec.ts vérifie la légende, le panneau d'alertes, le total au-delà de 44h, les marqueurs d'une journée modulée — pointillé et pictogrammes — jusque dans le panneau qui les épelle, et l'encart « Prévu initialement » : présent sur la journée dont les horaires ont été réécrits, en bureau comme en mobile, absent sur une journée que la trame répond seule.
8. Vérifier ce qui n'est pas branché
Tickets : APAJH-240 · Rôle : encadrant · Prérequis : mode démonstration activé
Regardez le haut de la barre d'outils.
✅ Résultat attendu : Publier le planning est grisé. Aucune mention de sauvegarde n'apparaît à côté : la maquette en affiche une, elle a été retirée parce qu'elle annonçait un enregistrement qui n'a pas lieu.
Ouvrez le menu Exporter.
✅ Résultat attendu : le menu s'ouvre et propose Excel et PDF, tous deux grisés.
Cliquez le bouton + flottant, en bas à droite.
✅ Résultat attendu : il se transforme en croix sans se déplacer, et ouvre un menu de trois actions — Déclarer une absence, Modifier un poste, Créer un planning. Chacune garde son aspect normal ; le clic ne fait rien et l'infobulle affiche « Disponible avec l'API planning. » Échap referme. La déclaration d'absence, elle, est branchée — mais depuis le panneau d'une case, où il y a un salarié et un jour ; depuis le bouton flottant il n'y en a pas, donc c'est un autre parcours.
Couverture automatique : e2e/planning.spec.ts vérifie l'ouverture des deux menus. L'aspect des boutons est à regarder.
9. Accéder au planning manager sur mobile
Tickets : APAJH-240 · APAJH-103 · APAJH-143 · Rôle : encadrant · Prérequis : viewport 390 × 844
La navigation mobile reste centrée sur « Mon espace », mais les écrans manager routés restent accessibles par leur adresse directe. Le planning conserve donc sa grille défilante, et le détail d'un évènement s'ouvre depuis le bas.
Ouvrez l'application en 390 × 844.
✅ Résultat attendu : la navigation bascule sur Mon espace, et l'entrée Activité ▸ Planning n'apparaît plus.
Saisissez directement l'adresse
/planning.✅ Résultat attendu : la grille s'affiche dans sa propre zone de défilement, sans faire déborder la page horizontalement.
Couverture automatique : e2e/planning.spec.ts vérifie qu'en 390 px le portail n'expose pas l'entrée Planning, que l'adresse directe reste utilisable et que le détail d'un évènement prend la forme d'un panneau bas.
10. Vérifier que deux postes de même nom ne se confondent pas
Tickets : APAJH-240 · Rôle : encadrant · Prérequis : mode démonstration activé
Deux postes différents peuvent porter le même code : il se déduit de la catégorie, si bien que tous les postes du matin s'appellent JM. Ce scénario vérifie que la grille les distingue quand même — c'est-à-dire qu'elle affiche les horaires du poste réellement affecté, et non ceux de son homonyme.
Ce que la base ne sait pas encore produire : la collision elle-même. Elle compte pourtant bien quatre postes par code — un par compte encadrant — mais aucune trame seedée n'en amène deux dans la même période. Reproduire ce scénario sur données réelles demanderait de fabriquer la donnée exprès : deux postes de même catégorie, d'horaires différents, dans deux bibliothèques, employés par deux salariés affichés à l'écran.
Ouvrez Activité ▸ Planning et repérez la ligne Émilie Chen.
✅ Résultat attendu : elle travaille trois matinées dans la semaine, dont deux de 07:00 à 14:30 et une de 07:30 à 15:30. Les trois cases n'affichent pas les mêmes horaires.
Cliquez la case de 07:00–14:30, notez le panneau, fermez-le avec Échap, puis cliquez celle de 07:30–15:30.
✅ Résultat attendu : les deux panneaux s'intitulent Matin, et tout le reste diffère — 7h contre 7h30 de durée, une pause à 11:00–11:30 contre 11:30–12:00.
Regardez la colonne Total de sa ligne.
✅ Résultat attendu : 35h30, c'est-à-dire deux matinées à 7h, une à 7h30 et deux soirées à 7h. Un total de 35h signalerait que les deux matinées ont été comptées au même horaire.
Ouvrez Modifier la journée depuis l'une de ces cases, puis tapez « Matin » dans le champ Poste.
✅ Résultat attendu : la liste propose les deux matinées séparément — « JM · Matin · 07:00–14:30 » et « JM · Matin · 07:30–15:30 ».
Couverture automatique : e2e/planning.spec.ts vérifie les horaires des trois matinées et le total de la ligne. Les panneaux et la liste des postes sont à regarder.
11. Voir un salarié dans deux équipes
Tickets : APAJH-243 · Rôle : encadrant · Prérequis : mode démonstration activé
Un salarié peut appartenir à plusieurs équipes. Sa ligne apparaît alors sous chacune d'elles, et c'est la même ligne, répétée : mêmes journées, même total. Ce scénario vérifie cette duplication, et qu'elle ne se confond pas avec un doublon.
Sur la base de recette, Camille appartient aux deux équipes Accompagnement du FAM et de l'IME ; chaque manager appartient à deux équipes Encadrement. Ce scénario visuel utilise les membres des fixtures. Le périmètre réel du lecteur se vérifie au scénario 15.
Ouvrez Activité ▸ Planning et parcourez les deux bandeaux, Unité Soin et Unité Éducative.
✅ Résultat attendu : Isabelle Aubert a une ligne sous chacun des deux bandeaux. Son nom apparaît donc deux fois à l'écran.
Comparez ses deux lignes case par case, puis leur colonne Total.
✅ Résultat attendu : les deux lignes sont identiques — mêmes postes aux mêmes jours, même total. Ce n'est pas une personne comptée deux fois, c'est la même ligne montrée dans les deux équipes auxquelles elle appartient.
Regardez l'effectif annoncé par chaque bandeau.
✅ Résultat attendu : 7 utilisateurs de part et d'autre. Elle compte dans les deux équipes, parce qu'elle est dans les deux.
Ouvrez Filtrer par équipe et cochez Unité Soin seule.
✅ Résultat attendu : il ne reste qu'une de ses deux lignes, et le bandeau Unité Éducative a disparu en entier.
Décochez le filtre, puis cliquez le bandeau Unité Soin pour le replier.
✅ Résultat attendu : seul ce bandeau se replie. La ligne d'Isabelle Aubert reste visible sous Unité Éducative — le pli porte sur le bandeau, pas sur la personne.
Cliquez son nom dans l'une des deux lignes, puis Voir la fiche.
✅ Résultat attendu : la fiche ouverte est la sienne, la même depuis l'une ou l'autre ligne. Les deux lignes désignent une seule personne.
Couverture automatique : e2e/planning.spec.ts vérifie les deux lignes, leurs cellules et leur total identiques, et qu'un filtre sur une équipe n'en laisse qu'une. Le pli, l'effectif et la fiche sont à regarder.
12. Les flèches de navigation restent à la même place, quelle que soit la période affichée
Tickets : APAJH-250 · Rôle : encadrant · Prérequis : mode démonstration activé
Le libellé entre les deux flèches ("14 septembre — 20 septembre 2026", "Août 2026"…) se réservait un espace fixe qui suffisait à la plupart des libellés, mais pas aux plus larges — une semaine entière dans un mois au nom long ("septembre"), ou le rendu de certains chiffres, plus larges que d'autres dans une police à chasse proportionnelle. La flèche de droite se décalait alors de quelques pixels d'une période à l'autre.
Ouvrez Activité ▸ Planning et repérez la flèche › à droite du libellé de la semaine.
Cliquez-la plusieurs fois de suite, en observant sa position à l'écran — sur au moins une dizaine de semaines, pour traverser un changement de mois.
✅ Résultat attendu : la flèche reste exactement à la même place, quel que soit le libellé affiché entre les deux flèches.
Basculez sur Mois, puis répétez le clic une dizaine de fois.
✅ Résultat attendu : même constat, la flèche ne bouge pas — y compris entre un nom de mois court (« Mai ») et un long (« Septembre »).
Couverture automatique : e2e/planning.spec.ts → « la flèche « Période suivante » ne bouge pas d'une semaine à l'autre » clique la flèche plusieurs fois en vue semaine puis en vue mois, et vérifie sa position (boundingBox) après chaque clic.
Scénarios sur données réelles
Aucune manipulation à demander : lire la base est le comportement par défaut de l'écran. Si vous venez de jouer les scénarios précédents, remettez VITE_ENABLE_MOCK=false et rechargez avec Ctrl + Maj + R.
Prérequis communs à cette section : mode réel, nouvelle cohorte décrite dans les prérequis. Connectez-vous comme Léa (l.martins) pour FAM/MAS, Mathieu (m.rousseau) pour MAS/IME, ou Admin (i.aubert) pour la vue complète. Camille est visible des deux managers. Les planifications couvrent déjà les deux années ; aucune affectation préalable n'est nécessaire. Les essais manuels sont conservés quand la seed est rejouée.
13. Enregistrer la modulation d'une journée, et la voir changer la case
Tickets : APAJH-240 · APAJH-251 · Rôle : encadrant · Prérequis : ceux de la section — Camille sur Journées 35 h, une semaine habituelle sans férié ni absence, par exemple celle du 2 février 2026
Cette écriture est celle du formulaire de journée. Une modulation ne détruit pas la trame : elle prend le dessus sur la journée qu'elle nomme, et rien d'autre.
Elle n'oblige pas non plus à retoucher les horaires. Une journée peut être modulée uniquement pour dire où elle se passe ou comment — les étapes 7 à 9 le vérifient, et c'est l'objet d'APAJH-251.
Ouvrez Activité ▸ Planning et repérez la ligne du salarié que vous avez planifié.
✅ Résultat attendu : sa semaine suit le cycle de la trame — Journée 08:00–12:00 puis 13:00–16:00 du lundi au vendredi, puis
Rsamedi etRHdimanche. Le lieu est FAM lundi, mardi et jeudi, IME mercredi et vendredi. La colonne Total affiche 35h.Cliquez le bloc du vendredi, puis Modifier la journée. Sans rien toucher, regardez Enregistrer.
✅ Résultat attendu : le bouton est grisé et inactif, et le panneau affiche « Modifiez au moins un élément de la journée. » Il ne s'agit pas d'un bouton inerte : il n'y a simplement rien à enregistrer. Dépliez Plus d'options, cochez Télétravail puis décochez-le : le bouton s'active, puis se regrise — la journée est revenue là où vous l'avez trouvée.
Dans la carte, remplacez les horaires par 08:00 → 12:00, retirez la pause, choisissez le motif Urgence, puis cliquez Enregistrer.
✅ Résultat attendu : le bouton se grise le temps de l'envoi, le panneau se ferme, et un bandeau vert « Poste enregistré » apparaît en bas de l'écran puis disparaît de lui-même.
Regardez la case du vendredi et la colonne Total.
✅ Résultat attendu : la case a changé — elle affiche maintenant 08:00–12:00 au lieu de 08:00–16:00. Elle garde le libellé Journée et la couleur du poste sous-jacent : une modulation qui ne choisit aucun poste emprunte l'identité de la journée qu'elle remplace. Le Total de la ligne tombe de 35h à 32h.
Rouvrez la même case, portez le créneau à 08:00 → 14:00, puis Enregistrer.
✅ Résultat attendu : l'enregistrement passe — il corrige la modulation existante au lieu d'en créer une seconde. La case affiche 08:00–14:00 et le total remonte à 34h. Aucun message d'erreur : c'est la différence avec le mode démonstration, où la grille ignore la modulation déjà en base et où un second envoi se heurte à l'unicité.
Ouvrez le back-office (
/admin), puis Planning ▸ Modifications de planning.✅ Résultat attendu : une seule ligne pour ce salarié, à la date du vendredi, motif Urgence, colonne Créneaux à 1. Ouvrez-la : le créneau 08:00–14:00 y est, et le poste de trame d'origine n'a pas bougé — supprimez la modulation et la case reprend ses 08:00–16:00, sa pause déduite et son lieu secondaire IME après le prochain replay de seed.
Revenez au planning. Cliquez le bloc du mardi — une journée que vous n'avez pas encore touchée — puis Modifier la journée. Sans toucher aux horaires, dépliez Plus d'options et changez seulement Établissement / Service pour un autre site, puis Enregistrer.
✅ Résultat attendu : l'enregistrement passe. Les horaires n'ont pas bougé — la case affiche toujours 08:00–16:00, et le Total de la ligne est identique. Ce qui a changé : le petit libellé sous l'horaire nomme le nouveau site, la case prend le contour en pointillé et un ⟳. C'est le cas d'APAJH-251 : moduler une journée sans toucher aux heures.
Rouvrez cette même case, puis Modifier la journée.
✅ Résultat attendu : le formulaire rouvre sur ce qui a été enregistré — le motif choisi, le site choisi, le commentaire s'il y en avait un. Cochez Télétravail et enregistrez : le reste est conservé, et la case gagne le pictogramme maison. Corriger un seul champ n'efface pas les autres.
Rouvrez-la une dernière fois, décochez Télétravail et remettez Établissement / Service sur le site d'origine du salarié, puis Enregistrer.
✅ Résultat attendu : le bandeau vert affiche « Poste rétabli » et non « Poste enregistré ». La case perd son pointillé et ses pictogrammes : la journée est redevenue celle de la trame. Une modulation qui ne dit plus rien n'est pas conservée — vérifiez dans Planning ▸ Modifications de planning que sa ligne a bien disparu, et non qu'elle y subsiste vide.
Couverture automatique : e2e/planning.spec.ts vérifie la charge utile envoyée (créneaux, motif, identifiant d'établissement), la création contre la correction, l'enregistrement d'un changement de lieu qui garde les heures de la trame — y compris sur un écran de téléphone —, la réouverture du formulaire sur la modulation qu'il corrige, la suppression d'une modulation vidée, le bouton inerte pendant l'envoi, la fermeture du panneau au succès et l'affichage d'un refus du serveur — le tout par interception, sans serveur. Ce qui reste à regarder ici est précisément ce qu'elle ne peut pas voir : l'aller-retour réel, la case qui change et le total qui suit.
14. Lire les cycles réels et les anomalies isolées
Tickets : APAJH-240 · Rôle : Admin ou les deux encadrants · Prérequis : ceux de la section, référence du 7 octobre 2026
Ouvrez Activité ▸ Planning en Admin, sans filtre, sur une semaine habituelle telle que le 2 février 2026.
✅ Les six bandeaux Encadrement/Accompagnement sont visibles ; les onze personnes ont un planning. Camille apparaît dans Accompagnement du FAM et de l'IME. Les managers apparaissent dans leurs deux équipes Encadrement.
Consultez Hugo sur une nuit et Inès sur une journée coupée.
✅ La nuit 21:00–07:00 compte 10 h avec sa pause rémunérée. Le cycle alterne 40 h et 30 h. Les deux créneaux d'Inès, 06:00–10:00 puis 16:00–19:00, comptent 7 h et 13 h d'amplitude. Le moteur applique les règles actives à ces journées, notamment celles des pauses et du repos.
Ouvrez la semaine du 5 octobre 2026, puis le panneau Alertes.
✅ Les quatre anomalies sont signalées : Camille 46 h > 44 h CCN-66, Karim avec 8 h de repos et Inès avec 14 h d'amplitude, en avertissement ; Malik 37 h > 35 h hors CCN-66, en blocage. Les semaines voisines ne portent pas ces quatre anomalies. D'autres règles actives peuvent produire leurs propres alertes.
Rejouez sur la semaine historique du 10 mars 2025.
✅ Les mêmes quatre constructions et alertes sont présentes. La fin de 2025 et le début de 2026 gardent le roulement sans reprise arbitraire en semaine 1.
Connectez-vous comme Léa, puis comme Mathieu ; revenez sur octobre.
✅ Léa voit FAM/MAS, Mathieu MAS/IME. Camille est visible des deux côtés, Karim est exclu du périmètre de Mathieu et Inès de celui de Léa. Chaque lecteur reçoit seulement les alertes de son périmètre.
Couverture automatique : backend/tests/test_devtools_seeds.py couvre les périmètres par l'API, la couverture, les pauses, nuits, fériés et les résultats du calcul d'alertes. La lecture visuelle reste à jouer sur bureau et téléphone.
15. Restreindre le périmètre de l'encadrant à ses établissements
Tickets : APAJH-243 · Rôle : encadrant, plus un accès au back-office pour préparer le rôle · Prérequis : ceux de la section
Ce que l'écran affiche dépend du périmètre du lecteur : les services de ses établissements, et eux seuls. Ce périmètre est donné par ses rôles d'encadrement — un rôle « Salarié », que chaque compte possède et qui ne porte jamais d'établissement, n'ouvre plus rien.
Ouvrez Activité ▸ Planning sans filtre. Notez la liste des bandeaux et l'établissement annoncé par l'un d'eux, disons É1.
✅ Résultat attendu : les services de toute l'association sont là. Le compte ne porte aucun rôle d'encadrement restreint : « vide = toute l'association ».
Ouvrez le back-office (
/admin), puis Rôles ▸ Rôles. Ouvrez la ligne du rôle Manager n1 de votre compte et renseignez Établissements avec É1 seul. Enregistrez. Ne touchez pas au rôle « Salarié » du même compte.✅ Résultat attendu : le rôle est enregistré avec un établissement.
Revenez sur Activité ▸ Planning et rechargez avec Ctrl + Maj + R.
✅ Résultat attendu : il ne reste que les bandeaux des services rattachés à É1 ; ceux des autres établissements ont disparu, et avec eux leurs lignes de salariés. Le rôle « Salarié » du compte, sans établissement, n'élargit rien : c'est précisément ce que ce ticket corrige — il ouvrait auparavant toute l'association et le périmètre du rôle Manager restait sans effet.
Retournez sur le rôle et ajoutez un second établissement, É2. Rechargez l'écran.
✅ Résultat attendu : les bandeaux des deux établissements sont là. Un service transverse, rattaché à la fois à É1 et à É2, apparaît une seule fois : un établissement partagé suffit à le rendre visible, il ne le dédouble pas.
Videz la liste Établissements du rôle, enregistrez, puis rechargez.
✅ Résultat attendu : tous les bandeaux sont revenus. Un rôle d'encadrement sans établissement vaut pour toute l'association — c'est la convention du modèle, et elle ne change pas.
Couverture automatique : backend/tests/test_roles.py et backend/tests/test_planning_calendar.py clouent la règle côté serveur, y compris le cas à quatre établissements du ticket. Rien côté end-to-end : le jeu de démonstration ne connaît ni compte, ni rôle, donc le périmètre ne s'observe qu'ici.
16. Voir une absence déclarée prendre la case, et ce qu'elle vaut en heures
Tickets : APAJH-190 · Rôle : encadrant, plus un accès au back-office pour déclarer l'absence · Prérequis : ceux de la section, plus un type d'absence existant (scénario 1 du chapitre Référentiel des absences) et le lieu de travail principal du salarié planifié renseigné sur un établissement en Jours ouvrables (lundi au samedi)
Trois règles se vérifient ici d'un coup :
- une absence prime sur la trame et sur la modulation — c'est un fait sur la personne, les deux autres sont des plans sur la journée ;
- mais seulement sur les demi-journées qu'elle couvre : une demi-journée laisse le poste prévu à côté d'elle, amputé de ce qu'elle recouvre (depuis APAJH-291, scénario 38) ;
- elle vaut le temps que la journée devait être travaillée — la journée modifiée s'il y en a une, sinon le poste de la trame — sans proratisation selon l'ETP, mais n'ajoute rien au Total, qui ne compte que les heures travaillées (APAJH-289, scénario 39).
Sur la trame Journées 35 h, Camille est planifié sur un poste de 7 h : une journée d'absence retire donc 7 h au Total, et une demi-journée 3 h 30. Adaptez les chiffres à la durée du poste de votre salarié.
Notez d'abord le Total de sa ligne sur la semaine en cours, avant toute absence.
✅ Résultat attendu : c'est ce chiffre qui sert de repère aux étapes suivantes. Sur une semaine habituelle de Camille sans férié ni absence, il vaut 35h.
Ouvrez le back-office (
/admin), Absences ▸ Absences, Ajouter absence. Choisissez le salarié planifié et votre type d'absence, Premier jour au jeudi de la semaine en cours, Dernier jour au dimanche de la même semaine. Laissez les deux demi-journées à leur valeur. Enregistrez.✅ Résultat attendu : la ligne est enregistrée.
Revenez sur Activité ▸ Planning, semaine en cours, et rechargez avec Ctrl + Maj + R.
✅ Résultat attendu : les cases du jeudi, du vendredi et du samedi portent le code du type d'absence à la place du poste, sans horaires et dans la teinte « absence » de la légende. La case du dimanche garde le repos de la trame : le dimanche n'est pas un jour ouvrable, l'absence ne le décompte pas, donc elle ne le peint pas.
Regardez la colonne Total de la ligne.
✅ Résultat attendu : le Total a perdu les 7h de chacun des postes du jeudi et du vendredi, soit 14h de moins qu'à l'étape 1 : une absence n'ajoute rien au Total, qui ne compte que les heures travaillées. Le samedi, que la trame met en repos, porte bien le badge et ne change rien : rien n'y était dû.
Cliquez la case du jeudi.
✅ Résultat attendu : le panneau de détail affiche Statut avec le libellé du type — ni horaires, ni pause. Le bouton dit « Modifier l'absence » et ouvre le parcours de déclaration pré-rempli sur cette absence, où il se corrige et se supprime — Absences § 11, étapes 20 à 24.
Retournez au back-office, ouvrez l'absence et passez Jusqu'à à
Matin, c'est-à-dire au samedi matin. Rechargez la grille.✅ Résultat attendu : la case du samedi affiche le libellé suivi de (matin). Comme la trame n'y prévoyait rien, elle vaut toujours 0h et aucun poste ne l'accompagne.
Créez une seconde absence sur le mardi, À partir de
Matinet Jusqu'àMatin— une demi-journée. Rechargez la grille.✅ Résultat attendu : la case du mardi porte deux blocs empilés — l'absence « (matin) » et le poste de la trame, avec ses horaires d'origine. Sa durée, elle, ne compte plus que la moitié du poste, 3h30 : le salarié a travaillé l'autre moitié, et le matin est celui de l'absence (APAJH-291, APAJH-289). Le Total de la ligne a donc encore perdu 3h30 par rapport à l'étape 4.
Cliquez le mardi.
✅ Résultat attendu : le créneau du poste affiche une Durée de 3h30 et, en dessous, Neutralisé par l'absence 3h30, ce que le matin lui a retiré. Les deux additionnés redonnent la durée du poste. Rien n'est à moduler pour que le Total soit juste.
Supprimez les deux absences au back-office, puis rechargez la grille.
✅ Résultat attendu : toutes les cases reprennent ce que la trame et les modulations disaient, le poste du mardi avec sa durée entière. Une absence n'efface rien, et la réduction de l'étape 7 n'était qu'un calcul.
Couverture automatique : backend/tests/planning_absences/test_planning_absence.py (21 assertions) cloue la précédence, le dimanche et le férié rendus à la trame, la base de décompte lue sur l'établissement, la valeur d'une journée (le poste planifié, y compris pour un temps partiel), l'invariant « une semaine entièrement posée vaut ce que la semaine planifiait et ne travaille rien », le poste conservé à côté d'une demi-journée — et réduit à ce qu'elle laisse —, et le fait que ces heures n'entrent jamais dans les règles de durée du travail. Rien côté end-to-end : la suite tourne sur le jeu de démonstration, qui n'interroge pas l'API — ce qui reste à regarder ici est justement l'aller-retour réel.
17. Ajouter un créneau à une journée, dans le formulaire de la journée
Tickets : APAJH-244 · Rôle : encadrant · Prérequis : ceux de la section — Camille sur Journées 35 h, une semaine habituelle sans férié ni absence
Depuis APAJH-291, « Ajouter un créneau » n'est plus une porte à part du détail : c'est un bouton du formulaire Modifier la journée, sous la dernière carte. Les deux portes d'avant écrivaient la même journée en n'en montrant chacune qu'une partie.
Cliquez le bloc du lundi du salarié planifié.
✅ Résultat attendu : le panneau de détail s'ouvre avec, sous « Déclarer une absence », un seul bouton pour la journée — Modifier la journée. Il n'y a plus de bouton Ajouter un créneau dans le détail.
Cliquez Modifier la journée, puis + Ajouter un créneau.
✅ Résultat attendu : sous la carte Créneau 1, qui porte le poste du jour, une carte Créneau 2 apparaît. Elle commence à l'heure où finit la première, reprend son motif, et attend son heure de fin : tant qu'elle manque, Enregistrer reste éteint et le panneau dit « Complétez les horaires de chaque créneau, ou retirez-le. »
Dans le champ Poste de la carte 2, choisissez un poste de la bibliothèque (ex. « Soir », puis ajustez son début à 16:00).
✅ Résultat attendu : les heures de la carte 2 se remplissent avec celles de ce poste ; la carte 1 ne bouge pas.
Cliquez Enregistrer.
✅ Résultat attendu : le panneau se ferme, un bandeau « Poste enregistré » apparaît, et la case du lundi affiche désormais deux blocs empilés — le poste du jour, inchangé, et le nouveau.
Ouvrez le back-office (
/admin), Planning ▸ Modifications de planning, ouvrez la ligne du lundi.✅ Résultat attendu : deux créneaux — le premier reprend les horaires du poste du jour (avec son propre poste si la bibliothèque le propose), le second porte le poste choisi à l'étape 3.
Couverture automatique : e2e/planning.spec.ts → « un poste choisi pour un second créneau part avec sa propre clé » vérifie la charge utile envoyée (chaque créneau porte son propre poste), par interception, sans serveur — la même limite que le scénario 13 : l'aller-retour réel et l'affichage des deux blocs empilés se vérifient ici. Côté serveur, la capacité existait déjà et est couverte par backend/tests/test_planning_override.py (test_a_manager_ creates_a_modulation_with_its_creneaux, test_a_split_modulation_serves_ one_slot_per_creneau).
18. Une journée déjà coupée s'ouvre en entier, chaque créneau sur sa carte
Tickets : APAJH-244 · APAJH-291 · Rôle : encadrant · Prérequis : ceux de la section
L'historique : « Modifier le poste » s'ouvrait d'abord vide sur une journée coupée, et l'enregistrer effaçait le second créneau ; il a ensuite édité le seul créneau cliqué en faisant voyager l'autre sans le montrer. C'est ce créneau invisible qui faisait compter 35 h au lieu de 28 h (scénario 34). Depuis APAJH-291, le clic désigne la journée : quel que soit le bloc cliqué, le détail et le formulaire montrent tous ses créneaux.
Rejouez le scénario 17 jusqu'à l'étape 4 (le lundi porte deux créneaux, un « Matin » et l'« Après-midi » ajouté).
Cliquez le bloc Après-midi du lundi, puis le bloc Matin.
✅ Résultat attendu : les deux clics ouvrent le même détail de la journée, qui liste Créneau 1 (Matin) puis Créneau 2 (Après-midi), chacun avec ses horaires, sa durée et son établissement, et une ligne Total de la journée.
Cliquez Modifier la journée.
✅ Résultat attendu : deux cartes, dans l'ordre de la journée, chacune avec ses horaires et sa poubelle.
Modifiez uniquement les heures de la carte 2 (ex.
14:00→18:00), puis Enregistrer.✅ Résultat attendu : la case affiche toujours deux blocs — le Matin inchangé, l'Après-midi avec ses nouvelles heures.
Couverture automatique : e2e/planning.spec.ts → « chaque bloc d'une journée coupée ouvre la même journée » et « une journée coupée s'édite en entier, chaque créneau avec son contexte » rejouent les étapes 2 à 4 sur une journée coupée par la trame elle-même (Karim Mokrani, en mode démonstration), en bureau et en mobile, par interception de la charge utile — l'aller-retour réel se rejoue ici à la main.
19. L'établissement et les options d'un créneau ajouté ne touchent pas celles du créneau déjà en place
Tickets : APAJH-244 · Rôle : encadrant · Prérequis : ceux de la section
Le bug d'origine : établissement, motif, options (télétravail, hors établissement, temps de trajet inclus) et commentaire vivaient sur la journée (ShiftOverride), partagés par tous ses créneaux — en régler un sur le créneau qu'on ajoutait réécrivait donc silencieusement celui du créneau déjà là. Chaque ShiftOverrideSlot porte désormais ses propres colonnes, et chaque carte du formulaire les siennes.
Cliquez un bloc travaillé d'un salarié planifié, notez son établissement affiché dans le panneau de détail (ex. « FAM Les Cigales »).
Cliquez Modifier la journée, puis + Ajouter un créneau. Renseignez les horaires de la carte 2, dépliez son Plus d'options, choisissez dans Établissement / Service un site différent (ex. « MAS Glycines »), cochez Télétravail, et Enregistrer.
✅ Résultat attendu : le panneau se ferme, la case affiche désormais deux blocs.
Cliquez de nouveau l'un des blocs du jour.
✅ Résultat attendu : dans le détail de la journée, Créneau 1 porte toujours l'établissement noté à l'étape 1, sans l'option télétravail ; Créneau 2 porte le site choisi et l'option.
Ouvrez le back-office (
/admin), Planning ▸ Modifications de planning, ouvrez la ligne du jour.✅ Résultat attendu : deux créneaux, chacun avec son propre établissement et sa propre case « télétravail » — celle du premier décochée, celle du second cochée.
Couverture automatique : backend/tests/test_planning_override.py → test_a_split_modulations_creneaux_keep_their_own_establishment_and_options vérifie la résolution de deux créneaux aux établissement/motif/options/ commentaire distincts, sur une journée construite directement en base ; côté écran, e2e/planning.spec.ts → « « Enregistrer » crée la modulation de la journée » vérifie que chaque carte envoie son propre contexte. L'aller-retour complet se rejoue ici à la main.
20. Modifier un poste passé : l'encadrant remonte jusqu'au 1er décembre de l'année précédente
Tickets : APAJH-129 · Rôle : encadrant (Admin, RH n1, RH n2, DA, Manager n1, Manager n2), puis Service Paie · Prérequis : ceux de la section
Un encadrant modifie un poste — une modulation ou une absence — sur toute l'année en cours jusqu'à aujourd'hui, plus décembre de l'année précédente : décembre est le mois dont la paie se clôt en janvier, le dernier qu'une correction peut encore atteindre. Avant, c'est refusé. Service Paie garde n = 0 : aujourd'hui, pas hier. L'affectation à une trame aussi, pour tout le monde — elle n'est pas un poste. Le back-office reste le seul endroit où corriger une erreur plus ancienne que la fenêtre.
En Manager n1, naviguez le planning sur la semaine précédente (flèche ◂ de la barre d'outils), cliquez un bloc travaillé d'un jour déjà passé, puis Modifier la journée. Changez le motif et cliquez Enregistrer.
✅ Résultat attendu : l'enregistrement réussit — la semaine dernière est dans la fenêtre.
Remontez jusqu'à la semaine du 1er décembre de l'année précédente et refaites l'étape 1 sur le 1er décembre, puis sur le 30 novembre.
✅ Résultat attendu : le 1er décembre s'enregistre ; le 30 novembre est refusé, le panneau reste ouvert et affiche « Impossible de modifier une date antérieure au 01/12/AAAA. ».
Sur un jour de la semaine dernière, Déclarer une absence (un congé d'une journée), enregistrez, puis supprimez-la.
✅ Résultat attendu : les deux réussissent — la même fenêtre vaut pour les absences.
Reconnectez-vous en Service Paie et refaites l'étape 1 sur un jour de la semaine dernière, puis sur aujourd'hui.
✅ Résultat attendu : hier est refusé avec « Impossible de modifier une date déjà passée. » ; aujourd'hui s'enregistre normalement.
Ouvrez le back-office (
/admin), Planning ▸ Modifications de planning, ouvrez une ligne antérieure au 1er décembre de l'année précédente, changez un champ, enregistrez.✅ Résultat attendu : l'enregistrement réussit — le back-office reste le point de correction d'une erreur de saisie hors fenêtre.
Couverture automatique : backend/tests/test_planning_override.py et backend/tests/absences/test_absences_declaration.py → leur section « Retroactive writes » (hier et le 1er décembre acceptés pour un encadrant, le 30 novembre refusé en création, correction et suppression, le déplacement d'une ligne hors fenêtre vers le futur refusé, Service Paie à n = 0, la permission portée par les six rôles seulement, et le modèle lui-même qui n'objecte pas — la garantie que le back-office reste exempté). backend/tests/test_planning.py garde n = 0 pour l'affectation à une trame.
21. Afficher, actualiser et supprimer des évènements dans la grille
Tickets : APAJH-103 · APAJH-143 · Rôle : encadrant · Prérequis : ceux de la section, plus au moins deux salariés visibles dans le planning de la semaine courante
Ce scénario est la procédure de validation avant création de la PR. Il se joue avec VITE_ENABLE_MOCK=false : les évènements et le planning doivent tous deux refléter la base. Gardez la même semaine pendant toute la recette.
Dans Manager ▸ Planning ▸ Évènements, créez les éléments suivants sur la semaine affichée :
- une formation à la journée, sur deux jours consécutifs, avec deux participants ;
- une réunion horaire avec ces participants mais sans établissement, par exemple 09:00–10:30 ;
- un évènement horaire sans participant, rattaché au premier établissement, par exemple 14:00–15:00 ;
- un évènement à la journée sur deux jours sans participant et sans établissement ;
- un évènement horaire sans participant rattaché à un second établissement.
Notez les titres, le lieu, le responsable et le type saisis.
Ouvrez Activité ▸ Planning, placez-vous sur la semaine concernée et repérez la ligne du premier participant.
✅ Résultat attendu : son poste, son repos ou son absence reste visible. Les la réunion horaire et la formation nominatives sont ajoutées sous le contenu normal de la case, sous forme de pastilles chaudes avec une icône calendrier, leur plage horaire et leur titre. La formation apparaît les deux jours sans remplacer la case. Les trois évènements sans participant ne sont répétés dans aucune case salarié.
Si ce salarié appartient à deux équipes, comparez ses deux lignes. Sinon, ajoutez-le temporairement à une seconde équipe depuis le back-office et rechargez.
✅ Résultat attendu : les mêmes pastilles apparaissent dans chacune de ses lignes. Elles ne sont ni perdues ni comptées deux fois dans l'en-tête. Les évènements sans participant restent uniquement dans l'en-tête.
Cliquez une pastille horaire, puis cliquez à côté d'elle sur le bloc du poste.
✅ Résultat attendu : la pastille ouvre à droite DÉTAIL DE L’ÉVÈNEMENT, avec titre, horaire, lieu, responsable et type, sans action Modifier ou Supprimer. Le bloc du poste ouvre toujours DÉTAIL DE LA JOURNÉE : les deux zones de clic sont indépendantes.
Dans l'en-tête du premier jour, cliquez la bulle calendrier et son compteur.
✅ Résultat attendu : le compteur porte le nombre d'évènements du jour distincts. Il comprend la formation nominative à la journée et tous les évènements sans participant, horaires compris ; la réunion horaire nominative n'y entre pas. Le panneau indique la plage horaire ou « Journée entière » ; un clic ouvre le détail et Retour revient à la liste.
Ouvrez le bouton flottant +.
✅ Résultat attendu : Déclarer une absence et Modifier un poste sont toujours présents. Fermez le menu, puis vérifiez à nouveau qu'un clic sur le poste fonctionne dans une case qui porte aussi un évènement.
Filtrez d'abord sur le premier établissement, puis ajoutez un filtre équipe qui masque tous les participants de la formation. Remplacez ensuite le filtre établissement par le second.
✅ Résultat attendu : seules les lignes encore visibles portent leurs évènements. Dès qu'aucun participant n'est visible, les évènements nominatifs disparaissent aussi du compteur. L'évènement collectif du premier établissement ignore le filtre équipe mais disparaît lorsque seul le second établissement est choisi. Celui du second apparaît alors. L'évènement collectif sans établissement reste visible sous toutes ces combinaisons.
Passez en vue Mois.
✅ Résultat attendu : chaque case compacte regroupe les évènements derrière une icône calendrier et un nombre. Le clic ouvre la liste du salarié et du jour, sans agrandir toutes les lignes du mois.
Revenez dans Évènements, modifiez le titre ou l'horaire de la réunion, puis revenez au planning. Répétez en laissant le planning ouvert dans un onglet et en lui redonnant le focus.
✅ Résultat attendu : la valeur modifiée apparaît sans donnée périmée. Le planning recharge les évènements au montage, au changement de période et au retour de focus ; il ne lance pas de rafraîchissement périodique.
Supprimez l'évènement collectif horaire du premier établissement depuis Évènements, puis revenez au planning ou redonnez-lui le focus.
✅ Résultat attendu : il disparaît du compteur, tandis que le poste, les deux jours de formation et l'évènement transversal restent affichés.
Répétez l'ouverture du regroupement mensuel avec une fenêtre de 390 × 844 et l'adresse directe
/planning.✅ Résultat attendu : le détail monte depuis le bas de l'écran. La page ne déborde pas horizontalement ; seule la grille défile dans sa boîte.
Couverture automatique : e2e/planning.spec.ts couvre les deux formes temporelles, le multi-jour, plusieurs évènements dans une case, le compteur distinct, la diffusion nominative, collective rattachée et collective transversale, les filtres établissement et équipe, la duplication d'une ligne multi-équipe, la coexistence avec le poste et le bouton flottant, le panneau de lecture seule, le regroupement mensuel, le panneau mobile, l'erreur non bloquante avec Réessayer, ainsi que le rafraîchissement après édition et suppression. Les droits et le chevauchement de l'API sont couverts par backend/tests/test_events.py.
22. Restituer les créations, corrections et suppressions d'astreintes
Tickets : APAJH-104 · APAJH-105 · APAJH-107 · Rôle : Manager n1 · Prérequis : mode API, un salarié au contrat éligible et une équipe visible dans un établissement couvert.
Critères d'acceptation du ticket
- Les périodes enregistrées dans Astreinte sont répercutées dans le planning.
- Une nuit commencée la veille apparaît encore au début de la période consultée.
- Le badge et les contacts ne remplacent ni poste, ni repos, ni absence.
Enregistrez une astreinte de 19 h à 7 h sur un seul jour depuis Activité ▸ Astreinte.
Ouvrez une période du planning commençant le lendemain de ce jour.
Corrigez puis supprimez l'astreinte ; revenez au planning après chaque enregistrement.
Répétez avec un salarié extérieur à l'équipe mais couvrant son établissement.
✅ La nuit est présente jusqu'à sa borne de fin. Le contact extérieur apparaît dans le bandeau de l'équipe couverte sans devenir membre de cette équipe. Les corrections et suppressions actualisent la restitution, les autres états restent présents.
Couverture automatique : tests/test_on_call.py vérifie la borne du lendemain, les contacts extérieurs et les données persistées ; e2e/astreintes.spec.ts couvre la sauvegarde et l'actualisation de la liste. La restitution visuelle dans le planning réel se rejoue en mode API, les specs habituelles du planning utilisant des données simulées.
23. Filtrer les équipes et leurs contacts d'astreinte par établissement
Tickets : APAJH-104 · APAJH-107 · Rôle : Manager n1 · Prérequis : deux équipes rattachées à un établissement A, une équipe de B et une astreinte couvrant A. Le salarié d'astreinte peut travailler habituellement dans B.
Ouvrez le planning sur les dates de l'astreinte et filtrez sur A.
Vérifiez les bandeaux des deux équipes de A, puis ajoutez B au filtre.
Ouvrez le filtre équipe : seules les équipes rattachées à A ou B sont proposées.
Sélectionnez une équipe de B, puis retirez B du filtre établissement.
Décochez tous les établissements. Répétez sur téléphone.
✅ Chaque équipe de A affiche le contact d'astreinte de A, même si ce salarié n'est pas membre de l'équipe. Les équipes de B n'apparaissent qu'après sélection de B et ne reçoivent pas le contact de A, sauf si elles sont également rattachées à A. Le lieu de travail principal des salariés ne fait pas apparaître une équipe d'un autre établissement. Retirer B efface aussi son équipe du filtre si elle n'est pas rattachée à A ; les équipes encore compatibles restent sélectionnées. Une équipe partagée n'est proposée qu'une fois. Sans filtre établissement, toutes les équipes de la période sont proposées. Si aucune n'est disponible, le sélecteur désactivé l'indique. Les membres et les périodes enregistrées restent inchangés.
Couverture automatique : tests/test_on_call.py vérifie les contacts dans plusieurs équipes, dont une équipe partagée, sans doublon ni appartenance ajoutée. tests/test_planning_calendar.py vérifie les rattachements complets transmis par l'API. e2e/planning.spec.ts → « filtre les équipes par leurs établissements et conserve leurs contacts d’astreinte », sur bureau et mobile.
24. Arriver sur le planning par le lien d'une demande d'absence
Tickets : APAJH-42, APAJH-256 · Rôle : encadrant · Prérequis : ceux de la section, plus une demande en attente dans la file (voir Absences § 13, étape 1). Le geste de départ est le scénario 16 du même chapitre ; celui-ci recette ce que la grille en fait.
Depuis
/demandes, cliquez « Voir au planning › » sur une demande dont le premier jour n'est pas un lundi.✅ Résultat attendu : la grille ouvre la semaine qui contient ce jour — un mercredi ouvre sur son lundi. C'est une semaine qu'on lit, pas une date qu'on pointe.
Regardez la ligne du salarié.
✅ Résultat attendu : sa case d'identité, dans la colonne gelée, est teintée et porte un filet vertical à gauche. Dans chacune de ses équipes s'il est membre de plusieurs : la mise en évidence suit la personne, et une ligne est un couple (bande, salarié). Seule la colonne gelée est teintée — c'est elle qui dit qui, et c'est la seule qui reste à l'écran quand la grille défile latéralement.
Regardez le panneau ouvert à droite.
✅ Résultat attendu : « Demande à arbitrer », l'identité du salarié, « Prévu initialement » avec le poste que la journée devait être, le récapitulatif « Demandé » avec l'effet compteur chiffré par le serveur, « Ajuster avant validation » et le pied « Refuser » / « Valider ». C'est exactement le panneau de la file — même composant, même demande. C'est la moitié du geste qui manquait : le panneau est modal, donc on ne peut pas « aller regarder la semaine sans lâcher la décision », et revenir à
/demandesperdrait ses filtres de la file. La décision voyage donc avec le contexte.Cliquez dans la grille, derrière le panneau.
✅ Résultat attendu : aucune journée ne s'ouvre — le voile prend le clic et referme l'arbitrage. Les deux panneaux de l'écran, celui d'une journée et celui d'une demande, ne s'ouvrent jamais ensemble ; c'est le voile qui le garantit, pas une mécanique ajoutée pour ça.
Regardez l'URL après cette fermeture.
✅ Résultat attendu :
absencea disparu,dateetemployeesont toujours là. Les deux derniers décrivent un état — quelle semaine, qui — et restent donc partageables et rechargeables ;absenceétait une commande, et elle a été consommée. La laisser rouvrirait le panneau à chaque rechargement, sur une demande que quelqu'un a peut-être tranchée entre-temps.Cliquez la flèche « Période suivante », puis rechargez la page (F5).
✅ Résultat attendu : la grille change vraiment de semaine — le paramètre ouvre la vue, il ne la retient pas — et la ligne reste détachée : elle nomme une personne, et une personne reste celle qu'on regarde quelle que soit la semaine affichée. Le rechargement ramène la semaine de la demande, sans rouvrir le panneau, l'URL ne le nommant plus. La contrepartie à connaître : après la flèche, l'URL nomme encore la semaine d'origine et non celle qui est à l'écran.
Refaites le trajet depuis
/demandeset cliquez « Valider ».✅ Résultat attendu : le bandeau « Demande validée », le panneau se ferme,
absencequitte l'URL, la semaine et la ligne détachée restent — et la case du jour a changé : l'absence accordée prend la journée. Trancher n'est pas quitter l'écran.Refaites le trajet une troisième fois, sur cette demande désormais validée (état « Validées » de la file, où le lien n'existe plus — ouvrez donc
/planning?date=<1er jour>&employee=<n°>&absence=<n° de la demande>à la main).✅ Résultat attendu : la semaine s'ouvre et la ligne est détachée, sans panneau. Une demande tranchée n'a plus rien à arbitrer, et le pied répondrait de toute façon « déjà tranchée par quelqu'un d'autre » — mais la semaine et la personne, elles, valent encore le voyage.
Reconnectez-vous avec un compte DA et rouvrez le lien d'une demande en attente.
✅ Résultat attendu : le panneau s'ouvre et n'a pas de pied. Pas un bouton grisé, pas d'infobulle : lire une demande et la trancher sont deux droits, et un DA n'a que le premier.
Ouvrez à la main
/planning?date=2019-01-07&employee=<n° du salarié>— une semaine que le périmètre ne sert pas — puis/planning?date=<1er jour>&employee=<n°>&absence=999999.✅ Résultat attendu : dans les deux cas la grille ouvre la semaine demandée, tout simplement. Le salarié n'a pas de ligne sur une semaine qu'il ne travaille pas, et le numéro de demande inventé n'ouvre aucun panneau : le serveur refuse une demande hors périmètre tout seul. Pas de message d'erreur dans un cas ni dans l'autre — l'écran a déjà son état vide, et en fabriquer un second serait un message que personne ne lit.
Ouvrez
/planning?date=n-importe-quoi.✅ Résultat attendu : la semaine courante, aucune ligne détachée. Un lien n'est pas un formulaire — il n'y a personne à qui rendre l'erreur, et la bonne semaine est à un clic.
Couverture automatique : e2e/planning.spec.ts vérifie la semaine ouverte depuis un mercredi, la ligne détachée dans chacune des équipes du salarié, la flèche de période qui navigue en gardant la mise en évidence, la date illisible qui ouvre la semaine courante, puis l'arbitrage lui-même : le panneau rouvert avec sa mesure, la validation et la fermeture qui consomment ?absence= sans quitter la semaine, une demande déjà tranchée qui n'ouvre rien, un refus de lecture qui laisse la grille intacte, un identifiant illisible qu'on ne demande même pas, le pied absent sans le droit de trancher, et le voile qui rend les deux panneaux exclusifs. Le poste « prévu initialement » y est vérifié à l'arrivée par le lien ; ce que l'encart dit dans les autres cas est le scénario 17 d'Absences.
25. Une modulation qui ne touche pas aux horaires garde les deux postes d'une journée coupée
Tickets : #111 · Rôle : encadrant, plus un accès au back-office · Prérequis : ceux de la section, mais dans l'ordre de l'étape 1 — la trame se coupe avant qu'on planifie
Le bug : une journée que la trame coupe en deux — un matin et un soir sur la même case, Trames § 9 — perdait son second poste dès qu'on lui posait une modulation sans horaires (télétravail, hors établissement, autre site). La case n'affichait plus qu'un bloc, le total de la ligne perdait les heures du soir, et les alertes CCN-66 raisonnaient sur une journée amputée. Rien n'était perdu en base : supprimer la modulation rendait ses deux postes à la journée. Une modulation qui, elle, réécrit les horaires n'est pas concernée — celle-là remplace la journée entière, c'est sa raison d'être.
Consultez Inès Fabre, déjà planifiée sur Journées coupées 35 h, un lundi habituel sans férié ni absence. Pour fabriquer un autre cas, copiez une trame, ajoutez un second poste selon Trames § 9, puis affectez cette copie à une fiche de recette supplémentaire.
L'ordre n'est pas un détail : éditer une trame sur laquelle quelqu'un est déjà planifié en archive le cycle d'avant et repointe sa planification dessus, pour qu'elle continue de dire ce que le salarié travaillait vraiment. Fait dans l'autre sens, il garderait donc sa journée à un seul poste.
✅ Résultat attendu : sur
/planning, le lundi du salarié affiche deux blocs, l'un sous l'autre.Notez le total d'heures de sa ligne et les alertes de la semaine.
Ouvrez le back-office (
/admin), Planning ▸ Modifications de planning, Ajouter : le salarié, ce lundi, un motif, cochez Télétravail, et aucun créneau. Enregistrez.Par le back-office, parce que c'est d'abord de là que cette ligne venait. L'écran la produit aussi depuis APAJH-291 : sur une journée dont les cartes reprennent exactement la trame, Modifier la journée n'écrit que les options, à condition qu'elles soient les mêmes sur tous les créneaux — cochez Télétravail sur les deux cartes. Cochée sur une seule, la journée est enregistrée en créneaux propres et ne suit plus la trame.
Revenez sur
/planninget rechargez la semaine.✅ Résultat attendu : la case affiche toujours deux blocs, aux horaires que la trame leur donnait, tous deux en pointillé et portant le picto télétravail — la modulation décore la journée entière, pas sa première moitié. Le total de la ligne et les alertes de la semaine sont ceux notés à l'étape 2. Avant ce correctif, seul le bloc du matin subsistait.
Cliquez le bloc du soir.
✅ Résultat attendu : le panneau porte le motif et l'option télétravail de la modulation, et aucun encart « Prévu initialement » — cette modulation n'a remplacé aucun horaire, donc rien n'a été prévu à la place (scénario 7).
Couverture automatique : backend/tests/test_planning_override.py → test_a_modulation_with_no_creneau_keeps_both_postes_of_a_split_day pour les deux blocs et leur décoration, et test_a_modulation_with_no_creneau_leaves_a_split_days_alerts_where_they_were pour l'amplitude et le repos quotidien que le poste disparu emportait. La ligne est construite directement en base, comme le back-office l'écrit ; l'aller-retour par les deux écrans se rejoue ici à la main.
26. Voir une demande en attente garder son poste, et la trancher depuis la case
Tickets : APAJH-255, APAJH-273 · Rôle : encadrant · Prérequis : ceux de la section, sur Camille Annualisation — la seed lui laisse deux jours de Congés annuels en attente sur des jours planifiés. Repérez leurs dates dans la file Absences, ou ouvrez directement Voir au planning. Avec la référence du 7 octobre 2026, la demande commence le 19 octobre.
Une demande d'absence non tranchée ne prend pas la journée. Tant que personne n'a décidé, la case reste celle qui doit être travaillée — le contraire annoncerait un congé que personne n'a accordé — et c'est le cerclage orange qui dit qu'une question est posée dessus. Le scénario 24 arrivait sur l'arbitrage par un lien depuis la file ; celui-ci y arrive par la grille.
Placez-vous sur la semaine de la demande, et regardez la ligne de Camille Annualisation.
✅ Résultat attendu : ses deux jours demandés affichent toujours le poste que sa trame leur donne, horaires compris, et portent en plus un cerclage orange — la pastille « Demande en attente » de la légende. Rien n'est remplacé : les journées sont celles du planning, entourées. Si l'un de ces jours est un repos dans le cycle, il est cerclé lui aussi : la demande couvre la journée, que la trame y ait prévu du travail ou non.
Survolez l'une de ces cases sans cliquer.
✅ Résultat attendu : l'infobulle nomme le poste, ses horaires, puis le motif Congés annuels demandé. La légende dit qu'une demande existe, l'infobulle dit laquelle.
Notez la colonne Total de la ligne, puis comparez-la à celle de la semaine précédente.
✅ Résultat attendu : le total de la semaine contestée est amputé des journées cerclées. Le Total annonce ce qui est acquis, et une journée que personne n'a tranchée n'en fait pas encore partie. Les alertes CCN-66, elles, continuent de la compter : le poste reste planifié, et un repos quotidien trop court reste un fait à signaler quoi qu'il advienne de la demande.
Cliquez l'une des cases cerclées.
✅ Résultat attendu : le panneau « Demande à arbitrer » s'ouvre — le même qu'au scénario 24, sur la même demande — et l'URL gagne
?absence=<n°>. Ce n'est pas le panneau de la journée : quelqu'un qui clique une case cerclée demande à trancher, et le poste derrière n'a rien à corriger tant que ce n'est pas fait. Le retour du navigateur ramène la grille sans le panneau.Cliquez Valider.
✅ Résultat attendu : le bandeau « Demande validée », le panneau se ferme, et les cases changent : l'absence accordée prend les journées, cerclage compris, et le Total se met à la valoriser en heures. C'est le basculement que ce lot introduit — avant la décision une question, après une décision un fait.
Reconnectez-vous avec un compte salarié (sans le droit de trancher) et ouvrez le planning sur une demande encore en attente — celle d'Émilie Chen, que la seed laisse sur une demi-journée d'après-midi.
✅ Résultat attendu : la case est cerclée de la même façon, mais le clic ouvre le panneau « Détail de la journée », comme sur n'importe quelle autre case, et l'URL ne gagne rien. C'est la lecture juste : il voit le poste qu'on attend encore de lui, et décider n'est pas à lui.
Passez en vue Mois.
✅ Résultat attendu : le cerclage subsiste, comme le pointillé du poste modifié. Une journée à la fois modulée et contestée porte les deux — l'un n'efface pas l'autre.
Sur un congé accordé dont le salarié a demandé l'annulation (scénario 26 d'Absences), regardez sa case, survolez-la, puis cliquez-la.
✅ Résultat attendu : le congé est toujours dessiné — demander n'est pas annuler — et porte le même cerclage orange ; l'infobulle dit « Congés annuels · Annulation demandée ». Le clic ouvre « Annulation à arbitrer », et l'URL gagne
?absence=<n°>: arriver par ce lien depuis la file rouvre le même panneau. Sur une demi-journée, seul le bloc de l'absence est cerclé.
Couverture automatique : e2e/planning.spec.ts vérifie le poste conservé et son infobulle, le clic qui ouvre l'arbitrage en écrivant ?absence=, en bureau comme en mobile, le total amputé de la journée contestée, et le détail de poste ouvert par un compte sans le droit de trancher — et pour une annulation demandée (APAJH-273), le congé cerclé dont le clic ouvre « Annulation à arbitrer » aux deux densités, et le lien qui rouvre une annulation. Côté serveur, tests/planning_absences/test_planning_absence.py vérifie qu'une demande en attente ne remplace rien tout en marquant la journée, qu'une demande validée la remplace toujours, et que les règles CCN-66 continuent de compter la journée contestée.
27. Savoir ce qu'une absence accordée a pris à la journée
Tickets : APAJH-256 · Rôle : encadrant · Prérequis : ceux de la section, plus une absence validée sur une journée que la trame faisait travailler — le scénario 11 d'Absences en pose une.
Une absence accordée remplace le poste dans la case : la journée ne montre plus ce qu'elle devait être. L'encart « Prévu initialement » du panneau est le seul endroit où cela se lit encore.
Sur la grille, cliquez la case grise d'une absence validée posée sur une journée travaillée.
✅ Résultat attendu : le panneau « Détail de la journée » s'ouvre, et sous l'identité une bande grise « Prévu initialement » portant le poste que la journée devait être — « JM · Matin · 07:00–14:30 », teinté de la couleur de sa catégorie. C'est le même encart, au même endroit, que sur une journée modulée (scénario 7) et que dans le panneau d'arbitrage.
Faites de même sur une journée qui avait été modulée avant d'être absente.
✅ Résultat attendu : l'encart porte les horaires de la modulation, pas ceux de la trame en dessous. Ce sont ces heures-là que le salarié aurait travaillées ; la trame n'était déjà plus ce qui devait se passer.
Cliquez une absence d'une demi-journée.
✅ Résultat attendu : pas d'encart. Le poste restant est dessiné juste à côté de l'absence, encore dû et encore à moduler — le nommer comme abandonné dans la même case dirait le contraire.
Cliquez une absence posée sur une journée où la trame ne prévoyait rien (case blanche, ou salarié sans trame en cours).
✅ Résultat attendu : pas d'encart non plus. Rien n'a été pris.
Rétrécissez à la largeur d'un téléphone et rejouez l'étape 1.
✅ Résultat attendu : même encart, les puces passant à la ligne sous leur libellé plutôt que de déborder du panneau.
Couverture automatique : e2e/planning.spec.ts vérifie l'encart sur la case d'une absence accordée, en bureau comme en mobile, et son absence sur une journée dont la rotation était déjà l'absence. Côté serveur, tests/planning_absences/test_planning_absence.py couvre les cinq cas : journée simple, journée modulée, journée coupée (les deux postes), demi-journée (rien) et journée sans rien de prévu (rien).
28. Affecter un poste sur une journée que la trame ne planifie pas
Tickets : APAJH-267 · Rôle : encadrant · Prérequis : ceux de la section, et une journée libre à viser : utilisez une fiche de recette supplémentaire sans planification. Les cycles de la cohorte couvrent tous les jours, repos compris. Planification permet aussi de démarrer la planification de cette fiche en milieu de semaine et de viser les jours précédents, restés vides.
Jusqu'ici une case vide était un cul-de-sac : on ne pouvait ajouter un poste que là où il y en avait déjà un, et affecter quelqu'un sur une journée libre passait par le back-office.
Survolez une case vide de la ligne du salarié.
✅ Résultat attendu : un cadre pointillé avec un + apparaît sous le pointeur, et seulement là. Les autres cases vides restent nues : un mois de trente salariés couvert de + se lirait comme autant de manques.
Cliquez-la.
✅ Résultat attendu : le panneau s'ouvre directement sur le formulaire, titré Affecter un poste — pas de détail derrière, donc pas de lien Retour. L'identité du salarié et la date sont en tête, puis une carte Créneau 1 vide. Il n'y a pas de récapitulatif « Prévu initialement » : rien n'était prévu, et rien n'est encore saisi.
Sans rien saisir, survolez Enregistrer.
✅ Résultat attendu : le bouton est éteint et la raison s'affiche dans le panneau, au-dessus des boutons : « Saisissez au moins un créneau complet. » Dépliez Plus d'options et cochez Télétravail : il reste éteint. Une journée sans heures dirait où elle se passe et jamais quand — contrairement à une journée travaillée, où déplacer le lieu suffit à faire une modulation.
(Une infobulle n'aurait rien dit : un bouton désactivé ne reçoit pas le survol. Le même changement vaut pour « Modifier la journée », scénario 13.)
Dans la carte, saisissez 09:00 et 17:00, puis Enregistrer.
✅ Résultat attendu : le toast « Poste enregistré », le panneau se ferme, et la case porte désormais le poste — avec le contour pointillé des journées modulées, puisque c'en est une. Le Total de la ligne augmente d'autant.
Rechargez la page (F5).
✅ Résultat attendu : la journée est toujours là. Elle a été écrite, pas affichée.
Rouvrez la journée que vous venez d'affecter.
✅ Résultat attendu : c'est désormais une journée modulée ordinaire. Le panneau s'ouvre sur son détail et non sur le formulaire, le bouton dit Modifier la journée, et le formulaire rouvre sur les heures saisies. La case n'est plus vide, donc la porte pointillée a disparu. Défaire l'affectation n'est pas possible depuis l'écran — voir la fin du chapitre.
Recommencez l'étape 2 sur une date antérieure au 1er décembre de l'année précédente, et enregistrez.
✅ Résultat attendu : le serveur refuse, et le panneau affiche « Impossible de modifier une date antérieure au 01/12/AAAA. » sans se fermer — la même règle que le scénario 20.
Couverture automatique : e2e/planning.spec.ts vérifie l'ouverture du formulaire, le bouton éteint sans créneau et la charge utile envoyée, en bureau comme en mobile ; côté serveur, tests/test_planning_override.py vérifie qu'une création sur un jour sans trame est acceptée et que la journée se relit. Ce qui reste à regarder ici est ce qu'elles ne voient pas : que le + n'apparaisse qu'au survol, la couleur du contour pointillé, et le total de la ligne.
29. Donner un poste à pourvoir à un salarié libre, sur le jour cliqué
Tickets : APAJH-267 · APAJH-129 · Rôle : encadrant · Prérequis : ceux de la section
Le pendant du scénario précédent, par l'autre porte : la ligne Poste à pourvoir qui clôt chaque unité. Le jour vient de la case cliquée, le salarié du champ de recherche.
Cliquez une case de la ligne Poste à pourvoir d'une unité — pas son en-tête (scénario 6), une case de jour.
✅ Résultat attendu : le panneau Ajouter un poste s'ouvre, portant l'unité, son établissement et la date cliquée.
Ouvrez Rechercher un salarié… sans rien taper.
✅ Résultat attendu : la liste ne propose que les salariés qui n'ont rien ce jour-là. Quelqu'un qui travaille déjà n'est pas disponible : lui donner le poste réécrirait sa journée sans le dire. Un repos et une absence comptent comme pris, au même titre qu'un poste. Si tout le monde travaille, la liste affiche « Aucun salarié disponible ». Les intérimaires y portent leur étiquette, comme au scénario 6.
Elle puise dans les salariés du périmètre de votre rôle, équipes affichées ou non — et c'est le serveur qui répond de la journée de chacun, y compris pour un salarié dont aucune équipe de l'écran ne parle et dont la grille, elle, ne sait rien. Si cette lecture échoue, la liste reste vide et le panneau l'écrit en rouge : « Les disponibilités de ce jour n'ont pas pu être lues. » Mieux vaut ne proposer personne que quelqu'un dont on ignore s'il travaille déjà.
Choisissez un nom, saisissez un créneau 14:00–18:00, puis Enregistrer.
✅ Résultat attendu : deux enregistrements, dans cet ordre — le salarié rejoint l'unité (bandeau « Salarié ajouté à l'équipe »), puis la journée part (« Poste enregistré »). Le poste apparaît donc sur la ligne de ce salarié dans ce bandeau, à la date cliquée, et la ligne Poste à pourvoir reste vide : elle est dessinée par le front et n'a rien à enregistrer pour elle-même. L'ordre compte : sans l'affectation, la journée irait sur le seul bandeau d'origine du salarié et la ligne où on vient de la saisir disparaîtrait au rechargement.
Rouvrez la même case de la ligne Poste à pourvoir, et rouvrez la liste.
✅ Résultat attendu : le salarié que vous venez d'affecter n'y est plus — il n'est plus libre ce jour-là.
Revenez sur une semaine déjà passée (flèche ◂), cliquez une case de la ligne Poste à pourvoir, donnez-la à un salarié qui n'est pas de l'unité, saisissez un créneau, Enregistrer, puis rechargez la page (F5).
✅ Résultat attendu : le panneau annonce « Le salarié choisi rejoint l'équipe … à partir du JJ/MM/AAAA », la date cliquée et non aujourd'hui. Après le rechargement, sa ligne et sa journée sont toujours dans ce bandeau, sur cette semaine passée : l'arrivée dans l'équipe part du jour du poste, sans quoi la journée tomberait avant l'affectation et quitterait le bandeau. Les autres membres de l'unité gardent leurs dates. Au-delà du 1er décembre de l'année précédente, l'enregistrement est refusé (scénario 20).
Couverture automatique : e2e/planning.spec.ts vérifie que la liste se réduit aux salariés libres du jour, que l'affectation part la première — la journée attend qu'elle soit passée —, qu'elle part du jour cliqué quand il est passé, et que l'enregistrement part sur ce salarié-là, à la date cliquée. tests/test_employee.py (« adding one member to a division ») vérifie qu'une arrivée part d'aujourd'hui par défaut ou d'un jour passé dans la fenêtre, qu'elle est refusée au-delà et qu'elle laisse le reste de l'équipe intact. Côté serveur, tests/test_planning_calendar.py vérifie que la disponibilité d'un jour ignore le périmètre de celui qui la demande — c'est sa raison d'être — qu'un repos y compte comme pris et qu'un compte salarié ne peut pas la lire.
30. Voir la semaine d'un agent polyvalent plafonnée à 35 h
Tickets : APAJH-199 · Rôle : encadrant · Prérequis : ceux de la section, Malik Nasri étant déjà planifié. Utilisez la semaine du 5 octobre 2026 avec la référence du 7 octobre 2026
Le cycle habituel de Malik fait 35 h. La modulation du samedi ajoute 2 h : 37 h reste sous les 44 h de la CCN-66 mais dépasse le seuil de son contrat. Cette construction est répétée le 10 mars 2025 et reste isolée.
Ouvrez Activité ▸ Planning sur la semaine du 5 octobre 2026 et repérez la ligne de Malik Nasri.
✅ Résultat attendu : son Total — 37h — est en rouge. Survolé, il indique « Durée hebdomadaire supérieure à 35 h (hors CCN-66) ».
Ouvrez le panneau Alertes.
✅ Résultat attendu : une alerte bloquante nommant Malik Nasri, datée du jour où la semaine passe 35 h : « Durée hebdo 37h > 35h (hors CCN-66) ».
Sur une fiche de recette supplémentaire en CDI, planifiez Journées 35 h sur cette semaine et ajoutez une modulation de 2 h le samedi.
✅ Résultat attendu : son total de 37h reste noir et aucune alerte hebdomadaire ne le nomme : il a jusqu'à 44 h.
Couverture automatique : e2e/planning.spec.ts → « un agent polyvalent est signalé dès 35 h, hors CCN-66 », bureau et mobile, sur le jeu de démonstration (l'étape 1 et le témoin de l'étape 3). Côté serveur, backend/tests/test_outside_collective_agreement.py vérifie l'alerte bloquante à 35 h et son absence sur la même semaine sous CCN-66. Voir aussi le scénario 14 d'Annuaire Utilisateurs pour la création du contrat.
31. Refuser une affectation sans contrat à la date requise
Tickets : GitHub #136 · Rôle : encadrant · Prérequis : un salarié sorti sans journée planifiée, un futur embauché sans équipe et une équipe visible.
Critères d'acceptation
- La disponibilité exige un contrat au jour demandé, bornes incluses.
- Un nouveau rattachement exige un contrat aujourd'hui.
- Une création ou correction de poste sans contrat au jour du poste est refusée.
- Ouvrez une case Poste à pourvoir après la fin du contrat du salarié sorti. ✅ Le salarié sorti n'est pas proposé.
- Ouvrez une case future couverte par le contrat du futur embauché, dont le contrat n'a pas encore commencé aujourd'hui. Choisissez-le, saisissez les horaires et enregistrez. ✅ Le rattachement est refusé avec la date concernée ; aucun poste n'est créé et le panneau reste ouvert.
- Dans Bibliothèque ▸ Équipes, conservez ou retirez un ancien membre dont le contrat est terminé. ✅ La modification reste possible : seuls les nouveaux rattachements sont contrôlés.
Couverture automatique : e2e/planning-contracts.api.spec.ts vérifie l'application de la disponibilité servie par l'API, l'arrêt après un refus et le parcours mobile. Les règles de contrat et l'absence de mutations après refus sont vérifiées dans test_planning_calendar.py, test_planning_override.py et test_employee.py.
32. Choisir un poste personnel encore absent du planning
Tickets : GitHub #137 · Rôle : encadrant · Prérequis : une équipe visible et un salarié disponible sous contrat.
Critères d'acceptation
- Les sélecteurs proposent les postes personnels non archivés, indépendamment des trames et de la période.
- Les postes déjà planifiés restent visibles, même archivés ou appartenant à un autre manager.
- Une erreur de bibliothèque reste visible et permet de réessayer ou de saisir les horaires manuellement.
- Créez un poste dans Bibliothèque ▸ Postes, sans l'utiliser dans une trame.
- Ouvrez Activité ▸ Planning, puis une case Poste à pourvoir et choisissez un salarié.
- Cherchez le nouveau poste, sélectionnez-le et enregistrez. ✅ Les horaires sont renseignés et le poste est affecté avec sa propre identité.
- Ouvrez Modifier la journée sur une journée existante, puis + Ajouter un créneau. ✅ Chaque carte propose la même bibliothèque personnelle ; les postes archivés et ceux d'autres managers restent exclus des choix.
- Consultez une journée utilisant un poste archivé ou appartenant à un autre manager. ✅ Son détail reste lisible ; modifier un autre créneau de la journée ne l'efface pas.
Couverture automatique : e2e/planning.spec.ts couvre le choix et l'enregistrement d'un poste personnel absent du planning, les archives, les homonymes, les états chargement/vide/erreur et les créneaux multiples, sur bureau et mobile. La création préalable dans la bibliothèque est couverte par e2e/postes.spec.ts.
33. Limiter une trame aux périodes sous contrat sans décaler son cycle
Tickets : GitHub #136 · Rôle : encadrant · Prérequis : un salarié affecté à une trame de deux semaines aux horaires distincts, dont la période dépasse celle du contrat.
Critères d'acceptation
- Les postes et repos issus de la trame n'apparaissent que pendant un contrat, début et fin inclus.
- Une arrivée, un renouvellement ou une interruption ne modifie pas la position dans le cycle.
- Les contrats simultanés ne dupliquent pas les journées ; les alertes suivent les seules journées produites.
- Affectez par anticipation le salarié à une trame démarrant le lundi 21/09, avec un contrat commençant le vendredi 25/09. ✅ Les 21 au 24 restent vides ; le 25 reprend le vendredi de la semaine de cycle choisie. Le lundi 28 passe à la semaine suivante.
- Renseignez une fin de CDD en milieu de semaine et rechargez le planning. ✅ Le dernier jour du contrat reste planifié ; les journées suivantes ne portent plus les postes ou repos de la trame.
- Prolongez le contrat, puis rechargez. ✅ Les journées réapparaissent jusqu'à la nouvelle fin, sans recréer l'affectation.
- Ajoutez un contrat ultérieur laissant deux jours d'interruption, puis un contrat chevauchant une période déjà couverte. ✅ L'interruption reste vide, la reprise respecte le cycle initial et aucun poste n'est doublé.
Couverture automatique : test_planning_calendar.py vérifie les bornes, l'absence de contrat, les arrivées en cours de semaine, les prolongations, interruptions et chevauchements sur le calendrier et le planning prévu. e2e/planning-contracts.api.spec.ts vérifie sur bureau et mobile l'affichage des journées servies par l'API autour d'une embauche et d'une fin de CDD.
34. Ajouter un créneau puis modifier le créneau initial : le total reste juste
Tickets : APAJH-291 · Rôle : encadrant · Prérequis : un salarié planifié quatre jours d'une semaine sur un poste de 7 h sans pause, soit 28h au Total de sa ligne.
Critères d'acceptation
- Un créneau ajouté puis abandonné par Retour n'est jamais enregistré.
- La modification du créneau initial ne change que lui, et le Total de la ligne reste celui de la journée réellement enregistrée.
- Retirer un créneau d'une journée est pris en compte par les compteurs.
Cliquez le premier jour du salarié, puis Modifier la journée et + Ajouter un créneau. Saisissez 15:00 → 22:00 dans la carte 2, sans enregistrer.
✅ Résultat attendu : rien n'est encore écrit.
Cliquez Retour, puis de nouveau Modifier la journée. Décalez le créneau d'une heure : 08:00 → 15:00.
✅ Résultat attendu : le formulaire rouvre sur la journée telle qu'elle est enregistrée, une seule carte. Le créneau 15:00 → 22:00 n'y est plus.
Cliquez Enregistrer.
✅ Résultat attendu : toast « Poste enregistré », la case affiche 08:00–15:00 seul, et le Total de la ligne vaut toujours 28h — et non 35h, le chiffre que donnait le créneau abandonné, enregistré en silence avec la modification.
Ouvrez Mes compteurs du salarié (ou son annualisation au back-office).
✅ Résultat attendu : l'annualisation compte la journée pour 7 h. Une modulation qui retire un créneau recalcule la journée comme toute autre écriture ; la preuve côté serveur est dans la couverture d'Annualisation.
Couverture automatique : e2e/planning-modulation.api.spec.ts rejoue le parcours, sur bureau et sur mobile, contre une API qui sert la journée telle que la dernière modulation l'a écrite : la charge du PATCH ne porte pas le créneau abandonné, et le Total de la ligne reste à 28h.
35. Poser une pause sur un créneau modulé : la durée de la journée la déduit
Tickets : APAJH-291 · Rôle : administrateur (back-office) puis encadrant · Prérequis : un salarié planifié sur une semaine en cours, et un compte qui ouvre le back-office.
Critères d'acceptation
- Un créneau modulé porte une pause, rémunérée ou non.
- Une pause non rémunérée est déduite de la durée de la journée ; une pause rémunérée ne l'est pas.
- Une pause qui ne tient pas dans son créneau est refusée.
Au back-office, ouvrez Modifications de planning, puis Ajouter. Choisissez le salarié et un jour de la semaine en cours, un motif, et dans Créneaux modulés un créneau 08:30 → 17:30. Renseignez la pause 12:30 → 13:30, décochez Pause rémunérée, puis enregistrez.
✅ Résultat attendu : la modification est enregistrée, avec son créneau et sa pause.
Dans Activité ▸ Planning, cliquez la case de ce jour.
✅ Résultat attendu : le détail du jour affiche 08:30–17:30, une Durée de 8h et une ligne Pause 1 h non rémunérée, 12:30–13:30. Le Total de la ligne compte 8 h pour cette journée, et l'infobulle de la case nomme la même pause.
Revenez au back-office, cochez Pause rémunérée sur le créneau et enregistrez. Rechargez le planning et cliquez de nouveau la case.
✅ Résultat attendu : la Durée vaut 9h ; la ligne dit désormais Pause 1 h rémunérée.
Au back-office, déplacez la pause à 18:00 → 18:30 et enregistrez.
✅ Résultat attendu : l'enregistrement est refusé, sous le créneau : « La pause doit tenir entre l'heure de début et l'heure de fin. »
Le planning, lui, garde cette pause quand il réenregistre la journée par Modifier la journée, et n'y saisit qu'une pause rémunérée : le scénario 37 le recette.
Couverture automatique : backend/tests/test_planning_override.py (durée nette, pause rémunérée, pause hors du créneau, pause de nuit après minuit) et backend/tests/test_annualization_projection.py (une journée modulée qui recopie la pause de son poste pèse dans l'annualisation ce que pesait la journée de la trame).
36. Passer une journée travaillée en repos ponctuel
Tickets : APAJH-291 · Rôle : administrateur (back-office) puis encadrant · Prérequis : un salarié planifié sur une semaine à venir par une trame qui le fait travailler le lundi, et un compte qui ouvre le back-office.
Critères d'acceptation
- Une journée que la trame fait travailler peut devenir un repos, compté 0 h.
- Le détail du jour dit que ce repos a été décidé hors trame, avec son motif, son commentaire et ce que la trame avait prévu.
- Un repos ponctuel ne porte aucun créneau.
- Supprimer la modification rend la journée de la trame, compteurs compris.
Au back-office, ouvrez Modifications de planning, puis Ajouter. Choisissez le salarié et le lundi de la semaine à venir, un motif, cochez Repos ponctuel, saisissez un commentaire, et ajoutez dans Créneaux modulés un créneau 09:00 → 17:00. Enregistrez.
✅ Résultat attendu : l'enregistrement est refusé : « Une journée en repos ponctuel ne porte aucun créneau — un repos doit être seul. »
Cochez Supprimer sur le créneau, puis enregistrez.
✅ Résultat attendu : la modification est enregistrée, sans créneau, et la liste la montre avec Repos ponctuel coché.
Dans Activité ▸ Planning, affichez la semaine à venir.
✅ Résultat attendu : le lundi du salarié affiche un Repos, entouré du pointillé « Poste modifié ». Le Total de la ligne ne compte plus les heures de ce lundi.
Cliquez la case.
✅ Résultat attendu : le détail du jour affiche Prévu initialement avec le poste de la trame, puis Statut « Repos (modifié) », le motif sous Poste modifié et le commentaire saisi.
Au back-office, supprimez la modification. Rechargez le planning.
✅ Résultat attendu : le lundi affiche de nouveau le poste de la trame, sans pointillé, et le Total de la ligne le compte. L'annualisation du salarié suit chaque étape.
Au back-office, recréez le repos ponctuel de l'étape 2 en cochant aussi Télétravail. Enregistrez.
✅ Résultat attendu : l'enregistrement est refusé : « Un repos ponctuel ne se déplace pas et ne se fait pas en télétravail : il ne porte ni établissement, ni option. » Décochez Télétravail : il passe. Le motif et le commentaire, eux, restent permis.
Le planning écrit aussi un repos ponctuel : retirer le dernier créneau d'une journée la passe en repos, après confirmation (scénario 37).
Couverture automatique : backend/tests/test_planning_override.py (bloc repos, prévu initialement, journée coupée, plafond hebdomadaire, refus avec créneaux, refus d'un établissement ou d'une option, retour à la trame), backend/tests/test_annualization_admin.py (dont le refus du télétravail au back-office) et backend/tests/test_annualization_updates.py (le back-office et l'API retirent la journée de l'annualisation et la rendent), backend/tests/planning_absences/test_planning_absence.py (une absence de la même semaine garde la valeur de son propre poste, une absence posée sur le repos ne vaut rien) et e2e/planning-modulation.api.spec.ts (la case et le détail, sur bureau et sur mobile).
37. Modifier toute une journée dans une seule fenêtre
Tickets : APAJH-291 · Rôle : encadrant · Prérequis : ceux de la section, et un salarié planifié sur une journée à venir par un poste de votre bibliothèque d'un seul créneau qui porte une pause (par exemple 08:30 → 17:30, pause 12:30 → 13:30, soit 9 h — une pause de poste est rémunérée depuis APAJH-300).
Critères d'acceptation
- Cliquer sur n'importe quel bloc d'une case ouvre le détail de la journée, qui liste tous ses créneaux, et ne propose que Déclarer une absence et Modifier la journée.
- Modifier la journée ouvre une seule fenêtre : une carte par créneau, un bouton + Ajouter un créneau, une poubelle par carte, un seul Enregistrer.
- On peut modifier le créneau initial et ajouter des créneaux en un seul enregistrement, supprimer un créneau ajouté, et supprimer le créneau de base quand d'autres restent.
- Chaque créneau garde la pause de son poste ; la carte ne saisit qu'une pause rémunérée, qui ne change pas le total. L'écart entre deux créneaux est une pause non rémunérée.
- Le récapitulatif compare, en heures nettes, la saisie au prévu initialement ; il coïncide avec le Total de la ligne.
- Retirer le dernier créneau passe la journée en repos ponctuel, après confirmation.
- Remettre sur les cartes les postes de la trame rend la journée à sa trame.
- L'infobulle d'un bloc nomme sa pause et dit si elle est rémunérée.
Survolez la case du salarié, puis cliquez-la.
✅ Résultat attendu : l'infobulle dit « … · 08:30–17:30 · Pause 1 h rémunérée ». Le panneau DÉTAIL DE LA JOURNÉE liste la journée, dont la ligne Pause 1 h rémunérée, puis deux boutons seulement : Déclarer une absence et Modifier la journée. Il n'y a plus de Rétablir le poste, même sur une journée déjà modifiée.
Cliquez Modifier la journée.
✅ Résultat attendu : une carte Créneau 1 aux horaires du jour, dont la Pause rémunérée 12:30 → 13:30 est modifiable. En pied, le récapitulatif : Prévu initialement 9h, Heures saisies 9h, Écart avec le prévu 0h.
Dans la carte 1, passez la fin à 12:30, puis cliquez + Ajouter un créneau et donnez à la carte 2 13:30 → 17:30.
✅ Résultat attendu : la pause a disparu de la carte 1, qui ne la contient plus. La carte 2 s'ouvre en commençant à 12:30, la fin de la carte 1, sous le même motif. Tant que son heure de fin manque, Enregistrer reste éteint et le panneau dit « Complétez les horaires de chaque créneau, ou retirez-le. » Complète, le récapitulatif dit Heures saisies 8h et un écart de −1h : l'heure entre les deux cartes n'est pas comptée.
Passez la fin de la carte 1 à 14:30 et le début de la carte 2 au même 14:30.
✅ Résultat attendu : aucune alerte dans le formulaire — les règles de temps de travail sont celles du serveur, lues sur le planning après l'enregistrement. Dans la carte 1, cliquez Ajouter une pause rémunérée et saisissez 11:00 → 11:20 : les Heures saisies ne bougent pas.
Ajoutez une carte 3, puis retirez-la par sa poubelle. Cliquez Enregistrer.
✅ Résultat attendu : toast « Poste enregistré » ; la case affiche deux blocs, et la carte 3 n'a laissé aucune trace. Le Total de la ligne compte ce que le récapitulatif disait, rien d'autre. L'infobulle du premier bloc dit « Pause 20 min rémunérée ».
Rouvrez la journée : cliquez le second bloc.
✅ Résultat attendu : le même détail que depuis le premier — Créneau 1 et Créneau 2, chacun avec ses horaires, sa durée et son établissement — et une ligne Total de la journée.
Modifier la journée, puis la poubelle de la carte 1. Enregistrez.
✅ Résultat attendu : le créneau de base est parti, l'après-midi reste seul dans la case.
Modifier la journée, puis la poubelle de la dernière carte.
✅ Résultat attendu : une fenêtre de confirmation dit que la journée sera déclarée comme un repos, et demande un Motif (prérempli avec celui de la carte) et un Commentaire. Annuler la ferme sans rien écrire : la carte est toujours là.
Recommencez, saisissez un commentaire et cliquez Confirmer.
✅ Résultat attendu : toast « Repos enregistré », le panneau se ferme et la case affiche un Repos en pointillé. Le Total de la ligne ne compte plus ce jour. Au back-office, la modification n'a ni établissement ni option, même si le créneau retiré était en télétravail.
Rouvrez la case, cliquez Affecter un poste — rien n'est travaillé sur un repos.
✅ Résultat attendu : pas de carte, mais l'encart « La journée est en repos » avec son motif et son commentaire ; le récapitulatif dit Prévu initialement 9h, Actuellement 0h, Heures saisies 0h, Écart avec le prévu −9h.
Cliquez + Ajouter un créneau, choisissez dans Poste le poste de la trame, sans rien changer d'autre, et enregistrez.
✅ Résultat attendu : toast « Poste rétabli » ; la case reprend le poste de la trame, sans pointillé, et le Total de la ligne avec lui. La modification a disparu du back-office : remettre la trame, c'est ne plus rien modifier.
Rouvrez la journée, Modifier la journée, dépliez Plus d'options, cochez Télétravail et enregistrez, sans toucher aux horaires.
✅ Résultat attendu : la case garde les horaires de la trame, prend le pointillé et le picto maison. Au back-office, la modification de ce jour n'a aucun créneau : la journée suit toujours sa trame, et une trame modifiée plus tard la changerait.
Couverture automatique : e2e/planning.spec.ts → la section « modifier toute la journée » (le même détail depuis chaque bloc, l'édition d'une journée coupée, l'ajout et le retrait de cartes en un seul enregistrement, la pause rémunérée qui ne change pas le total, en bureau et en mobile, avec une page qui ne déborde jamais de l'écran), « retirer le créneau de base garde les autres », « « Enregistrer » change le lieu seul, sans toucher aux heures » ; e2e/planning-modulation.api.spec.ts (un créneau abandonné par Retour ne part pas, la poubelle du dernier créneau et sa confirmation, le retour d'un repos à la trame, le repos levé par un créneau, la pause rémunérée nommée par l'infobulle, avec un Total qui suit) ; e2e/absences-queue.spec.ts (« Ajuster » garde la pause qui tient encore) ; backend/tests/test_planning_calendar.py et backend/tests/test_planning_override.py (la pause servie avec paid).
38. Voir une absence neutraliser les créneaux qu'elle recouvre
Tickets : APAJH-291 · Rôle : encadrant, plus un accès au back-office pour déclarer l'absence · Prérequis : ceux du scénario 16, un type d'absence en heures (par exemple « Heures de délégation ») en plus du type en jours, et une journée à venir du salarié planifié, qu'aucune absence ne couvre encore
Critères d'acceptation
- Une absence accordée sur une partie de la journée retire de chaque créneau ce qu'elle recouvre : ses heures pour une absence en heures, ce qui tombe avant ou après 12:00 pour une demi-journée, un créneau à cheval étant coupé à midi.
- Le Total vaut la valeur de l'absence plus ce qui reste des créneaux.
- Un créneau entièrement recouvert reste dessiné, atténué et barré, et ne compte plus rien.
- Supprimer l'absence rend la journée telle qu'elle était.
- Une demande en attente ne neutralise rien.
Depuis APAJH-289 (scénario 39), deux de ces critères ont changé : une demi-journée retire la moitié du temps travaillé de la journée — la première moitié pour un matin, la dernière pour un après-midi — et non plus ce qui tombe de part et d'autre de 12:00 ; et l'absence n'ajoute rien au Total, qui ne compte que les heures travaillées. Les résultats attendus ci-dessous suivent ces règles.
Sur Activité ▸ Planning, cliquez la journée choisie, Modifier la journée, et découpez-la en trois créneaux : 08:00 → 09:00, 10:00 → 12:30 et 13:30 → 17:30. Enregistrez, et notez le Total de la ligne.
✅ Résultat attendu : la journée vaut 7h30 (1 h + 2 h 30 + 4 h).
Au back-office, déclarez sur ce jour une absence du type en jours, À partir de
Matinet Jusqu'àMatin, au statut Validée. Rechargez la grille.✅ Résultat attendu : la case porte l'absence « (matin) » et les trois créneaux. 08:00–09:00 et 10:00–12:30 sont atténués et barrés ; leur infobulle finit par « Neutralisé par l'absence ». 13:30–17:30 affiche 3h45 : le matin a pris la première moitié des 7 h 30 de la journée, soit ces deux créneaux et le premier quart d'heure du troisième. Le Total de la ligne a perdu 3h45 par rapport à l'étape 1. La légende nomme le nouveau marquage.
Cliquez le créneau barré.
✅ Résultat attendu : le détail du jour liste les trois créneaux. Le premier dit Durée 0h et Neutralisé par l'absence 1h, le deuxième Durée 0h et Neutralisé par l'absence 2h30, le troisième Durée 3h45 et Neutralisé par l'absence 0h15. Le Total de la journée vaut 3h45.
Au back-office, remplacez l'absence par une absence en heures de 15:00 à 17:00 sur le même jour, validée. Rechargez.
✅ Résultat attendu : plus rien n'est barré. 13:30–17:30 affiche 2h, les deux autres créneaux leur durée entière, et le Total ne compte que les 5h30 de créneaux : les 2 h d'absence n'y entrent pas.
Passez cette absence au statut En attente. Rechargez.
✅ Résultat attendu : la case prend l'anneau orange de la demande, et les trois créneaux comptent leur durée entière : une demande n'est pas encore un fait.
Supprimez l'absence. Rechargez.
✅ Résultat attendu : la journée est revenue à l'étape 1 — 7 h 30, rien d'atténué.
Recommencez l'étape 4 sur un salarié dont la journée finit à 21:00 et reprend le lendemain à 07:00, avec une absence en heures de 17:00 à 21:00. Ouvrez Alertes.
✅ Résultat attendu : l'alerte « Repos quotidien 10h < 11h » de ce lendemain a disparu : la journée finit à 17:00, et laisse 14 h de repos.
Couverture automatique : backend/tests/planning_absences/test_planning_absence_neutralization.py (la coupure en heures et à midi, le créneau neutralisé, la journée entière, la demande en attente, la suppression de l'absence, l'annualisation coupée au même midi, et les trois règles CCN-66 mesurées sur ce qui reste) ; e2e/planning-modulation.api.spec.ts (« une demi-journée d'absence neutralise les créneaux du matin », Total, bloc barré et détail, en bureau et en mobile) ; e2e/absences-queue.spec.ts (« Ajuster » refaçonne le créneau que l'absence recoupe).
39. Voir une absence décompter tout le poste d'un temps partiel
Tickets : APAJH-289 · Rôle : encadrant, plus un accès au back-office pour déclarer l'absence · Prérequis : ceux du scénario 16, avec un salarié dont le contrat est à temps partiel (par exemple 17,5 h par semaine, soit 50 %) et que la trame planifie sur un poste de 7 h au moins trois jours de la semaine en cours.
Critères d'acceptation du ticket
- Un CT posé sur un poste de 7 h décompte 7 h, quel que soit l'ETP.
- Le comportement est le même pour les autres types d'absence.
- Aucune régression sur les compteurs d'annualisation.
Précisé avec le porteur pendant la revue : le Total de la ligne ne compte que les heures travaillées — une absence n'y ajoute rien —, et une demi-journée retire la moitié du temps que la journée devait être travaillée, la première moitié pour un matin, la dernière pour un après-midi.
Ouvrez Activité ▸ Planning sur la semaine en cours et notez le Total de la ligne du salarié.
✅ Résultat attendu : c'est le repère des étapes suivantes.
Au back-office, Absences ▸ Absences, Ajouter absence : le salarié, le type CT, une seule journée travaillée de la semaine, sans demi-journée. Enregistrez, puis rechargez la grille avec Ctrl + Maj + R.
✅ Résultat attendu : la case porte le code CT et le Total de la ligne a perdu 7h — la durée du poste que l'absence remplace, et non 3h30 (la moitié qu'aurait donnée l'ETP). L'absence elle-même n'ajoute rien au Total.
Créez une absence d'un autre type compté en jours (par exemple un congé payé) sur le matin d'un deuxième jour travaillé : À partir de
Matin, Jusqu'àMatin. Rechargez.✅ Résultat attendu : la case porte l'absence « (matin) » et le poste, dont la durée tombe à 3h30 — la moitié des 7 h, quels que soient ses horaires. Le Total a perdu 3h30 de plus.
Sur un troisième jour travaillé, ouvrez la case, Modifier la journée, ramenez le créneau à 4 h et enregistrez. Posez ensuite au back-office une absence d'une journée entière sur ce même jour, puis rechargez.
✅ Résultat attendu : le Total perd 4h — ce que la journée devait être travaillée avant d'être posée — et non les 7h du poste de la trame.
Ouvrez Annualisation pour ce salarié, sur le mois en cours.
✅ Résultat attendu : les trois jours posés y figurent avec leur code d'absence sur fond ambre, et le matin de l'étape 3 y garde 3h30 travaillées, comme sur la grille.
Supprimez au back-office les absences et la modification créées ici.
✅ Résultat attendu : la ligne retrouve le Total de l'étape 1.
Couverture automatique : backend/tests/planning_absences/test_planning_absence.py (le temps partiel sur un poste de 7 h, un autre motif en jours, la journée modulée, le repos ponctuel, la demi-journée coupée), backend/tests/test_counter_integration.py (le décompte en heures théoriques d'un temps partiel et d'une journée modulée : c'est là que l'absence vaut 7 h, non proratisée), et backend/tests/planning_absences/test_planning_absence_neutralization.py (la moitié du temps travaillé). Le reste est à rejouer à la main.
Ce que cette livraison ne couvre pas
Repris de la maquette et non livré. Ne l'ouvrez pas en anomalie :
- les écritures autres que la modulation d'une journée, l'affectation d'une journée libre et la déclaration d'une absence : publier, exporter ;
- retirer quelqu'un d'une équipe depuis le planning. L'écran sait ajouter (scénario 6) ; pour retirer, on passe par Bibliothèque ▸ Équipes ou par la fiche salarié, qui savent le faire — et les journées déjà saisies pour cette personne, elles, ne partent pas avec l'affectation ;
- choisir la date à laquelle l'affectation commence. Depuis l'en-tête de l'unité, c'est toujours aujourd'hui : l'en-tête ne nomme aucun jour, et ajouter quelqu'un en consultant une semaine passée ne le fait donc pas apparaître sur cette semaine-là (scénario 6, étape 12). Depuis une case passée, c'est le jour de la case (scénario 29, étape 5) ;
- défaire une affectation de journée que la trame ne planifie pas. Retirer son dernier créneau en fait un repos ponctuel (scénario 37), pas une case vide : la rendre vide reste un geste de back-office ;
- une équipe de nuit sous une demi-journée d'absence : la coupure est fixée à 12:00, et un créneau commencé la veille au soir n'est pas touché par le matin d'absence du lendemain (scénario 38) ;
- revenir à la trame sur une journée dont le poste de trame n'est pas dans votre bibliothèque — celui d'un autre encadrant. La carte ne peut le choisir, ni recopier sa pause non rémunérée : supprimer la modification au back-office reste alors le chemin ;
- l'export Excel / PDF ;
- la sélection multiple de cases, le choix d'une couleur par case, une bande synthétique d'évènements indépendante des lignes, l'ajout et la suppression de lignes. Les pastilles d'évènements affectés aux salariés sont livrées au scénario 21 ;
- décider un repos ponctuel depuis l'écran : il se pose au back-office et se lit au planning (scénario 36) ; le planning saura l'écrire, en vidant la journée, dans une livraison suivante du même ticket. Les absences, elles, sont servies depuis APAJH-190 et se déclarent, se corrigent et se suppriment depuis l'écran depuis APAJH-252 — Absences § 11. Ce qui reste au back-office, c'est la seule saisie que l'écran ne sait pas faire : une plage dont les deux bornes portent des demi-journées différentes, que le parcours rouvre alors en lecture seule ;
- la ligne de couverture en pied de grille et les jours fériés grisés : ils ne sont pas non plus dans la maquette ;
- les vues Absences et Planning réalisé, ainsi que Mon planning et Mon équipe de l'espace salarié. L'API « mes évènements » est prête, mais sa représentation dans Mon planning attend toujours la création de cet écran.
40. Le planning ne propose que le périmètre du rôle
Tickets : APAJH-298 · Rôle : Manager n1 (ou RH, Admin) dont le rôle est rattaché à quelques établissements seulement · Prérequis : make seed-all, mode démonstration désactivé, un salarié du périmètre sans équipe, et un salarié d'un établissement hors périmètre
Ouvrez Activité ▸ Planning, puis le filtre établissement.
✅ Résultat attendu : il ne propose que les établissements rattachés au rôle du compte — pas tout le référentiel. Les équipes de la grille sont celles de ces établissements.
Cliquez une case de la ligne Poste à pourvoir d'une unité, puis ouvrez Affecter à un salarié.
✅ Résultat attendu : le salarié du périmètre sans équipe est proposé (s'il est libre ce jour-là) ; le salarié de l'établissement hors périmètre ne l'est jamais, même libre.
Couverture automatique : e2e/planning-perimeter.api.spec.ts → « le planning ne propose que le périmètre du rôle » (bureau et mobile).
41. Un salarié sans équipe apparaît sous son établissement
Tickets : APAJH-280 · Rôle : Manager n1 (ou RH, Admin) · Prérequis : make seed-all, mode démonstration désactivé, un salarié sous contrat dont le lieu de travail principal est un établissement du périmètre, affecté à aucune équipe (par exemple créé depuis Utilisateurs ▸ Nouvel utilisateur sans rattachement)
Ouvrez Activité ▸ Planning sur la semaine en cours.
✅ Résultat attendu : après les bandeaux des équipes, un bandeau porte le nom de l'établissement et contient la ligne du salarié, avec ses journées s'il en a. Ce bandeau n'a pas de ligne « Poste à pourvoir » : il n'existe aucune équipe où affecter quelqu'un.
Cliquez une case vide de sa ligne.
✅ Résultat attendu : le panneau « Affecter un poste » s'ouvre comme pour n'importe quel salarié ; l'enregistrement écrit la journée sans l'affecter à une équipe, et la ligne reste dans le bandeau de l'établissement au rechargement.
Ajoutez au salarié un lieu de travail secondaire (fiche salarié ▸ Lieux de travail & équipes), dans un autre établissement du périmètre, puis rechargez le planning.
✅ Résultat attendu : il apparaît sous les deux établissements, avec les mêmes journées ; ses heures ne sont pas comptées deux fois dans les alertes.
Modifiez quatre journées consécutives de sa semaine prochaine — la première sur 07:00–21:00, les trois suivantes sur 07:00–18:00 — puis ouvrez le Tableau de bord.
✅ Résultat attendu : les alertes de planning du salarié (repos quotidien, plafond hebdomadaire) apparaissent au tableau de bord, comme dans la grille.
Depuis un compte RH ou Admin, affectez-le à une équipe d'un établissement hors du périmètre du manager, à partir de dans trois semaines. Revenez sur le compte manager et rouvrez le planning de la semaine prochaine, puis le Tableau de bord.
✅ Résultat attendu : la semaine prochaine, il reste dans le bandeau de son établissement ; ses alertes de l'étape 4 restent au tableau de bord. Les semaines où il est affecté ailleurs, il n'apparaît plus sur ce planning.
Repliez le bandeau de l'établissement, puis filtrez sur lui avec Filtrer par équipe.
✅ Résultat attendu : il se replie et se filtre comme une équipe, sans toucher une équipe voisine.
Retirez l'affectation de l'étape 5, puis affectez-le à une équipe à partir d'aujourd'hui (Bibliothèque ▸ Équipes, ou la ligne « Poste à pourvoir » d'une unité), puis rechargez.
✅ Résultat attendu : il quitte le bandeau de l'établissement et n'apparaît plus que dans celui de son équipe ; le bandeau de l'établissement disparaît s'il ne contenait que lui.
Un salarié sans aucun lieu de travail, sans contrat sur la période ou dont le compte est fermé n'apparaît nulle part : il n'a pas d'établissement sous lequel se ranger, ou personne à planifier. Un salarié sans équipe lit sa propre semaine dans ce bandeau, seul ; aucun écran salarié ne l'affiche encore, ce qui se vérifie par les tests automatiques.
Couverture automatique : e2e/planning-default-team.api.spec.ts → « un salarié sans équipe apparaît sous son établissement » (bureau et mobile), et backend/tests/test_planning_calendar.py (§ « The salariés posted to no team ») et backend/tests/test_dashboard.py → test_dashboard_alerts_include_salaries_posted_to_no_team, test_dashboard_alerts_keep_a_salarie_posted_outside_the_perimeter_later_on.
42. Signaler une modification d'horaires hors délai de prévenance
Tickets : APAJH-181 · Rôle : Manager n1 · Prérequis : un salarié à temps partiel (contrat de moins de 35 h) et un salarié à temps plein, tous deux planifiés sur les deux semaines qui viennent.
Critères d'acceptation
- Modifier les horaires d'une journée à moins de 7 jours ouvrés l'enregistre, et la confirmation signale le délai de modulation non respecté.
- Pour un salarié à temps partiel, à moins de 3 jours, la confirmation signale aussi son propre délai.
- Au-delà de 7 jours ouvrés, rien n'est signalé. Changer seulement le lieu, une option ou le commentaire n'est pas modifier les horaires.
Sur le planning, ouvrez Modifier la journée pour le salarié à temps partiel, demain. Changez l'heure de fin d'un créneau et cliquez sur Enregistrer.
✅ « Poste enregistré » est suivi de deux « Attention : Délai de prévenance non respecté… » : l'un pour le temps partiel (3 jours), l'autre pour la modulation (7 jours ouvrés), chacun avec la date de la journée. La modulation est bien enregistrée.
Rouvrez la même journée, changez seulement le commentaire et enregistrez.
✅ « Poste enregistré », sans avertissement.
Refaites l'étape 1 sur le salarié à temps plein, dans 4 ou 5 jours.
✅ Seul l'avertissement de modulation (7 jours ouvrés) s'affiche.
Refaites l'étape 1 sur une journée à plus de 7 jours ouvrés (au moins dix jours calendaires plus tard).
✅ Aucun avertissement.
Sur le salarié à temps plein, modifiez une journée dans 4 jours ouvrés en choisissant le motif Urgence sur la carte.
✅ Aucun avertissement : en urgence, 3 jours ouvrés suffisent. À 2 jours ouvrés, l'avertissement « même en urgence… 3 jours ouvrés » s'affiche.
Délais appliqués : 3 jours pour un temps partiel (R-K01/AL-H02, Code du travail L3123-11 — 7 jours par la loi, 3 par accord), 7 jours ouvrés pour toute modification de la modulation (R-K03/AL-L02, accord de branche 1/4/1999 art. 11.4), 3 jours ouvrés sous le motif « Urgence » (R-K04/AL-L03, art. 11.4 al. 3). Les jours ouvrés vont du lundi au vendredi, fériés exclus, et se comptent entre le jour de la modification et la journée modifiée. Les 7 jours calendaires de la CCN-66 (R-K02, art. 20.8) sont couverts par ce second délai. Le référentiel prévoit un blocage avec motif ; à la demande du métier, l'application avertit sans refuser. Un contrat hors convention collective n'a pas de modulation et ne reçoit pas le second avertissement. Une journée déjà passée relève de la correction, pas de la prévenance. Le back-office n'applique pas ces délais.
Couverture automatique : e2e/planning.spec.ts → « signale un délai de prévenance non respecté sans refuser la modulation » (bureau) ; tests/test_planning_override.py couvre les bornes des 3 jours, des 7 jours ouvrés et des 3 jours ouvrés en urgence, le jour férié, le temps plein, le contrat hors convention, la correction sans changement d'horaire et la lecture sans avertissement.
43. Signaler le travail du dimanche d'un agent polyvalent
Tickets : APAJH-181 · Rôle : Manager n1 · Prérequis : un agent polyvalent (contrat hors convention collective) et un salarié CCN-66 dans la grille
Modulez un dimanche de l'agent polyvalent (« Modifier la journée », un créneau travaillé) et enregistrez.
✅ Une alerte « Travail du dimanche : il requiert l'accord du travailleur et une clause au contrat d'accompagnement par le travail » apparaît pour ce dimanche.
Faites de même pour le salarié CCN-66.
✅ Pas d'alerte de ce type.
CASF art. R.243-11-1 (ALT-DIM-01). L'application n'enregistre ni l'accord du travailleur ni la clause : l'alerte demande de les vérifier, elle ne bloque rien.
Couverture automatique : tests/test_outside_collective_agreement.py.
44. Retrouver au journal qui a modifié une journée, et ce qu'elle disait
Tickets : APAJH-313 · Rôle : Manager n1, puis un administrateur avec la permission audit.view_actionlog · Prérequis : un salarié planifié sur la semaine qui vient.
Critères d'acceptation
- Toute écriture d'une journée modulée laisse une ligne au Journal des actions : « Modifier la journée », « Affecter un poste », le repos ponctuel, la suppression, et le back-office.
- La ligne porte qui a écrit, quand, et la journée avant et après, créneaux compris (heures, poste, établissement, pause, options).
- Un enregistrement qui ne change rien n'écrit rien.
En Manager n1, ouvrez Modifier la journée sur le salarié, un jour de la semaine qui vient. Repoussez l'heure de fin du créneau d'une heure et enregistrez.
Rouvrez la même journée et enregistrez sans rien changer.
Rouvrez-la encore, supprimez son dernier créneau avec la poubelle et confirmez le repos ponctuel.
Au back-office, ouvrez Planning ▸ Modifications de planning, choisissez une autre journée modulée, changez l'heure de fin d'un créneau et enregistrez.
Ouvrez Audit ▸ Journal des actions et filtrez le Type d'objet sur « modification de planning ».
✅ Trois lignes Modification : deux au nom du Manager n1 (étapes 1 et 3), une au nom de l'administrateur (étape 4). Leurs Modifications montrent
slotsavant et après — l'heure de fin changée à l'étape 1, la liste vidée etis_restpassé àtrueà l'étape 3. L'étape 2 n'a laissé aucune ligne.
Une journée créée par « Modifier la journée » ou « Affecter un poste » donne une ligne Création ; une modulation supprimée, au planning comme au back-office, une ligne Suppression qui garde ce que disait la journée.
Couverture automatique : tests/test_planning_override_audit.py couvre la création, la correction, le réenregistrement sans changement, le repos ponctuel et la suppression par l'API — avec l'annualisation recalculée à chaque écriture — ainsi que la création, la correction, le réenregistrement et les deux suppressions du back-office. Le reste est à rejouer à la main.
45. Arriver au planning sur une demande de modification à arbitrer
Tickets : APAJH-321 · Rôle : Manager n1 · Prérequis : une demande de modification en attente d'un salarié du périmètre (voir Demandes de modification, scénario 5)
Critères d'acceptation
- « Voir au planning › » d'une modification en attente, dans Activité ▸ Demandes, ouvre
/planning?date=<jour>&employee=<n°>&modification=<n°>: la semaine du jour demandé, la ligne du salarié détachée, et le panneau Demande à arbitrer de la modification. - Valider ou fermer le panneau consomme
modificationet garde la semaine et la ligne détachée. - Une demande tranchée entre-temps ouvre la semaine sans panneau ; un numéro illisible n'est même pas demandé au serveur.
- Un seul panneau à la fois : une adresse portant
absenceetmodificationn'ouvre que celui de l'absence, et le fermer retire les deux. Une autre demande amenée sous le panneau par Précédent / Suivant s'ouvre en lecture, sans le refus armé sur la première.
Dans Activité ▸ Demandes, cliquez « Voir au planning › » sur la ligne de la modification.
✅ Résultat attendu : la semaine du jour demandé, la ligne du salarié détachée, et le panneau Demande à arbitrer ouvert, Théorique → Demandé.
Appuyez sur Échap.
✅ Résultat attendu : le panneau se ferme ;
modificationquitte l'URL,dateetemployeerestent — la semaine et la ligne détachée aussi.Revenez à la file, refaites le trajet et cliquez « ✓ Valider ».
✅ Résultat attendu : le bandeau « Demande validée et appliquée au planning », le panneau se ferme,
modificationquitte l'URL et la case du jour porte ses nouvelles heures.Rouvrez à la main l'adresse du pas 1, demande désormais validée, puis
/planning?date=<jour>&employee=<n°>&modification=abc.✅ Résultat attendu : la semaine s'ouvre, la ligne détachée, sans panneau dans les deux cas.
Couverture automatique : e2e/planning.spec.ts vérifie la semaine, la ligne détachée et le panneau rouverts par ?modification=, la validation et la fermeture qui consomment le paramètre sans quitter la semaine, une modification tranchée qui n'ouvre rien, un identifiant illisible qu'on ne demande pas, une autre modification amenée sous le panneau qui s'ouvre sans le refus armé, et une adresse portant les deux commandes qui n'ouvre qu'un panneau — et, depuis APAJH-324, « Ajuster le poste avant validation » ouvert dans ce panneau, dont la journée remaniée part avec la validation (voir Demandes de modification, scénario 9). La case réellement modifiée par l'accord est à rejouer à la main.
46. Voir au planning la journée dont la modification est demandée
Tickets : APAJH-322 · Rôle : Manager n1 · Prérequis : une demande de modification en attente d'un salarié du périmètre, sur un jour travaillé (voir Demandes de modification, scénario 5)
Critères d'acceptation
- La case du jour demandé garde le poste prévu et porte le cercle orange ; son infobulle dit « Modification demandée » et le motif.
- Le Total de la ligne ne change pas : la journée sera travaillée quelle que soit la décision.
- Un clic sur la case ouvre Demande à arbitrer et écrit
modification=dans l'URL. Le décideur qui clique sa propre ligne, ou un compte qui ne tranche pas les modifications, ouvre le détail de la journée (ce second cas n'est vérifié qu'automatiquement : aucun rôle de recette n'en relève). - Une demande d'absence en attente le même jour l'emporte : la case porte son anneau à elle, et le clic ouvre l'arbitrage de l'absence.
Notez le Total de la ligne du salarié, puis faites déposer la demande (scénario 5 du chapitre cité) et rechargez Activité ▸ Planning sur sa semaine.
✅ Résultat attendu : la case du jour demandé montre toujours son poste, cerclée d'orange ; au survol, « Modification demandée (Demande personnelle) » — ou le motif choisi. Le Total de la ligne est inchangé.
Cliquez la case.
✅ Résultat attendu : le panneau Demande à arbitrer s'ouvre sur la demande, et l'URL porte
modification=<n°>.Refusez la demande avec un motif, depuis ce panneau.
✅ Résultat attendu : le panneau se ferme et la case n'est plus cerclée, sans recharger la page.
Couverture automatique : e2e/planning.spec.ts vérifie le cercle, l'infobulle et le clic qui ouvre l'arbitrage de la modification aux deux densités, le Total inchangé, et le détail de la journée pour un compte sans le droit de trancher ou sur sa propre ligne, et un ?absence= déjà tranché qui ne masque pas la modification cliquée. tests/planning_absences/test_shift_change_calendar.py vérifie, côté serveur, la priorité de l'absence demandée, l'anneau masqué par une absence accordée sur la journée entière, gardé par l'autre moitié d'une demi-journée et absent du créneau qu'elle neutralise, les demandes tranchées qui ne marquent rien, et la lecture en une requête, par la seule période.
47. Retrouver un salarié dans la grille
Tickets : APAJH-327 · Rôle : encadrant · Prérequis : mode démonstration activé
Tapez « aub » dans Rechercher un salarié.
✅ Résultat attendu : la liste propose Isabelle Aubert, et pas les autres noms.
Choisissez Isabelle Aubert.
✅ Résultat attendu : la grille ne garde que ses lignes — une sous Unité Soin, une sous Unité Éducative, puisqu'elle est dans les deux équipes. Personne d'autre n'est affiché.
Videz le champ avec la croix.
✅ Résultat attendu : tous les salariés reviennent.
Filtrez sur l'établissement FAM Les Cigales, puis tapez « mar » dans la recherche.
✅ Résultat attendu : la liste propose Léa Martins (Unité Soin) mais pas Sophie Martin, dont l'équipe n'est pas affichée : la recherche ne propose que les salariés que les autres filtres laissent.
Videz le champ et le filtre établissement, choisissez de nouveau Isabelle Aubert, puis filtrez sur FAM Les Cigales. Retirez ensuite ce filtre.
✅ Résultat attendu : changer d'établissement vide la recherche et rend toutes les lignes du site ; retirer le filtre ne la ramène pas, tout le monde est affiché.
Couverture automatique : e2e/planning.spec.ts → « la recherche salarié réduit la grille à celui choisi, dans chacune de ses équipes » et « changer d'établissement lève la recherche salarié, sans retour » (bureau et mobile).