Apparence
Demandes de modification
Le salarié demande qu'une de ses journées soit travaillée autrement : d'autres heures, un créneau en plus, une pause rémunérée, du télétravail. L'encadrant la tranche ensuite, et l'accord devient la modification de la journée.
Le 1 et le 2 s'appellent à la main sur l'API : ces lots sont serveur, et l'encadrant n'a pas encore d'écran pour trancher. Le 3 se joue au back-office. Le 4 se joue dans l'application, sur des demandes déposées sur l'API ; le 5 les dépose par le formulaire du salarié. Le 6 suit la cloche de chacun. Le 7 lit les demandes dans la file de l'encadrant, et le 8 les y tranche.
Pour qui : le compte du salarié lui-même pour déposer et retirer ; un compte Manager n1 pour lire et trancher les demandes de son périmètre ; un compte RH n1 pour corriger et trancher au back-office.
Prérequis communs : la stack tourne (make up) et les seeds sont jouées (make seed-all) — voir l'index pour les comptes et les adresses.
Déposer une demande sur l'API
1. Déposer une demande pour sa journée, la relire et la retirer
Tickets : APAJH-314 · Rôle : Salarié sur l'API, puis Manager n1 sur l'API · Prérequis : un salarié porteur d'un contrat et d'une trame en cours (une journée « Matin » planifiée dans les jours qui viennent), un jeton de son compte et un jeton d'un compte Manager n1 de son établissement
Critères d'acceptation du ticket
- Le salarié dépose une demande pour une de ses journées : motif, créneaux (heures, pause), options du jour, commentaire.
- La journée est celle de son compte ; il ne demande pas pour un autre.
- Une demande remonte jusqu'à J-14, et pas au-delà.
- Une demande qui ne change rien, ou une deuxième demande en attente le même jour, est refusée.
- Le salarié relit ses demandes et retire celle qui est en attente ; le journal d'audit garde le dépôt et le retrait.
Avec le jeton du salarié, déposez une demande pour un jour planifié en « Matin » :
POST /api/shift-change-requests/avec{"date": "<ce jour>", "reason": "demande_personnelle", "comment": "Rendez-vous médical", "slots": [{"start_time": "09:00", "end_time": "17:00", "break_start_time": "12:00", "break_end_time": "12:15", "break_paid": true}]}.✅ Résultat attendu :
201. La réponse porteemployeeà sa fiche,statusàpending,planned_descriptionà « JM · Matin · 07:00–14:30 » (selon le poste de la trame),planned_minutesà la durée nette de ce poste etrequested_minutesà 480 — la pause rémunérée de 15 min n'est pas déduite.Rejouez exactement la même requête.
✅ Résultat attendu :
400sousdate— « Vous avez déjà une demande en attente le … : retirez-la avant d'en déposer une autre. »Déposez pour un autre jour une demande sans créneau ni option :
{"date": "<un autre jour planifié>", "reason": "urgence"}.✅ Résultat attendu :
400— « Votre demande ne change rien à la journée. » Ajoutez"is_remote": true:201, etrequested_minuteségaleplanned_minutes— sans créneau, la journée garde ses heures.Déposez une demande datée de J-15 (quinze jours avant aujourd'hui), puis ajoutez dans la charge
"employee": <la fiche d'un collègue>à une demande valable.✅ Résultat attendu : J-15 répond
400sousdate. La seconde répond201et porte la fiche du salarié qui dépose : le champemployeeenvoyé est ignoré.Avec le jeton du Manager n1, lisez
GET /api/shift-change-requests/, puis tentezDELETE /api/shift-change-requests/<n° de la demande de l'étape 1>/.✅ Résultat attendu : la liste contient les demandes du salarié. Le
DELETErépond403: l'encadrant tranche une demande, il ne la retire pas.Avec le jeton du salarié, retirez la demande de l'étape 1 :
DELETE /api/shift-change-requests/<n°>/.✅ Résultat attendu :
204, et la demande ne figure plus dansGET /api/shift-change-requests/. Au back-office, Journal des actions porte, pour cette demande, une ligne Création puis une ligne Suppression, toutes deux avec le créneau 09:00–17:00 et sa pause.
Trancher une demande sur l'API
2. Valider une demande, en refuser une autre, et retrouver la journée au planning
Tickets : APAJH-315 · Rôle : Salarié sur l'API, puis Manager n1 sur l'API · Prérequis : les prérequis du 1 ; un compte DA de l'établissement du salarié
Critères d'acceptation du ticket
- Un décideur valide une demande en attente : elle devient la modification de la journée, créée ou remplacée, avec ses créneaux (poste, établissement, pause) et ses options sur chacun.
- Il refuse une demande en motivant son refus ; sans motif, le refus est refusé.
- Une décision porte la version de la demande lue : une version dépassée répond
409, une demande déjà tranchée400. - Le DA ne tranche pas ; personne ne tranche sa propre demande.
- Le journal d'audit garde la décision et l'écriture de la journée.
Avec le jeton du salarié, déposez une demande pour un jour planifié en « Matin » :
POST /api/shift-change-requests/avec{"date": "<ce jour>", "reason": "demande_personnelle", "is_remote": true, "slots": [{"start_time": "09:00", "end_time": "17:00", "break_start_time": "12:00", "break_end_time": "12:30", "break_paid": false}]}. Notez son numéro.✅ Résultat attendu :
201,statusàpendingetversionà1.Avec le jeton du Manager n1, lisez-la :
GET /api/shift-change-requests/<n°>/.✅ Résultat attendu :
200.current_daydécrit la journée telle qu'elle est planifiée maintenant : le « Matin », sa durée nette et son créneau 07:00–14:30 avec sa pause.warningsvautnull.Avec le jeton du DA, tentez de la valider :
POST /api/shift-change-requests/<n°>/validate/avec{"version": 1}.✅ Résultat attendu :
403— le DA lit la demande, il ne la tranche pas.Avec le jeton du Manager n1, validez-la avec
{"version": 2}, puis avec{"version": 1}.✅ Résultat attendu : la première répond
409— « Cette demande a déjà été tranchée par quelqu'un d'autre… ». La seconde répond200:statusàvalidated,versionà2,decided_byau compte du manager.Au planning (Activité ▸ Planning), ouvrez la semaine de ce jour.
✅ Résultat attendu : la journée du salarié est modifiée, de 09:00 à 17:00, avec le picto télétravail. Ouvrir la journée montre le créneau, sa pause de 30 min non rémunérée et le motif « Demande personnelle ».
Revalidez la même demande avec
{"version": 2}.✅ Résultat attendu :
400sousstatus— « Seule une demande en attente peut faire l'objet d'une décision. »Avec le jeton du salarié, déposez une demande pour un autre jour planifié (
{"date": "<un autre jour>", "reason": "urgence", "is_remote": true}). Avec le jeton du Manager n1, refusez-la :POST /api/shift-change-requests/<n°>/refuse/avec{"version": 1}, puis avec{"version": 1, "reason": "Effectif insuffisant ce jour-là."}.✅ Résultat attendu : le premier refus répond
400sousreason. Le second répond200:statusàrefusedetdecision_reasonporte le motif. La journée reste celle de la trame au planning.Avec le jeton du Manager n1, déposez une demande pour sa propre journée, puis tentez de la valider.
✅ Résultat attendu : le dépôt répond
201; la validation403— « Vous ne pouvez pas trancher votre propre demande de modification. »Au back-office, ouvrez Journal des actions.
✅ Résultat attendu : la demande validée porte une ligne Modification du manager (état
pending→validated), et la modulation du jour une ligne Création du même manager, avec le créneau 09:00–17:00, sa pause et le télétravail. La demande refusée porte sa ligne Modification avec le motif.
Corriger et trancher une demande au back-office
3. Corriger une demande en attente, la valider avec un motif, et en refuser une autre
Tickets : APAJH-316 · Rôle : Salarié sur l'API, puis RH n1 au back-office · Prérequis : les prérequis du 1 ; un compte RH n1 ouvrant le back-office (second facteur compris)
Critères d'acceptation du ticket
- Le back-office corrige une demande en attente : motif, options, commentaire et créneaux. Chaque correction est tracée et fait avancer la version.
- Il valide et refuse une demande en attente par la même décision que l'API : application au planning, annualisation, journal.
- Refuser exige un motif, et valider une demande corrigée au back-office aussi ; sans motif, rien n'est écrit.
- Un compte sans le droit de trancher, ou sur sa propre fiche, ne tranche pas.
- Une demande tranchée est en lecture seule et ne se supprime pas ; sa propre demande ne se corrige pas au back-office.
Avec le jeton du salarié, déposez deux demandes pour deux jours planifiés en « Matin » :
POST /api/shift-change-requests/avec{"date": "<jour A>", "reason": "demande_personnelle", "slots": [{"start_time": "09:00", "end_time": "17:00"}]}, puis{"date": "<jour B>", "reason": "urgence", "is_remote": true}.✅ Résultat attendu :
201pour chacune,requested_minutesà 480 pour la première.Au back-office, en RH n1, ouvrez Planning ▸ Demandes de modification de planning, filtrez l'état sur « En attente » et ouvrez la demande du jour A.
✅ Résultat attendu : la fiche montre le salarié, la date, la Journée au dépôt (« Matin » et sa durée), le créneau 09:00–17:00 en bas, un champ Motif de la décision et, sous les boutons d'enregistrement, Valider la demande et Refuser la demande.
Passez la fin du créneau à 18:00 et cliquez Enregistrer.
✅ Résultat attendu : la Version passe à 2, la durée demandée à 540, et Corrigée par porte le compte RH. Journal des actions porte une ligne Modification de la RH sur la demande, avec le créneau avant (17:00) et après (18:00).
Rouvrez la demande et cliquez Valider la demande sans saisir de motif.
✅ Résultat attendu : le message « Cette demande a été modifiée au back-office : saisissez le motif de la décision… ». La demande reste En attente, et rien n'apparaît au planning.
Saisissez le motif « Fin à 18 h, convenu par téléphone. » et cliquez Valider la demande.
✅ Résultat attendu : « Demande validée et appliquée au planning. » La fiche passe en lecture seule, état Validée, décidée par la RH avec son motif. Au planning, la journée du salarié est modifiée de 09:00 à 18:00, et Modifications de planning en porte la ligne.
Ouvrez la demande du jour B et cliquez Refuser la demande sans motif, puis avec le motif « Effectif insuffisant ce jour-là. ».
✅ Résultat attendu : le premier envoi affiche « Saisissez le motif de la décision : le salarié le lira. » et laisse la demande En attente. Le second la passe en Refusée avec son motif ; la journée reste celle de la trame.
Avec le jeton d'un compte RH n1 porteur d'une fiche salarié (contrat et trame en cours), déposez une demande pour sa propre journée, puis ouvrez-la au back-office avec ce même compte.
✅ Résultat attendu : la fiche s'ouvre en lecture seule, sans Enregistrer, Valider la demande ni Refuser la demande : nul ne corrige ni ne tranche sa propre demande.
Ouvrez la demande refusée à l'étape 6 et cherchez Supprimer, puis cochez-la dans la liste et lancez Supprimer les demandes … sélectionnées.
✅ Résultat attendu : la fiche n'offre pas Supprimer, et l'action groupée est refusée : une demande tranchée ne se supprime pas.
Suivre ses demandes dans « Mes demandes »
4. Retrouver ses demandes de modification parmi ses absences, et en retirer une
Tickets : APAJH-317 · Rôle : Salarié sur l'API, puis Salarié dans l'application · Prérequis : les prérequis du 1 ; une demande d'absence déposée par le même salarié (voir Absences, scénario 22) ; un compte Manager n1 pour la refuser
Critères d'acceptation du ticket
- « Mon espace ▸ Mes demandes » liste les demandes de modification avec les absences, dans une seule liste, la dernière déposée en premier.
- Les puces comptent les deux sortes ensemble : « En attente », « Validées », « Refusées ».
- Une carte de modification montre la journée prévue — celle du dépôt, ou celle qu'un accord a remplacée — et ce qui est demandé, créneau par créneau, avec les pauses et les options.
- Une demande refusée montre la date et le motif du refus.
- Le salarié retire une demande en attente ; une demande tranchée n'a pas de bouton.
Avec le jeton du salarié, déposez deux demandes de modification pour deux jours planifiés en « Matin » :
POST /api/shift-change-requests/avec{"date": "<jour A>", "reason": "demande_personnelle", "is_remote": true, "comment": "Rendez-vous médical", "slots": [{"start_time": "09:00", "end_time": "17:00", "break_start_time": "12:00", "break_end_time": "12:15", "break_paid": true}]}, puis{"date": "<jour B>", "reason": "urgence", "is_remote": true}. Avec le jeton du Manager n1, refusez la seconde :POST /api/shift-change-requests/<n°>/refuse/avec{"version": 1, "reason": "Effectif insuffisant ce jour-là."}.✅ Résultat attendu :
201pour chaque dépôt,200pour le refus.Connectez-vous avec le compte du salarié, en mobile (390 × 844), et ouvrez Mon espace ▸ Mes demandes.
✅ Résultat attendu : les deux cartes Modification de journée figurent dans la même liste que les absences. La première carte est la dernière demande déposée, quelle que soit sa sorte. La page ne défile pas horizontalement.
Lisez la carte du jour A.
✅ Résultat attendu : la pastille En attente ; la date du jour et le motif « Demande personnelle » ; « Déposée il y a … » ; Prévu décrit le « Matin » et sa durée ; Demandé porte « 09:00–17:00 · pause 12:00–12:15 rémunérée » et « Total 8h00 » ; Options porte « Télétravail » ; « Votre commentaire : « Rendez-vous médical » » ; un bouton « Annuler la demande ».
Lisez la carte du jour B.
✅ Résultat attendu : la pastille Refusée ; « Refusée le JJ/MM » ; Demandé porte « Horaires inchangés » — une demande sans créneau garde les heures de la journée ; l'encadré rouge « Commentaire du refus : « Effectif insuffisant ce jour-là. » » ; aucun bouton.
Parcourez les filtres.
✅ Résultat attendu :
Toutescompte les absences et les modifications ;En attentemontre la demande du jour A parmi les absences en attente,Refuséescelle du jour B.Sur la carte du jour A, cliquez « Annuler la demande », puis confirmez.
✅ Résultat attendu : la boîte « Annuler cette demande ? » nomme « Modification de journée » et le jour A. Après confirmation, le message « Demande annulée » s'affiche et la carte disparaît. Au back-office, Journal des actions porte une ligne Suppression pour cette demande.
Repassez en bureau (1440 × 900) sur la même URL.
✅ Résultat attendu : le même écran en colonne centrée ; dans chaque carte, l'étiquette Prévu / Demandé / Options se place à gauche de sa valeur au lieu d'au-dessus.
Couverture automatique : e2e/mes-demandes.spec.ts rejoue aux deux densités la liste mêlée et son ordre, les comptes des puces, la carte d'une modification (prévu, créneaux et pauses, total, options, commentaire), le refus avec son motif et sans bouton, le retrait qui appelle le DELETE des demandes de modification et non celui des absences, et le retrait refusé parce que la demande a été tranchée entre-temps : la liste est relue, la carte dit la décision, et l'erreur s'efface dès que le salarié réussit une autre écriture. Ce qui reste à jouer à la main est ce que la spec bouchonne : la lecture réelle des deux listes et la trace du retrait au journal.
Demander une modification depuis « Mon espace »
5. Demander une autre journée par le formulaire, poste et pause repris
Tickets : APAJH-318 · Rôle : Salarié dans l'application · Prérequis : les prérequis du 1 — une journée « Matin » planifiée dans les jours qui viennent, avec sa pause non rémunérée
Critères d'acceptation du ticket
- Le « + » de « Mes demandes » mène à « Demander une modification », aux deux densités.
- Le salarié choisit un seul jour, à J-14 au plus tôt, sur un calendrier teinté de ses postes.
- Le formulaire reprend les créneaux actuels du jour : heures, poste et établissement en lecture seule, pause. Le salarié change les heures, ajoute ou retire des créneaux, tape une pause rémunérée, coche les options du jour et commente.
- L'écart entre le poste actuel et le total demandé est calculé net des pauses non rémunérées.
- « Envoyer la demande » reste inactif tant que la demande ne change rien, et une demande ne vide pas la journée.
- Un refus du serveur s'affiche tel quel ; après l'envoi, la demande apparaît « En attente » dans « Mes demandes ».
Connectez-vous avec le compte du salarié, en mobile (390 × 844). Ouvrez Mon espace ▸ Mes demandes, cliquez le « + », puis « Demander une modification ».
✅ Résultat attendu : la page « Demander une modification » s'ouvre sur un seul champ Date. « Envoyer la demande » est inactif, avec « Veuillez choisir une date. » au-dessus.
Ouvrez le calendrier.
✅ Résultat attendu : les jours sont teintés de la couleur de vos postes ; les jours d'avant J-14 sont grisés et ne se cliquent pas.
Choisissez le jour planifié en « Matin » et cliquez Valider.
✅ Résultat attendu : Poste actuel décrit le « Matin » et sa durée nette. Créneau 1 porte le poste en lecture seule, les heures 07:00 et 14:30, et sa pause non rémunérée. « Envoyer la demande » reste inactif : « Modifiez au moins un horaire, une pause ou une option de la journée. »
Changez le motif pour « Urgence » et saisissez un commentaire.
✅ Résultat attendu : le bouton reste inactif — le motif et le commentaire disent pourquoi, pas quoi.
Passez la fin du créneau à 15:30, cliquez « Ajouter un créneau » et saisissez 15:30–17:00, puis « Ajouter une pause rémunérée » de 16:00 à 16:15 sur ce second créneau. Cochez Télétravail.
✅ Résultat attendu : le second créneau ne porte ni poste ni établissement. Le bloc d'écart montre Poste actuel, Total demandé et l'Écart positif, la pause non rémunérée du premier créneau déduite des deux côtés et la pause rémunérée du second non déduite. Le bouton s'active.
Cliquez « Envoyer la demande ».
✅ Résultat attendu : l'écran « Demande de modification envoyée » nomme le jour. « Voir mes demandes » ramène à la liste, où une carte Modification de journée est En attente : Demandé porte vos deux créneaux, la pause non rémunérée du premier et la pause rémunérée du second, et Options porte « Télétravail ».
Rouvrez le formulaire sur le même jour et cochez Hors établissement, puis envoyez.
✅ Résultat attendu : le refus du serveur s'affiche tel quel au-dessus du bouton — « Vous avez déjà une demande en attente le … » — et le formulaire reste ouvert.
Sur la carte en attente, cliquez « Annuler la demande » et confirmez. Rouvrez le formulaire sur ce jour, cochez seulement Hors établissement et envoyez.
✅ Résultat attendu : la demande part. Sa carte porte « Horaires inchangés » : sans changement d'heures, la demande ne porte aucun créneau et la journée garde les siennes.
Rouvrez le formulaire sur un autre jour planifié et cliquez la corbeille du créneau.
✅ Résultat attendu : le bouton reste inactif — « Une demande ne peut pas retirer tous les créneaux : pour ne pas travailler ce jour-là, demandez une absence. »
Choisissez ensuite un jour de repos et cochez seulement Télétravail.
✅ Résultat attendu : sur le jour de repos, le bouton reste inactif — « Ajoutez un créneau : ce jour-là ne porte aucun poste, une option seule n'y change rien. »
Repassez en bureau (1440 × 900) sur la même URL.
✅ Résultat attendu : le formulaire tient dans une carte blanche centrée, un bouton Annuler à gauche d'« Envoyer la demande » ramène à « Mes demandes ». La page ne défile jamais horizontalement, à aucune des deux densités.
Couverture automatique : e2e/demande-modification.spec.ts rejoue aux deux densités le parcours complet — date obligatoire, créneau pré-rempli avec son poste et sa pause, écart net, corps du POST poste et pause compris, écran d'envoi —, le créneau ajouté avec une pause rémunérée et sans poste, l'option seule envoyée sans créneau, la demande qui ne change rien ou vide la journée et reste inactive, la journée sans poste qui s'ouvre sans créneau et n'envoie pas une option seule, l'avertissement d'une journée aux options différentes d'un créneau à l'autre, les heures passées d'un créneau à un autre qui ne se lisent pas comme inchangées, la journée relue à chaque ouverture, la borne J-14 du calendrier et le refus du serveur affiché tel quel ; plus le compte sans fiche salarié. backend/tests/shift_changes/test_shift_change_day.py vérifie la lecture de la journée : créneaux de la trame ou de la modulation, poste, établissement et pause compris, les bornes du dépôt (J-14, contrat) dès la lecture, et qu'une journée renvoyée telle quelle ne change rien au dépôt. Ce qui reste à jouer à la main est ce que la spec bouchonne : les vraies couleurs du calendrier, la lecture réelle de la journée et le refus du doublon en attente.
Être prévenu
6. Recevoir la demande, puis la décision, dans la cloche
Tickets : APAJH-319 · Rôle : Salarié dans l'application, Manager n1 sur l'API, puis RH n1 au back-office · Prérequis : les prérequis du 1 ; le rôle Manager n1 du décideur restreint à l'établissement du salarié ; un compte Admin sans établissement
Critères d'acceptation du ticket
- Un dépôt sonne les décideurs dont la file affiche la demande : les rattachés aux établissements du salarié d'abord, ceux qui couvrent toute l'association à défaut, jamais l'auteur. Un compte qui peut trancher mais ne lit pas la file n'est pas sonné.
- Une demande que personne ne peut trancher est déposée quand même : l'écran d'envoi le dit, l'auteur et ceux qui peuvent réparer au back-office sont prévenus.
- La décision sonne le salarié, motif compris, qu'elle vienne de l'API ou du back-office. Accordée après une correction au back-office, la décision dit « validée avec modification » et nomme les créneaux accordés. Un retrait ne sonne personne.
- Un dépôt dont la sonnerie échoue est annulé : il n'existe pas sans elle.
Connectez-vous avec le compte du salarié et déposez une demande par le formulaire (scénario 5, pas 1 à 6).
✅ Résultat attendu : l'écran « Demande de modification envoyée » dit qu'elle a été transmise à votre responsable, puis « Vous serez notifié de la décision. »
Connectez-vous avec le compte Manager n1, puis avec l'Admin sans établissement, et ouvrez la cloche de chacun.
✅ Résultat attendu : le Manager n1 lit « Nouvelle demande de modification — salarié a déposé une demande de modification de la journée du JJ/MM/AAAA. » L'Admin n'a rien : il voit la demande (
GET /api/shift-change-requests/?status=pending), mais quelqu'un de rattaché peut la trancher.Avec le jeton du Manager n1, refusez la demande :
POST /api/shift-change-requests/<id>/refuse/avec{"version": 1, "reason": "Effectif insuffisant"}.✅ Résultat attendu : dans la cloche du salarié, « Demande de modification refusée » en rouge, qui se termine par « Motif : Effectif insuffisant ».
En salarié, déposez une seconde demande sur un autre jour, puis retirez-la depuis « Mes demandes » avant toute décision.
✅ Résultat attendu : la cloche du Manager n1 a sonné au dépôt, et pas au retrait.
Déposez une troisième demande. En RH n1, ouvrez-la au back-office (Planning ▸ Demandes de modification de planning) et cliquez « Valider la demande ».
✅ Résultat attendu : dans la cloche du salarié, « Demande de modification validée » en information — « Votre demande de modification de la journée du JJ/MM/AAAA a été validée. » Le back-office sonne comme l'API.
Déposez une quatrième demande, 09:00–17:00. En RH n1 au back-office, passez la fin du créneau à 18:00, puis cliquez « Valider la demande » avec le motif « Fin à 18 h ».
✅ Résultat attendu : la cloche du salarié dit « … a été validée avec modification : 09:00-18:00. Motif : Fin à 18 h » — l'accord n'est pas ce qu'il avait demandé.
Remettez le rôle du Manager n1 dans son état d'origine.
La demande que personne ne peut trancher. Un encadrant seul décideur de son site qui demande pour lui-même, sur une base où aucun décideur ne couvre toute l'association, ne s'affiche dans aucune file. Le dépôt passe quand même : l'écran d'envoi dit « a été enregistrée », puis « Aucun responsable ne peut traiter cette demande pour l'instant. Contactez votre service RH. » ; l'auteur en garde une trace dans sa cloche, et chaque Admin reçoit « Demande de modification sans valideur » avec la cause — à défaut d'Admin, les RH n1/n2, à défaut un superutilisateur. Le jeu de démonstration compte des décideurs sans établissement, ce qui rend le cas injouable à la main sans le défaire : il est couvert par la suite automatique.
Couverture automatique : backend/tests/shift_changes/test_shift_change_notifications.py vérifie que le dépôt sonne le décideur rattaché dont la file montre la demande, et pas l'Admin non rattaché qui la voit aussi, ni un décideur qui ne lit pas la file ; l'auteur jamais sonné et le repli sur les non rattachés ; la demande orpheline, signalée à l'auteur et à l'Admin avec sa cause, et has_recipient ; le dépôt annulé quand sa sonnerie échoue ; la relecture qui ne dit rien des destinataires, le retrait silencieux, et les décisions — sur l'API comme au back-office, l'accord d'une demande corrigée nommant ce qui a été accordé — portées au salarié. Les règles communes aux absences (repli de l'alerte, superutilisateur) sont couvertes par backend/tests/absences/test_absences_notifications.py. e2e/demande-modification.spec.ts rejoue aux deux densités l'écran d'envoi d'une demande transmise et celui d'une demande orpheline.
Lire les demandes dans la file de l'encadrant
7. Retrouver les modifications parmi les absences dans « Activité ▸ Demandes »
Tickets : APAJH-320 · Rôle : Salarié dans l'application, puis Manager n1 dans l'application · Prérequis : les prérequis du 1 ; une demande d'absence en attente sur le périmètre du Manager n1 (voir Absences, scénario 22)
Critères d'acceptation du ticket
- L'entrée de menu Activité ▸ Absences devient Activité ▸ Demandes, à l'adresse
/demandes; l'ancienne adresse/absencesne mène plus nulle part. - La file liste les demandes de modification avec les absences, dans un seul tableau, sous les mêmes filtres d'état, de période, d'établissement, d'équipe et de salarié.
- Un filtre de type — Tout, Absences, Modifications —, en tête de la ligne, avant le filtre d'état, garde une seule sorte de demande. Sous « Annulations », qui ne liste que des absences, il est figé sur Absences.
- Une ligne de modification s'ouvre sur son panneau, comme une absence — c'est le scénario 8, qui l'a rendue cliquable (APAJH-321).
- Les modifications sont bornées au périmètre de l'encadrant, comme les absences. Si elles ne peuvent pas être lues, l'écran le dit et liste les absences.
Connectez-vous avec le compte du salarié et déposez une demande de modification par le formulaire (scénario 5, pas 1 à 6), en motif « Remplacement ».
✅ Résultat attendu : l'écran « Demande de modification envoyée ».
Connectez-vous avec le compte Manager n1, en bureau (1440 × 900).
✅ Résultat attendu : le menu Activité porte l'entrée Demandes ; l'entrée Absences n'existe plus. Saisir
/absencesdans la barre d'adresse ne mène à aucun écran.Ouvrez Activité ▸ Demandes.
✅ Résultat attendu : le titre Demandes ; sur la seconde ligne de filtres, le filtre de type en premier, sur Tout, puis l'état À valider ; la demande du pas 1 parmi les absences en attente, triées ensemble par date. Sa colonne Demande dit « Modification de journée · JJ mois · Remplacement », son état En attente. Le clic et le lien « Voir au planning › » sont le scénario 8.
Cliquez le filtre Modifications, puis Absences, puis Tout.
✅ Résultat attendu : Modifications ne garde que la demande du pas 1, Absences que les absences, Tout les deux. Le compte sous le tableau suit.
Choisissez l'établissement du salarié, puis un autre.
✅ Résultat attendu : la modification reste au premier, disparaît au second, comme les absences du même salarié.
Cliquez l'état Annulations.
✅ Résultat attendu : le filtre de type reste à sa place mais se grise, figé sur Absences — le filtre d'état ne bouge pas sous le clic ; seules des demandes d'annulation d'absence sont listées — une modification n'est jamais annulée.
Avec le jeton du Manager n1, refusez la demande du pas 1 :
POST /api/shift-change-requests/<id>/refuse/avec{"version": 1, "reason": "Effectif insuffisant"}. Revenez sur l'écran, état Refusées.✅ Résultat attendu : la modification y figure, badge Refusée, et ne figure plus sous À valider.
Rétrécissez la fenêtre à la largeur d'un téléphone (390 × 844).
✅ Résultat attendu : le filtre de type passe à la ligne avec les autres filtres ; le tableau défile dans son cadre, jamais la page.
Couverture automatique : e2e/absences-queue.spec.ts rejoue le titre et l'entrée de menu renommés, la liste mêlée — une modification et une absence de même identifiant donnant bien deux lignes —, la phrase d'une modification, son état, l'état et la fenêtre envoyés à /api/shift-change-requests/, le filtre de type, le filtre d'établissement appliqué aux deux sortes, une modification validée sous Validées, l'état Annulations qui ne lit pas les modifications et fige le filtre de type, placé avant le filtre d'état, et le filtre de type sur un téléphone ; la route refusée à un salarié et l'entrée de menu absente sans le droit se rejouent sur /demandes. Ce qui reste à jouer à la main est ce que la spec bouchonne : la lecture réelle des deux listes sous le périmètre du Manager n1. La spec couvre aussi une modification hors du périmètre écartée, et la lecture des modifications en échec, signalée sans emporter les absences.
Trancher dans la file de l'encadrant
8. Valider une modification depuis « Activité ▸ Demandes », en refuser une autre
Tickets : APAJH-321 · Rôle : Salarié dans l'application, puis Manager n1 dans l'application, puis DA · Prérequis : les prérequis du 1 ; deux demandes de modification déposées par le salarié sur deux jours différents (scénario 5), l'une avec une pause rémunérée et Télétravail coché
Critères d'acceptation du ticket
- Un clic sur une ligne de modification ouvre le panneau Demande à arbitrer : qui la demande, le jour et le motif, puis Théorique → Demandé — la journée telle qu'elle est maintenant, ce qu'un accord remplacerait, face aux créneaux demandés avec leurs pauses —, la durée demandée, l'écart, les options et le commentaire du salarié.
- Une absence accordée ce jour-là est signalée avant de trancher : elle prime sur la modification, qui ne se verrait pas au planning.
- Valider applique la demande au planning et ferme le panneau ; les avertissements de prévenance de l'accord sont dits dans le bandeau.
- Refuser demande d'abord un motif, et refuse un motif vide.
- Une demande tranchée entre-temps par quelqu'un d'autre le dit, et les boutons disparaissent.
- Sans le droit de trancher (DA), ou sur sa propre demande, le panneau n'a pas de pied.
- Une modification déjà tranchée s'ouvre aussi, titrée Demande validée ou Demande refusée, sans pied ; son « Théorique » est la journée que l'accord a remplacée, et un refus montre son motif.
- Une modification en attente porte « Voir au planning › », qui ouvre sa semaine et y rouvre le panneau (voir Planning, scénario 45).
Connectez-vous avec le compte Manager n1, en bureau (1440 × 900), et ouvrez Activité ▸ Demandes.
✅ Résultat attendu : les deux modifications du salarié sous À valider, chacune avec « Voir au planning › » ; au survol, la ligne se surligne.
Cliquez la ligne de la demande avec pause rémunérée.
✅ Résultat attendu : le panneau Demande à arbitrer — le salarié, sa fonction et son établissement ; « Jour JJ mois AAAA · motif » ; à gauche Théorique, la journée telle qu'elle est planifiée et sa durée ; à droite Demandé, chaque créneau « HH:MM–HH:MM · pause HH:MM–HH:MM rémunérée » ; puis Durée, Écart signé (« +0h30 »), Options « Télétravail » et le Commentaire entre guillemets. En pied, « ✕ Refuser » et « ✓ Valider ».
Cliquez « ✓ Valider ».
✅ Résultat attendu : le bandeau « Demande validée et appliquée au planning », le panneau se ferme et la ligne quitte À valider. Sous Validées, elle figure, badge Validée ; au Planning, la journée porte les nouvelles heures, le poste et la pause d'origine, et le picto télétravail.
Ouvrez la seconde demande, cliquez « ✕ Refuser », puis « ✕ Confirmer le refus » sans rien écrire.
✅ Résultat attendu : le champ « Commentaire du refus (obligatoire) » apparaît et prend le curseur ; au second clic, « Indiquez pourquoi la demande est refusée : le salarié lira cette phrase. » — rien n'est envoyé.
Écrivez « Pas de remplaçant ce jour-là. » et confirmez.
✅ Résultat attendu : le bandeau « Demande refusée — la case reste inchangée » ; côté salarié, « Mes demandes » montre la demande Refusée avec ce motif.
Le salarié dépose une nouvelle demande sur un jour où il a un congé accordé ; rouvrez-la en Manager n1.
✅ Résultat attendu : sous la carte, « Absence accordée ce jour (motif) : elle prime sur la modification là où elle la recouvre. » Une demande d'absence encore en attente le même jour ne déclenche pas cet avertissement.
Ouvrez la même demande dans deux onglets ; validez-la dans le premier, puis cliquez « ✓ Valider » dans le second.
✅ Résultat attendu : le second dit que la demande a déjà été tranchée ; les deux boutons disparaissent, et le champ du motif aussi s'il était ouvert ; la file se recharge sans elle.
Passez sur l'état Validées, filtre Modifications, et ouvrez la demande validée au pas 3 ; puis, sous Refusées, celle du pas 5.
✅ Résultat attendu : Demande validée, puis Demande refusée en titre, sans aucun bouton ; « Théorique » dit la journée d'avant l'accord, et la refusée montre « Motif du refus : « Pas de remplaçant ce jour-là. » ».
Reconnectez-vous avec un compte DA du même établissement et ouvrez une demande en attente. Recommencez avec un Manager n1 qui ouvre sa propre demande de modification.
✅ Résultat attendu : le panneau se lit, sans pied ni bouton, dans les deux cas.
Rétrécissez la fenêtre à la largeur d'un téléphone (390 × 844) et ouvrez une demande.
✅ Résultat attendu : Théorique et Demandé s'empilent, la flèche pointe vers le bas ; les deux boutons restent visibles ; la page ne défile jamais en largeur.
Couverture automatique : e2e/absences-queue.spec.ts rejoue, aux deux densités, le panneau ouvert depuis la file — la journée actuelle relue face aux créneaux demandés, pause rémunérée, durée, écart, options et commentaire —, la validation qui envoie la version lue et ferme le panneau, l'avertissement de prévenance dans le bandeau, le refus qui exige son motif puis l'envoie, l'absence accordée signalée, le conflit 409 qui retire les boutons et le motif, le pied absent sans le droit de trancher, sur sa propre demande et sur une demande tranchée, le lien « Voir au planning › » de la ligne et l'arrivée au planning sur le panneau. Ce qui reste à jouer à la main est ce que la spec bouchonne : l'accord réellement appliqué au planning et le conflit entre deux onglets. backend/tests/shift_changes/test_shift_change_decision.py vérifie que le détail nomme l'absence accordée du jour, une fois par motif, et pas une demande d'absence en attente ni sur une demande tranchée.
Ajuster avant de valider
9. Ajuster le poste avant de valider, et motiver l'accord d'une demande corrigée
Tickets : APAJH-324 · Rôle : Salarié dans l'application, puis Manager n1 dans l'application, puis RH n1 au back-office · Prérequis : les prérequis du 1 ; deux demandes de modification déposées par le salarié sur deux jours différents (scénario 5), dont une sur un jour planifié avec un poste et une pause
Critères d'acceptation du ticket
- Sur une demande en attente, « ✎ Ajuster le poste avant validation » ouvre les créneaux de la journée à accorder, semés de la demande — ou de la journée actuelle si elle n'en porte aucun — avec leur poste, leur pause et leurs options ; le poste se choisit dans la bibliothèque de l'encadrant, un créneau s'ajoute ou se retire, et le bilan dit Poste actuel, Total accordé et l'Écart.
- Le dernier créneau ne se retire pas, et des heures incomplètes grisent « ✓ Valider ». Un créneau ajouté garde les options de la demande (télétravail…). Sur une journée sans créneau (un repos posé depuis le dépôt), le volet s'ouvre sur une carte vierge.
- « Annuler l'ajustement » referme le volet : la demande se valide alors telle quelle, sans motif. Un refus armé puis abandonné n'empêche pas de valider.
- Un ajustement garde le commentaire que portait déjà la journée modulée.
- Valider une journée ajustée exige un motif ; l'accord applique la journée ajustée au planning. La demande garde ce qui a été demandé, et « Mes demandes » montre à côté la journée Accordé, son total et son écart avec la demande.
- Le salarié reçoit « validée avec modification », les créneaux accordés et le motif.
- Une demande corrigée au back-office exige aussi un motif pour être validée, dans l'application comme au back-office.
Connectez-vous avec le compte Manager n1, en bureau (1440 × 900), ouvrez Activité ▸ Demandes et cliquez la première demande.
✅ Résultat attendu : sous la carte Théorique → Demandé, le lien « ✎ Ajuster le poste avant validation ».
Cliquez le lien.
✅ Résultat attendu : une carte par créneau demandé, ses heures, son poste et sa pause repris ; pas de corbeille sur une carte seule ; « Ajouter un créneau » et, dessous, Poste actuel, Total accordé et Écart. Le lien disparaît.
Changez le poste pour un autre de votre bibliothèque, puis l'heure de fin ; cliquez « ✓ Valider » sans motif.
✅ Résultat attendu : le bilan suit les heures ; le champ « Motif de la décision (obligatoire) » refuse le clic : « Expliquez ce qui est accordé : la demande n'est pas validée telle qu'elle a été déposée. » — rien n'est envoyé.
Écrivez « Fin à 16 h. » et validez.
✅ Résultat attendu : le bandeau « Demande validée et appliquée au planning », le panneau se ferme ; au Planning, la journée porte le poste et les heures ajustés. Côté salarié, la cloche dit « … a été validée avec modification : créneaux accordés. Motif : Fin à 16 h. », et « Mes demandes » montre la demande Validée : Demandé avec les créneaux et le total demandés, puis Accordé avec les créneaux accordés et « Total durée (écart sur la demande) ».
Sur la seconde demande, ouvrez « Ajuster », cliquez « Ajouter un créneau », puis videz l'heure de fin d'une carte.
✅ Résultat attendu : le créneau ajouté porte les options de la demande ; « ✓ Valider » est grisé, son infobulle demande l'heure de début et de fin de chaque créneau. « Annuler l'ajustement » referme le volet, le motif disparaît, et « ✓ Valider » accorderait la demande telle quelle.
Connectez-vous au back-office en RH n1, ouvrez la seconde demande, changez l'heure de fin d'un créneau et cliquez Enregistrer ; revenez dans l'application en Manager n1, rouvrez-la et cliquez « ✓ Valider ».
✅ Résultat attendu : le motif est exigé avant tout envoi ; écrit, la demande est validée et le salarié reçoit « validée avec modification ».
Rétrécissez la fenêtre à la largeur d'un téléphone (390 × 844) et rouvrez « Ajuster » sur une demande en attente.
✅ Résultat attendu : les cartes et le bilan tiennent dans la largeur ; la page ne défile jamais en largeur.
Couverture automatique : e2e/absences-queue.spec.ts rejoue, aux deux densités, l'ajustement semé de la demande, le motif exigé puis la validation qui envoie en un seul appel la journée ajustée, motif et options de la demande repris sur chaque créneau ; l'ajustement d'une demande sans créneau, semé de la journée actuelle, ou d'une carte vierge sur une journée qui n'en a aucun ; un créneau ajouté qui garde les options de la demande, puis grise la validation tant qu'il est incomplet ; « Annuler l'ajustement » ; un refus abandonné qui laisse valider ; et l'accord d'une demande corrigée au back-office, qui exige son motif. e2e/mes-demandes.spec.ts vérifie que le salarié lit, sur une demande accordée avec ajustement, ce qu'il a demandé, ce qui lui est accordé et l'écart. backend/tests/shift_changes/test_shift_change_decision.py vérifie que l'accord ajusté écrit la journée du décideur et la photographie à côté de la demande, gardée telle que déposée, qu'il s'écrit sur le jour de la demande quoi que nomme la charge, qu'il exige son motif, qu'il garde le commentaire de la journée modulée, qu'une règle de la modulation enfreinte répond sous ses clés sans rien écrire, et qu'une demande corrigée au back-office exige un motif sur l'API aussi ; test_shift_change_notifications.py, que le salarié lit les créneaux accordés.