Apparence
Fiche salarié
La fiche d'un salarié (/utilisateurs/<id>/<volet>) : un bandeau de résumé, un rail de six volets, et trois politiques d'écriture différentes selon le volet et le rôle.
Prérequis communs : make seed-all, qui seed les sources et soldes manuels puis calcule les compteurs automatiques avec les services métier.
Les trois profils à faire jouer
Le point de la fiche n'est pas ce qu'elle affiche, c'est qui peut y écrire. Trois profils suffisent à couvrir les trois politiques :
| Profil | Exemple | Écrit sur |
|---|---|---|
RH / Admin (RH n1, RH n2, Admin) | i.aubert | tout |
Encadrant (Manager n1) | Léa Martins | Urgence et Lieux de travail & équipes seulement |
Lecteur (Service Paie) | — | rien |
L'encadrant est le cas qui compte : il ne tient pas le dossier et écrit tout de même deux volets. Une régression qui rabattrait ces droits sur ceux du dossier passerait inaperçue avec les deux autres profils. Les droits viennent du rôle, jamais du Statut équipe (scénario 22).
1. Lire le bandeau de résumé
Tickets : APAJH-208 · Rôle : RH / Admin · Prérequis : make seed-all
Ouvrez la fiche de Camille Annualisation (
annualization.demo).✅ Résultat attendu : le bandeau porte le nom, la fonction et l'établissement (Éducateur · FAM Les Cigales), le contrat et l'ETP (CDI · 100 %), puis trois chiffres issus des données préparées : l'ancienneté, le solde des congés annuels acquis par la paie et le solde d'annualisation (−6 h).
Vérifiez ce solde dont la valeur absolue dépasse 5 heures sans atteindre 30.
✅ Résultat attendu : la mention Alerte compteur apparaît. En deçà de 5 h, elle n'apparaît pas.
Couverture automatique : e2e/fiche-salarie.spec.ts → « le bandeau résume le salarié et ses trois chiffres ».
2. Naviguer entre les volets
Tickets : APAJH-208 · Rôle : n'importe quel rôle d'encadrement · Prérequis : make seed-workforce
Critères d'acceptation du ticket
- « Étant donné une fiche salarié, quand consultée, alors elle est organisée en onglets. »
Ouvrez une fiche et regardez le rail.
✅ Résultat attendu : la fiche est découpée en six onglets — Informations, Contrat, Famille & social, Urgence, Lieux de travail & équipes, Compteurs — et non en une page continue.
Depuis Informations, cliquez sur Contrat dans le rail.
✅ Résultat attendu : l'adresse devient
/utilisateurs/<id>/contratet le volet Contrat s'affiche.Passez sur Compteurs, puis utilisez le bouton Précédent du navigateur.
✅ Résultat attendu : vous revenez sur Contrat. Les volets sont des adresses, pas un état interne.
Couverture automatique : e2e/fiche-salarie.spec.ts → « le rail ouvre chaque volet par son adresse ». Voir aussi Portail § 6.
3. Vérifier ce qui est verrouillé dans Informations et Contrat
Tickets : APAJH-208 · Rôle : RH / Admin, puis Encadrant · Prérequis : make seed-workforce
En RH, ouvrez Informations.
✅ Résultat attendu : les champs qui viennent de la paie — dont le nom — sont en lecture seule. Nationalité est modifiable. Le bouton Enregistrer est présent.
Ouvrez Contrat.
✅ Résultat attendu : Type de contrat et Qualification sont renseignés ; Ancienneté, Date d'entrée et Nombre d'heures mensuelles (
151,67 h) sont en lecture seule — ce sont des valeurs calculées, jamais saisies.Décochez Non annualisé.
✅ Résultat attendu : le champ Dimanches & jours fériés prévisionnels disparaît. Un contrat annualisé porte déjà ces jours dans son calendrier ; la prévision n'a plus d'objet.
Rouvrez les deux volets avec le profil Encadrant.
✅ Résultat attendu : les mêmes valeurs sont visibles, tous les champs sont en lecture seule, les cases à cocher sont désactivées, et il n'y a pas de bouton Enregistrer.
Couverture automatique : e2e/fiche-salarie.spec.ts → « les champs de la paie sont en lecture seule, le reste est éditable par RH », « un rôle manager lit la fiche sans pouvoir l'enregistrer », « affiche les trois blocs, cadenas compris », « les dimanches ne sont demandés que sur un contrat non annualisé » et « un rôle manager voit le contrat sans pouvoir le modifier ».
4. Vérifier le masquage des données des enfants
Tickets : APAJH-208, APAJH-209 · Rôle : Encadrant, puis RH, puis Admin · Prérequis : un salarié avec au moins un enfant à charge
Critères d'acceptation du ticket
- « Étant donné les droits liés aux enfants, quand affichés, alors seuls nombre et âge apparaissent par défaut. »
Avec le profil Encadrant, ouvrez Famille & social.
✅ Résultat attendu : l'enfant n'expose que son âge. Ni date de naissance, ni mention Situation de handicap.
Affichez le code source de la page et cherchez la date de naissance de l'enfant.
✅ Résultat attendu : elle est absente de la page entière, pas seulement masquée à l'écran. C'est l'API qui retire les champs avant de répondre.
Rouvrez le volet en RH / Admin.
✅ Résultat attendu : Date de naissance et Âge sont renseignés, le commutateur Handicap reflète la situation, et le volet est enregistrable.
Rouvrez le volet en Admin, modifiez la Date de naissance de l'enfant et basculez Handicap, puis enregistrez.
✅ Résultat attendu : l'Admin voit et modifie les enfants exactement comme RH — date de naissance, âge et situation de handicap sont affichés, et l'enregistrement aboutit.
Couverture automatique : e2e/fiche-salarie.spec.ts → « par défaut, un enfant n'expose que son âge » et « RH voit le détail et peut l'éditer ». Côté serveur, le masquage est couvert par backend/tests/test_employee.py.
5. Vérifier les deux volets qu'un encadrant peut écrire
Tickets : APAJH-208, APAJH-177 · Rôle : Encadrant, puis Lecteur · Prérequis : make seed-workforce
Critères d'acceptation du ticket (APAJH-177)
- « Un professionnel peut être rattaché à l'organisation du chef de service technique indépendamment de son établissement principal. »
Avec le profil Encadrant, ouvrez Urgence.
✅ Résultat attendu : Nom & prénom de la personne à prévenir est modifiable et Enregistrer est présent — bien que ce profil ne tienne pas le reste du dossier.
Ouvrez Lieux de travail & équipes.
✅ Résultat attendu : les équipes du salarié sont modifiables et enregistrables. Deux champs restent en lecture seule, et ils disent deux choses différentes : Établissement de paie (avec un cadenas) vient du contrat de référence, et Lieu de travail principal est porté par la fiche — il se saisit à la création ou en back-office.
Affectez le salarié à une équipe qui appartient à un autre établissement que son lieu de travail principal, puis enregistrez.
✅ Résultat attendu : l'affectation est acceptée. Lieu de travail principal n'a pas changé pour autant : les deux axes sont indépendants et ont le droit de diverger. C'est ce qui permet de rattacher un professionnel à l'organisation d'un chef de service technique sans toucher à son lieu de travail principal.
Rejouez les deux volets avec le profil Lecteur.
✅ Résultat attendu : les mêmes informations sont visibles ; les champs sont en lecture seule, les équipes s'affichent comme de simples étiquettes sans croix de retrait, et il n'y a pas de bouton Enregistrer.
Couverture automatique : e2e/fiche-salarie.spec.ts → les quatre tests du groupe « fiche salarié — Urgence et Lieux de travail ». L'étape 3 n'est couverte par aucun test end-to-end : la spec vérifie qu'un encadrant peut modifier les équipes, pas qu'une équipe d'un autre établissement est acceptée. C'est donc le critère d'APAJH-177 qui reste entièrement à la main. Côté serveur, le modèle DivisionAssignment porte la règle et backend/tests/test_employee.py la couvre.
6. Vérifier les compteurs et le verrouillage des années
Tickets : APAJH-208 · Rôle : RH / Admin, puis Encadrant · Prérequis : make seed-all
En RH, ouvrez Compteurs.
✅ Résultat attendu : Congés annuels et Compte épargne-temps s'affichent, et le bouton Éditer est présent.
Cliquez sur Éditer, puis essayez de changer d'année.
✅ Résultat attendu : les onglets d'année sont désactivés tant que l'édition est ouverte. Changer d'année jetterait la saisie en cours sans prévenir.
Rouvrez le volet en Encadrant.
✅ Résultat attendu : les mêmes figures sont lisibles, sans bouton Éditer ni Enregistrer.
Couverture automatique : e2e/fiche-salarie.spec.ts → « les figures sont lisibles par tous, éditables par le seul staff » et « un rôle manager voit les compteurs sans bouton Éditer ».
7. Consulter et éditer la fiche sur téléphone
Tickets : APAJH-208 · Rôle : RH / Admin · Prérequis : make seed-workforce
En 390 × 844, ouvrez le volet Contrat.
✅ Résultat attendu : le rail des volets passe au-dessus du volet au lieu d'être à sa gauche, et la page ne défile pas horizontalement.
Faites défiler jusqu'au bas du volet.
✅ Résultat attendu : les champs restent éditables et la barre d'actions (Enregistrer) reste atteignable.
Couverture automatique : e2e/fiche-salarie.spec.ts → « le rail passe au-dessus du volet et la page ne déborde pas » et « le volet reste éditable et sa barre d'actions atteignable ».
8. Rattacher un salarié provisoire à son matricule paie
Tickets : APAJH-205 · Rôle : RH / Admin · Prérequis : un salarié créé sans matricule — voir Annuaire Utilisateurs § 7.
Critères d'acceptation du ticket
- Étant donné ce compte, quand le matricule paye devient disponible, alors il y est rattaché.
Ouvrez la fiche du salarié créé sans matricule.
✅ Résultat attendu : le bandeau porte le badge rouge Non synchronisée paie.
Dans le volet Informations, bloc Identité, saisissez le Matricule communiqué par la paie, puis Enregistrer.
✅ Résultat attendu : le badge Non synchronisée paie disparaît du bandeau, et la pastille Provisoire disparaît de la ligne du salarié dans l'annuaire.
Ouvrez la fiche d'un intérimaire.
✅ Résultat attendu : le champ Matricule est en lecture seule, avec l'aide « Sans objet : un intérimaire n'entre pas dans la paie. » Le bandeau porte Intérim et jamais Non synchronisée paie.
Couverture automatique : e2e/fiche-salarie.spec.ts → « sans matricule paie, le bandeau porte « Non synchronisée paie » », « avec son matricule, la fiche ne porte aucun marqueur provisoire » et « un intérimaire sans matricule porte « Intérim », pas « Non synchronisée paie » ». L'étape 2 n'est couverte par aucun test end-to-end : les specs tournent sans backend, donc la disparition des marqueurs après enregistrement reste à jouer à la main. Côté serveur, backend/tests/test_employee.py couvre la saisie du matricule par PATCH.
9. Enregistrer une attestation d'honorabilité
Tickets : APAJH-201 · Rôle : RH / Admin · Prérequis : un salarié dont la date de naissance est vide — c'est ce champ vide qui déclenchait le refus.
Critères d'acceptation du ticket
- Étant donné une attestation d'honorabilité, quand ses dates et son justificatif sont enregistrés, alors ils sont conservés sans erreur de format de date.
Volet Informations, bloc Attestation d'honorabilité : basculez le commutateur sur Oui, saisissez une Date de remise (passée) et une Date de renouvellement (l'échéance, donc à venir), joignez un PDF, puis Enregistrer.
✅ Résultat attendu : « Fiche enregistrée. » Les deux dates et le nom du fichier sont toujours là après rechargement de la page. En particulier, aucun message « La date n'a pas le bon format » — ce refus venait de la date de naissance vide, réencodée en
""parce que l'envoi partait en JSON au lieu de multipart.Reprenez le champ Justificatif et, cette fois, glissez-déposez le PDF dessus depuis votre explorateur de fichiers au lieu de cliquer sur Joindre un fichier.
✅ Résultat attendu : pendant le survol, le champ se souligne d'un liseré pointillé ; au lâcher, le fichier s'affiche comme s'il avait été choisi par le bouton. Un fichier au mauvais format refusé par le dépôt donne le même message que par le bouton, précédé du nom du fichier — et ne décroche pas celui qui était déjà joint. Le bouton reste disponible : c'est lui la voie clavier.
Rechargez la page, puis cliquez sur Consulter à droite du justificatif.
✅ Résultat attendu : le PDF s’ouvre dans un nouvel onglet via un blob. La requête passe par l’API protégée et porte le JWT ; aucun lien public de stockage n’est utilisé. Sans permission RH et hors dossier personnel, le nom reste visible mais le bouton Consulter est absent.
Cliquez sur la croix du justificatif, puis Enregistrer.
✅ Résultat attendu : « Aucun justificatif ». Les deux dates sont conservées.
Rebasculez le commutateur sur Non, puis Enregistrer.
✅ Résultat attendu : les deux dates et le justificatif sont effacés — la réponse « Non » est une suppression, pas un simple masquage.
Couverture automatique : e2e/fiche-salarie.spec.ts → « le justificatif d'honorabilité part bien en multipart » affirme le format d'envoi et le contenu du corps, qui est là où le bug se logeait, et « le justificatif peut être glissé-déposé sur le champ » affirme que le dépôt de l'étape 2 arrive au même état que le bouton, refus de format compris ; « un fichier refusé n'emporte pas celui déjà joint » affirme qu'un lot dont une seule pièce est refusée garde les autres et nomme la fautive. Côté serveur, backend/tests/test_employee.py couvre l'envoi du fichier, l'effacement d'une date et le détachement du justificatif par null. Les étapes 1 à 5 restent à jouer à la main de bout en bout : les specs tournent sans backend, donc ni la persistance après rechargement ni le lien de l'étape 3 — qui n'existe que servi par la vraie API — ne sont vérifiables automatiquement.
10. Retrouver l'auteur d'une modification de fiche
Tickets : APAJH-189 · Rôle : RH / auditeur · Prérequis : un compte RH autorisé à modifier une fiche et un compte équipe portant la permission audit.view_actionlog
Critères d'acceptation du ticket
- Une écriture faite depuis l'application laisse une trace nominative unique.
- La trace désigne l'objet modifié et montre les valeurs avant et après.
Connectez-vous à l'application avec le compte RH, ouvrez une fiche salarié et modifiez son Adresse, puis cliquez sur Enregistrer.
✅ Résultat attendu : la fiche confirme son enregistrement et affiche la nouvelle adresse.
Connectez-vous au back-office avec le compte auditeur et ouvrez Audit ▸ Journal des actions.
Filtrez sur l'action Modification, le type d'objet salarié et le compte RH utilisé à l'étape 1, puis ouvrez la ligne la plus récente.
✅ Résultat attendu : une seule ligne désigne le salarié modifié, affiche l'identité figée du compte RH et montre l'ancienne et la nouvelle adresse.
Couverture automatique : backend/tests/test_audit.py vérifie la ligne unique, l'acteur, l'objet ciblé et le diff. Le passage visuel entre l'application et le back-office reste à rejouer manuellement.
11. Vérifier les données d'un contrat importé depuis CEGI
Tickets : APAJH-234 · Rôle : RH / Admin · Prérequis : un export CEGI « Personnes Contrats » déposé et traité avec succès
Critères d'acceptation du ticket
- Un fichier déposé est intégré sans action manuelle dans un délai de 15 minutes.
- Un fichier déjà traité n'est pas réimporté.
- Un fichier malformé est rejeté et ne bloque pas les fichiers suivants.
Dans le back-office, ouvrez Imports de données ▸ Lots d'import et repérez le fichier déposé.
✅ Résultat attendu : le lot est Réussi, avec une tentative et un résultat par ligne. Le total des créations, mises à jour et lignes inchangées correspond au nombre de lignes.
Ouvrez dans l'application la fiche d'un salarié de cet export, volet Contrat.
✅ Résultat attendu : le type, la qualification, les dates et les heures hebdomadaires correspondent au fichier. Le mensuel est calculé à partir de l'hebdomadaire × 52 / 12 :
cont_hheb=2800donne 28 h et 121,33 h mensuelles.cont_hmesest absent du nouveau format ; si cette colonne est présente dans un ancien fichier, elle est ignorée.Redéposez le même contenu sous un autre nom, puis attendez le passage suivant.
✅ Résultat attendu : aucun deuxième salarié ni contrat n'est créé. Un seul lot existe pour cette empreinte.
Déposez un fichier dont une adresse email est malformée, puis un fichier valide.
✅ Résultat attendu : le premier lot est Rejeté, avec la ligne et la colonne en erreur ; le second est Réussi.
Déposez un fichier dont les téléphones personnels, l'email, les attributs d'ancienneté, la fonction et la qualification sont vides, avec l'identité, le type de contrat, la date de début, l'hebdomadaire et le dossier de paie renseignés.
✅ Résultat attendu : le fichier est accepté. Les données absentes restent vides. Aucun email ou horaire fictif n'est ajouté. Refaire avec des cellules
NULL, puis sans les colonnes facultatives : même résultat.pers_matppeut manquer ; le libellé du type peut manquer si son code CEGI est déjà référencé. Retirer l'hebdomadaire ou le dossier de paie rejette le fichier.Dans GTA, renseignez le téléphone professionnel, le nombre d'enfants à charge et le motif d'embauche. Déposez un nouveau CSV, puis un ancien CSV contenant des valeurs différentes ou invalides dans
pers_tel1,per_enf,code_embetlib_emb.✅ Résultat attendu : les valeurs GTA sont conservées. Les colonnes retirées
doc_lib,code_pay,cont_sdtcetcont_datfprevsont également ignorées. Le rattachement dépend uniquement dedos_code. Le téléphone professionnel reste modifiable par RH/Admin, en lecture seule pour un lecteur.Importez
pos_lib=Chef de serviceetlib_qc=Cadre classe 2, puis refaites avecpos_libvide ouNULL, puis les deux libellés vides. Vérifiez le volet Contrat sur ordinateur et mobile.✅ Résultat attendu : Qualification affiche successivement « Chef de service », « Cadre classe 2 », puis rien. Les codes sources restent distincts.
Modifiez
pers_dnaiet laissezcont_datfvide, avec une ancienne colonnecont_datfprevrenseignée.✅ Résultat attendu : la date de naissance est mise à jour ; aucune date de fin n'est importée depuis la date prévisionnelle. L'import séparé des enfants n'est pas encore disponible.
Couverture automatique : backend/tests/test_data_imports.py couvre le parsing UTF-8/Windows-1252, les mappings, l'atomicité, l'idempotence, la poursuite après rejet, la reprise et les durées de conservation. Le scénario « qualification importée et téléphone pro géré dans GTA » de e2e/fiche-salarie.spec.ts couvre l'affichage et les droits sur ordinateur et mobile. L'étape 2 reste à jouer avec un export réel pour confronter l'affichage au fichier source.
12. Vérifier le masquage du n° de sécurité sociale
Tickets : APAJH-209 · Rôle : Encadrant, puis RH / Admin · Prérequis : un salarié dont le n° de sécurité sociale est renseigné (la saisie se fait en RH / Admin, au scénario 3 ou à la création)
Même dispositif que le scénario 4, sur un champ de plus : le numéro est un identifiant national, il n'est visible que des rôles Admin, RH n1 et RH n2. Un DA et les deux rangs de Manager ouvrent toutes les fiches pour planifier des personnes, pas pour administrer leur dossier.
Avec le profil Encadrant, ouvrez le volet Informations.
✅ Résultat attendu : le bloc Identité ne comporte aucune ligne « N° sécurité sociale ». Pas un champ vide ni cadenassé : la ligne est absente. Le reste du bloc — civilité, nationalité, matricule — est intact.
Affichez le code source de la page et cherchez le numéro.
✅ Résultat attendu : il est absent de la page entière. C'est l'API qui retire le champ avant de répondre, comme pour les enfants.
Rouvrez le même volet en RH / Admin.
✅ Résultat attendu : la ligne « N° sécurité sociale » est là, renseignée, et cadenassée comme les autres champs venant de la paie.
Couverture automatique : e2e/fiche-salarie.spec.ts → « un rôle manager n'en voit pas la trace » et « un rôle RH le lit, cadenassé comme les autres champs de la paie ». Côté serveur, le masquage et le refus en écriture sont couverts par backend/tests/test_employee.py.
13. Attacher une qualification à un salarié
Tickets : APAJH-202 · Rôle : RH / Admin, puis Encadrant · Prérequis : make seed-workforce
Critères d'acceptation du ticket
- Depuis l'onglet Informations, RH peut rattacher une ou plusieurs qualifications (référentiel « IDE, AS, AES… ») à un salarié.
- Le référentiel des qualifications se tient depuis le back-office.
Avec le profil RH / Admin, ouvrez l'onglet Informations d'une fiche et faites défiler jusqu'au bloc Qualifications, en bas de l'onglet.
✅ Résultat attendu : un champ de recherche à sélection multiple, comme celui des lieux de travail secondaires du volet Lieux de travail & équipes.
Ajoutez deux qualifications (par exemple Infirmier(ère) diplômé(e) d'État (IDE) et Aide-soignant(e) (AS)), puis Enregistrer.
✅ Résultat attendu : « Fiche enregistrée. » Les deux qualifications restent affichées après rechargement de la page.
Rejouez la même fiche avec le profil Encadrant.
✅ Résultat attendu : les qualifications s'affichent comme de simples étiquettes, sans croix de retrait ni champ de recherche, et il n'y a pas de bouton Enregistrer — même politique d'écriture que le reste de l'onglet Informations.
Couverture automatique : e2e/fiche-salarie.spec.ts → « RH peut rattacher des qualifications à un salarié » et « un rôle manager voit les qualifications comme de simples étiquettes ». Côté serveur, backend/tests/test_skills.py couvre le référentiel et backend/tests/test_employee.py le rattachement à un salarié et son verrouillage hors RH/Admin. La persistance après rechargement (étape 2) reste à jouer à la main : les specs tournent sans backend, comme le reste de ce document.
14. Reprendre et rejouer l'acquisition des compteurs de base
Tickets : APAJH-55 · Rôle : RH / Admin · Prérequis : un salarié avec un contrat couvrant une partie de l'année 2026 ; moteur planifié désactivé
Lancez
make acquire-counters ARGS="--from 2026-01-01 --as-of 2026-08-20 --dry-run".✅ Résultat attendu : la commande détaille les mouvements par salarié et par période, affiche une synthèse, puis indique Prévisualisation annulée. Aucun compteur ni audit supplémentaire ne subsiste.
Relancez la même commande sans
--dry-run, puis ouvrez le volet Compteurs du salarié.✅ Résultat attendu : seuls les CT ont été crédités par le moteur local. Aucun droit CP ou ANC n'est calculé. L'objectif automatique d'un contrat annualisé hors forfait jours attend le droit CA définitif de la paie. Dans le journal d'audit, les écritures CT portent Système.
Corrigez manuellement la valeur Acquis d'un CT, enregistrez, puis rejouez exactement la même plage.
✅ Résultat attendu : aucune seconde acquisition n'est ajoutée et la correction reste inchangée. Une période ultérieure ajoutera seulement son nouveau delta.
Contrôlez un CT comportant une absence.
✅ Résultat attendu : le montant est le droit théorique issu des jours de contrat. Aucun retrait d'absence n'est attendu dans cette phase.
Couverture automatique : backend/tests/test_counter_acquisition.py couvre les périodes, seuils, prorata, années cibles, reprises, corrections, audits et rollback. backend/tests/test_counters.py couvre l'audit des corrections API. Aucun test Playwright n'est ajouté : le volet et son contrat HTTP ne changent pas.
15. Alerter la RH du renouvellement d'une attestation d'honorabilité
Tickets : APAJH-236 · Rôle : RH n1 / RH n2 · Prérequis : au moins un compte avec le rôle RH n1 ou RH n2 actif ; un salarié dont la date de renouvellement de l'attestation d'honorabilité (à défaut, sa date de remise) tombe exactement dans 60, 30 ou 15 jours — cette date fait office d'échéance, pas d'historique : c'est la date à laquelle l'attestation doit être renouvelée.
Critères d'acceptation du ticket
- Étant donné un salarié dont l'attestation échoit dans exactement 60, 30 ou 15 jours, quand la tâche quotidienne s'exécute, alors chaque compte RH reçoit une notification (persistante, push et email) nommant le salarié et la date d'échéance.
- Étant donné plusieurs salariés dont l'attestation échoit à la même date, quand la tâche s'exécute, alors une seule notification regroupe tous les noms concernés.
- Étant donné un salarié dont l'attestation échoit dans 55 jours (ni 60, ni 30, ni 15), quand la tâche s'exécute, alors ce salarié n'est notifié pour aucun seuil.
Sur le volet Informations d'un salarié, réglez la Date de renouvellement de l'attestation d'honorabilité à aujourd'hui + 15 jours, puis lancez
make send-honorability-renewal-alerts.✅ Résultat attendu : la commande affiche « 1 groupe(s) d'échéance, 1 salarié(s) concerné(s), N notification(s) RH envoyée(s) ». Chaque compte RH n1/RH n2 voit une nouvelle notification dans sa cloche, citant le salarié et l'échéance, et reçoit un courriel équivalent (
make logsen local, le backend d'email est la console).Réglez un second salarié à la même date, relancez la commande.
✅ Résultat attendu : une seule notification supplémentaire par compte RH, listant les deux salariés — pas une par salarié.
Relancez la commande une seconde fois sans rien changer.
✅ Résultat attendu : « 0 groupe(s) d'échéance ». Aucune notification ni courriel supplémentaire — la tâche est idempotente pour la même journée.
Couverture automatique : backend/tests/test_honorability_alerts.py couvre le calcul de l'échéance (priorité à la date de renouvellement, repli sur la date de remise + durée par défaut administrable), la correspondance stricte sur 60/30/15 jours, le regroupement, le ciblage RH n1/RH n2 (rôle actif uniquement) et l'idempotence d'une même journée. Aucun test Playwright n'est ajouté : la notification apparaît dans la cloche déjà couverte ailleurs, et la tâche elle-même n'a pas d'écran propre.
16. Vérifier les contrôles de format de l'identité et des coordonnées
Tickets : APAJH-246 · Rôle : RH / Admin · Prérequis : un salarié dont le n° de sécurité sociale est renseigné (la saisie se fait en RH / Admin, au scénario 3 ou à la création)
Les formats sont contrôlés avant l'appel à l'API : le message s'affiche sous le champ fautif et l'enregistrement n'est pas tenté. Les mêmes règles existent côté serveur — le formulaire en est un miroir, pas un remplacement — donc un appel direct à l'API est refusé de la même façon.
Le n° de sécurité sociale est cadenassé sur ce volet. Ce qui s'y contrôle, c'est la date de naissance qu'il faut lui accorder ; la saisie du numéro lui-même est au scénario 12 de Annuaire Utilisateurs.
Bloc Coordonnées : remplacez Téléphone perso par
06 12, puis Enregistrer.✅ Résultat attendu : sous le champ, « Le numéro de téléphone doit comporter au moins 10 chiffres, éventuellement séparés par des espaces, des points ou des tirets. » Aucun enregistrement — ni message « Fiche enregistrée. », ni valeur modifiée après rechargement de la page.
Rétablissez le téléphone, puis saisissez
karim.mokrani— sans arobase — dans Email perso. Enregistrer.✅ Résultat attendu : « Saisissez une adresse e-mail valide. », en français, sous le champ, comme tous les autres messages. Si vous voyez à la place une bulle grise au-dessus du champ, éventuellement en anglais, c'est la validation native du navigateur qui a reparlé :
noValidatea disparu d'un des deux formulaires.Rétablissez une adresse valide. Bloc Identité : changez l'année de la Date de naissance sans toucher au mois, puis Enregistrer.
✅ Résultat attendu : « Les chiffres 2 et 3 du n° de sécurité sociale ne correspondent pas à l'année de naissance. » Ces deux chiffres du numéro encodent l'année de naissance ; comme le volet ne peut corriger que la date, c'est elle qui porte le message.
Corrigez la date — un autre jour du même mois de la même année, pour que la fiche reste modifiée et donc enregistrable.
✅ Résultat attendu : le message disparaît de lui-même, sans nouveau clic sur Enregistrer, et l'enregistrement aboutit.
Saisissez une Date de naissance située il y a moins de 16 ans.
✅ Résultat attendu : « Le salarié doit avoir au moins 16 ans. » C'est le seul message affiché, alors que cette date contredit aussi le numéro : une règle de champ est signalée avant les règles croisées.
Bloc Identité : décochez Non applicable, saisissez
10100654321dans N° RPPS, puis Enregistrer.✅ Résultat attendu : « La clé du n° RPPS est incorrecte. » Un n° RPPS compte 11 chiffres fermés par une clé de Luhn.
Remplacez-le par
10100654325, recochez Non applicable, puis Enregistrer.✅ Résultat attendu : « Un n° RPPS ne peut pas être renseigné quand le RPPS est déclaré non applicable. » Cocher la case gèle le champ sans le vider : le numéro saisi juste avant est toujours dans le formulaire, et c'est exactement ce que cette règle rattrape.
Couverture automatique : e2e/fiche-salarie.spec.ts → les scénarios du groupe « saisies refusées (APAJH-246) ». Côté serveur, backend/tests/test_employee.py couvre chaque règle à ses deux frontières — le validateur de modèle, dont héritent l'API et le back-office, et les règles croisées appelées par le sérialiseur comme par le formulaire d'administration. La persistance après rechargement reste à jouer à la main : les specs tournent sans backend.
17. Vérifier les dates de l'attestation d'honorabilité et son justificatif
Tickets : APAJH-246 · Rôle : RH / Admin · Prérequis : aucun
Complète le scénario 9, qui enregistre une attestation valable ; ici on vérifie ce qui est refusé. Les deux dates ne portent pas la même règle, et c'est le point à contrôler.
C'est aussi ce qui rend le scénario 15 possible : l'alerte de renouvellement lit la date de renouvellement comme une échéance, donc cette date doit pouvoir être saisie dans le futur.
Bloc Attestation d'honorabilité sur Oui. Date de remise : hier. Date de renouvellement : le mois dernier. Puis Enregistrer.
✅ Résultat attendu : « La date de renouvellement ne peut pas précéder la date de remise. »
Portez la Date de renouvellement à trois ans d'ici, puis Enregistrer.
✅ Résultat attendu : l'enregistrement aboutit. Une date de renouvellement est l'échéance à laquelle le contrôle doit être refait : elle est normalement à venir, et rien ne la borne.
Saisissez une Date de remise dans le futur.
✅ Résultat attendu : « La date ne peut pas être future. » Une remise est un événement passé — l'attestation a été remise —, alors qu'une échéance ne l'est jamais. C'est pourquoi une seule des deux dates est bornée.
Joignez un fichier
.txtau Justificatif, puis un PDF de plus de 10 Mo.✅ Résultat attendu : « Le justificatif doit être un fichier PDF, JPG ou PNG. », puis « Le justificatif ne doit pas dépasser 10 Mo. » Les deux s'affichent dès le choix du fichier, sans clic sur Enregistrer : c'est la seule valeur de la fiche que personne ne corrige en la retapant, et un envoi de 10 Mo refusé après coup serait un aller-retour pour rien.
Couverture automatique : e2e/fiche-salarie.spec.ts → « une échéance d'honorabilité antérieure à la remise est refusée », « une échéance d'honorabilité dans le futur est acceptée » et « un justificatif au mauvais format ou trop lourd est refusé dès qu'il est choisi ». Côté serveur, backend/tests/test_employee.py.
18. Vérifier les contrôles de format des volets Contrat, Famille et Urgence
Tickets : APAJH-246 · Rôle : RH / Admin, puis Encadrant pour l'étape 3 · Prérequis : un salarié ayant au moins un enfant et une personne à prévenir
Volet Contrat : portez Dimanches & jours fériés prévisionnels à
64, puis Enregistrer.✅ Résultat attendu : « Le nombre de dimanches et jours fériés ne peut pas dépasser 63. » — 52 dimanches et une dizaine de jours fériés.
Volet Famille & social : donnez à un enfant une Date de naissance fixée à demain, puis Enregistrer.
✅ Résultat attendu : « La date ne peut pas être future. », sous la ligne de cet enfant. Le volet renvoie toute la liste à chaque enregistrement, donc le message désigne sa ligne. Il n'y a aucune borne d'âge haute : un enfant qui grandit cesse d'être compté, il ne rend pas la fiche non enregistrable.
Volet Urgence : remplacez le Téléphone d'une personne à prévenir par
06 12, puis Enregistrer.✅ Résultat attendu : le même message de téléphone qu'au scénario 16, sous la ligne de cette personne. Autre téléphone est le seul champ optionnel du bloc : une personne à prévenir qu'on ne peut pas joindre n'est pas un contact.
Rejouez l'étape 1 sur un téléphone (viewport 390 px de large).
✅ Résultat attendu : le message revient à la ligne sous le champ, entièrement lisible, et la page ne défile pas latéralement.
Couverture automatique : e2e/fiche-salarie.spec.ts → « plus de 63 dimanches et jours fériés est refusé sur le volet Contrat », « un enfant né dans le futur est refusé, à sa ligne », « une personne à prévenir sans téléphone joignable est refusée, à sa ligne » et, pour l'étape 4, « un refus de format reste lisible sous son champ (APAJH-246) ».
16. Afficher le catalogue extensible de compteurs
Tickets : APAJH-55.1 · Rôle : RH / Admin, puis Manager · Prérequis : make seed-all
Préparation commune aux scénarios 15 à 17
Démarrez l'environnement avec
docker compose up -d.Exécutez
make seed-allpour rejouer les sources puis les acquisitions et consolidations réelles.Choisissez un salarié ayant un contrat en 2026 et notez son identifiant.
Gardez ouverts le back-office et
/utilisateurs/<identifiant>/compteurs, avec l'année 2026 sélectionnée.✅ Résultat attendu : le référentiel contient 6 catégories, 52 motifs et 15 raccords absence-compteur. La fiche salarié se recharge sans erreur avant de commencer les vérifications métier.
Critères d'acceptation du ticket
- La fiche ordonne les compteurs d'après le catalogue administrable : Temps de travail, Congés, Compteurs en heures, puis Autres.
- La section Autres suit l'ordre du catalogue pour les droits attribués au salarié.
- Chaque ligne d'un compteur mensuel indique le mois et l'année de sa période.
- Un type archivé reste visible quand une ligne historique le référence.
- Le Manager lit les mêmes valeurs sans pouvoir les modifier.
En RH / Admin, ouvrez Utilisateurs, une fiche salarié, puis le volet Compteurs.
✅ Résultat attendu : les quatre sections sont visibles. Les libellés, unités et l'ordre correspondent au catalogue ; CET et annualisation gardent leur présentation spécialisée.
Dans le back-office, consultez une année contenant une ancienne ligne EM, sans ligne AEH. Rechargez le volet.
✅ Résultat attendu : l'historique EM reste affiché même si son type est archivé. AEH n'est pas affiché : l'absence de ligne signifie qu'il n'est pas applicable au salarié.
Dans le back-office, créez deux lignes DLG_CSE pour janvier et février de la même année, puis rechargez le volet.
✅ Résultat attendu : les deux lignes affichent respectivement le mois et l'année de janvier et de février ; elles ne sont plus visuellement ambiguës.
Ouvrez la même fiche avec le rôle Manager, sur ordinateur puis avec une largeur de téléphone.
✅ Résultat attendu : la section Autres reste lisible sans débordement et aucun bouton Éditer ou Enregistrer n'est proposé.
Rechargez une année où une ligne EM historique existe et une année où elle n'existe pas.
✅ Résultat attendu : l'historique EM reste visible dans la première année et aucune nouvelle ligne EM vide n'est proposée dans la seconde.
Couverture automatique : e2e/fiche-salarie.spec.ts couvre l'ordre, les mois et la lecture Manager. backend/tests/test_counters.py couvre le catalogue, les archives, l'ordre, les périodes mensuelles et la compatibilité GET/PUT ; backend/tests/test_counter_catalog_migration.py couvre la conservation des données historiques.
17. Attribuer, ouvrir et valider les nouveaux compteurs
Tickets : APAJH-55.2 · Rôle : RH / Admin, puis Manager · Prérequis : un salarié avec un contrat en 2026, au moins un enfant de moins de 16 ans et les types de compteurs actifs
Critères d'acceptation du ticket
- Un salarié ne voit que ses droits applicables ; un droit applicable mais non ouvert est explicite et n'est pas présenté comme un zéro.
- Un droit en attente distingue Acquis de Restant utilisable.
- Seuls Admin, RH n1 et RH n2 peuvent valider ou refuser.
- Les auteurs, dates et motifs sont audités ; une décision concurrente ne peut pas être écrasée.
- JRS est attribué en une fois à hauteur de 24 jours pour une année complète, ou au prorata arrondi au jour supérieur en cas d'entrée/sortie.
- DLG_CSE, DLG_DS et DLG_CS sont crédités automatiquement depuis des mandats RH datés, sans validation N+1.
Lancez l'acquisition au 1er janvier 2026 pour un salarié ayant un enfant de moins de 16 ans, puis ouvrez
/utilisateurs/<id>/compteurset sélectionnez 2026.✅ Résultat attendu : EM_H — Heures pour enfant malade est Ouvert et son montant utilisable correspond à la règle familiale et au prorata du temps de travail. Aucun ancien EM ou AEH sans historique n'est inventé.
Avec Éditer, attribuez AID avec un montant
0et un motif.✅ Résultat attendu : la ligne reste visible avec l'état Applicable, non ouvert. Acquis et Restant affichent
—, pas0 h.Cliquez sur Débloquer, saisissez
21et un motif d'ouverture.✅ Résultat attendu : AID passe En attente de validation. Acquis vaut
21 h, Restant utilisable vaut0 h.Connectez-vous successivement en Manager puis en RH. Essayez de prendre une décision, puis validez en RH.
✅ Résultat attendu : le Manager ne dispose d'aucune action. Admin, RH n1 ou RH n2 peut valider ; l'état devient Validé et Restant vaut
21 h.Depuis deux sessions RH ouvertes sur la même version, validez dans la première puis tentez un refus dans la seconde.
✅ Résultat attendu : la seconde action reçoit un conflit et invite à recharger. La validation, son auteur et sa date restent inchangés.
Dans le back-office, créez successivement des mandats CSE titulaire, DS et conseiller du salarié couvrant mars 2026. Relancez l'acquisition.
✅ Résultat attendu : mars affiche trois lignes ouvertes automatiquement : DLG_CSE à
24 h, DLG_DS à18 het DLG_CS à15 h. Aucune décision N+1 n'est proposée.Pour un contrat au forfait jours couvrant toute l'année, relancez l'acquisition ; recommencez avec une entrée au 1er juillet.
✅ Résultat attendu : le premier JRS vaut
24 j. Le second est proratisé sur les jours calendaires restants puis arrondi au jour supérieur. Une relance identique ne crédite pas une seconde fois.Corrigez un montant EM_H avec un motif, puis relancez l'acquisition après un changement de situation familiale donnant droit à un complément.
✅ Résultat attendu : la correction est tracée avec avant/après, auteur, date et motif. Le journal automatique antérieur demeure ; seul le complément positif est ajouté.
Rejouez les états Ouvert, Applicable, non ouvert et En attente de validation sur ordinateur puis à largeur de téléphone.
✅ Résultat attendu : les trois états, les montants acquis/utilisables et les mois restent lisibles sans débordement horizontal.
Créez un partage CSE de
4 hvers un autre titulaire, avec une information de l'employeur huit jours avant la date prévue. Créez ensuite un report vers un mois ultérieur.✅ Résultat attendu : les heures quittent le mois source et créditent le bénéficiaire sans validation. Un délai inférieur à huit jours, un report à plus de douze mois ou un dépassement du plafond du bénéficiaire est refusé.
Annulez l'opération depuis le back-office.
✅ Résultat attendu : une nouvelle version compense exactement le débit et le crédit. L'opération et ses mouvements restent consultables.
Couverture automatique : backend/tests/test_counter_entitlements.py couvre attribution, déblocage, validation, refus, expiration, changement de situation, permissions, concurrence, audit et maintien du journal automatique. backend/tests/test_delegation_counters.py couvre les crédits par mandat, le cumul, les reports, les partages, les plafonds et les compensations. e2e/fiche-salarie.spec.ts couvre les trois états et les crédits DLG automatiques sur ordinateur et mobile.
18. Relier une absence et le temps réalisé aux compteurs
Tickets : APAJH-55.3 · Rôle : RH / Admin, puis Manager · Prérequis : un salarié avec CP et EM_H ouverts, et les types d'absence canoniques
Critères d'acceptation du ticket
- une absence future réserve Planifiés, puis passe intégralement dans Pris à sa date de début ;
- acquis, planifiés, pris et restant sont distincts, y compris pour EM_H/AID et le CET ;
- une modification, un refus ou une annulation compense la version précédente sans effacer une correction RH ;
- une unité incompatible, un solde insuffisant ou un segment sans période annule l'opération entière ;
- le temps réalisé alimente l'annualisation sans compter deux fois une journée couverte par une absence.
Créez une absence CP de deux jours dans le futur, puis rechargez la fiche salarié, volet Compteurs.
✅ Résultat attendu : CP conserve son Acquis, augmente Planifiés de
2 j, ne change pas Pris et recalcule Restant.Lancez
docker compose exec backend python manage.py sync_absence_counters --as-of <premier-jour> --promote-only.✅ Résultat attendu : les
2 jquittent entièrement Planifiés et apparaissent dans Pris. Une seconde exécution ne change rien.Modifiez ensuite l'absence pour ne conserver que le premier jour, comme si le salarié avait été rappelé au travail le deuxième jour. Rechargez la fiche et relancez la commande de l'étape 2.
✅ Résultat attendu : la version de l'absence augmente. Le journal compense les
2 jprécédemment pris, puis applique1 jpris. Le résultat net est Pris1 jet le deuxième jour est restitué dans Restant. La relance n'ajoute aucun mouvement.Créez une absence Enfant malade de deux heures, puis une absence AEH/Aidant sur un salarié auquel AID est attribué.
✅ Résultat attendu : EM_H puis AID affichent séparément Acquis, Planifiés, Pris et Restant. Un AID non ouvert reste à
—, jamais à un faux zéro.Modifiez de nouveau la durée de l'absence CP, puis annulez-la.
✅ Résultat attendu : la version augmente ; l'ancien delta est compensé, le nouveau appliqué, puis l'annulation restitue exactement le droit. Les événements et mouvements restent consultables au back-office.
Rejouez les étapes 1 et 4 sur ordinateur et à largeur de téléphone.
✅ Résultat attendu : les quatre figures des lignes CP et EM_H/AID restent lisibles sans débordement horizontal.
Sur un salarié annualisé, injectez une journée réalisée à
9 hpour7 hthéoriques, puis une absence rémunérée et une non rémunérée de7 h.✅ Résultat attendu : le réalisé ajoute
2 hen Heures + ; l'absence rémunérée écrit deux traces opposées et reste nette à zéro ; la non rémunérée ajoute7 hen Heures −. Un événement de temps sur la journée absente n'ajoute rien.
Couverture automatique : backend/tests/test_counter_integration.py couvre versions, idempotence, priorité, unités, demi-journées, périodes multiples, insuffisance, rollback, CET, RC opt-in et annualisation ; backend/tests/test_counter_integration_migration.py couvre les données existantes. e2e/fiche-salarie.spec.ts couvre CP et EM_H sur ordinateur et mobile.
19. Activer les droits de délégation d'un salarié
Tickets : APAJH-195 · Rôle : RH / Admin, puis Manager · Prérequis : un salarié sans mandat CSE
Volet Contrat, cochez Droits de délégation, puis Enregistrer.
✅ Résultat attendu : la case reste cochée après rechargement de la fiche. Côté serveur, cette coche ouvre le mandat de titulaire CSE du salarié (
apps.counters.RepresentativeMandate) et crédite24 hsur le mois en cours.Rechargez la fiche, volet Compteurs.
✅ Résultat attendu : « Heures de délégation CSE » apparaît dans le bloc Autres, avec
24 hen Acquis pour le mois courant — le même compteurDLG_CSEque les mandats saisis en back-office.Décochez Droits de délégation, puis Enregistrer.
✅ Résultat attendu : le mandat de titulaire CSE se clôture ; le mois déjà crédité conserve ses
24 h, aucun mois suivant n'en reçoit de nouveaux.Ouvrez la même fiche avec un profil Manager.
✅ Résultat attendu : la case est visible mais grisée, comme le reste du volet Contrat — pas de bouton Enregistrer.
Couverture automatique : backend/tests/test_employee.py (persistance du champ, refus pour un rôle manager) et backend/tests/test_delegation_counters.py (ouverture/clôture du mandat, crédit mensuel, idempotence, conflit avec un rôle CSE saisi en admin) ; e2e/fiche-salarie.spec.ts → « un rôle RH active les droits de délégation » et « un rôle manager voit le contrat sans pouvoir le modifier ».
20. Retrouver l'annualisation actualisée dans Compteurs
Tickets : APAJH-61, APAJH-81
Rôle : salarié pour sa fiche, manager ou RH pour les dossiers autorisés.
Ouvrez la fiche puis Compteurs sur un salarié annualisé.
Depuis une autre consultation, validez ou annulez une absence par l'API.
Revenez à Compteurs dans les 30 secondes, puis au relevé Annualisation.
✅ Les deux écrans relisent les soldes sans rechargement manuel. Le seuil reste inchangé ; les heures positives/négatives et le solde correspondent à la même date d'arrêté. Les jours futurs restent prévus et ne sont pas encore comptés.
Rejouez sur téléphone et après un retour au premier plan du navigateur.
✅ Les valeurs actualisées restent lisibles. Aucun bouton de recalcul n'est nécessaire. Un seuil absent garde son état non initialisé.
Voir aussi la recette Annualisation. Les tests serveur couvrent la cohérence des trois réponses ; les tests navigateur contrôlent la restitution.
21. Activer ou désactiver un compte
Tickets : APAJH-175
Rôle : RH / Admin pour tous les salariés ; DA, Manager n1 ou Manager n2 pour les intérimaires seulement ; un compte avec Statut équipe pour le back-office.
Prérequis : make seed-all, une fiche en CDI et une fiche d'intérimaire (badge « Intérim » dans le bandeau).
En RH, ouvrez la fiche en CDI et décochez Compte actif, au-dessus du bandeau.
✅ La case se décoche sans passer par « Enregistrer », et le bandeau affiche « Compte désactivé ». Le salarié ne peut plus se connecter. Ses compteurs restent lisibles dans Compteurs, mais le calcul automatique ne les alimente plus.
Recochez Compte actif.
✅ Le badge disparaît. Au prochain calcul, les compteurs rattrapent aussi la période pendant laquelle le compte était désactivé : ce sont les contrats qui décident des droits acquis, pas le compte.
Connectez-vous en
Manager n1, puis ouvrez la fiche de l'intérimaire et celle du salarié en CDI.✅ La case n'apparaît que sur la fiche de l'intérimaire. Elle reste proposée après la fin de sa mission, pour pouvoir le réactiver. Sur votre propre fiche, personne ne voit la case.
Dans le back-office, ouvrez Utilisateurs. Cochez plusieurs comptes, dont le vôtre, puis lancez Désactiver les comptes sélectionnés.
✅ La colonne Actif passe à « non » pour les comptes choisis, sauf pour le vôtre : un avertissement dit qu'il a été épargné. Activer les comptes sélectionnés fait l'inverse. Chaque changement, qu'il vienne de la fiche ou du back-office, apparaît dans le journal des actions sur le compte.
Rejouez les étapes 1 et 3 sur un téléphone.
✅ La case reste lisible et cliquable au-dessus du bandeau.
Couverture automatique : backend/tests/test_account_activation.py (qui peut agir sur quel compte, trace, actions de masse, acquisition suspendue) ; e2e/fiche-salarie.spec.ts → « fiche salarié — compte actif », sur ordinateur et sur mobile.
22. Modifier une fiche en RH sans Statut équipe
Tickets : APAJH-191
Rôle : un compte RH n1 sans Statut équipe, puis un compte Manager n1avec Statut équipe.
Prérequis : make seed-all. Dans le back-office, sur deux comptes de test, donnez le rôle RH n1 au premier en laissant Statut équipe décoché ; donnez le rôle Manager n1 au second et cochez son Statut équipe.
Connectez-vous à l'application avec le compte
RH n1, ouvrez la fiche d'un salarié, puis l'onglet Informations.✅ Résultat attendu : les champs sont modifiables, y compris l'attestation d'honorabilité, et Enregistrer est présent.
Changez la date de délivrance de l'attestation d'honorabilité, puis enregistrez.
✅ Résultat attendu : « Fiche enregistrée. » s'affiche et la date reste après rechargement de la page.
Ouvrez Famille & social, Contrat puis Compteurs.
✅ Résultat attendu : les trois volets sont modifiables (le bouton Éditer est proposé dans Compteurs).
Connectez-vous avec le compte
Manager n1, ouvrez la même fiche puis Informations.✅ Résultat attendu : les champs sont en lecture seule et aucun bouton Enregistrer n'apparaît, malgré le Statut équipe. Les volets Urgence et Lieux de travail & équipes restent modifiables.
Rejouez l'étape 1 sur un téléphone.
✅ Résultat attendu : même comportement, les champs sont modifiables.
Couverture automatique : backend/tests/test_employee.py (écriture du dossier par le rôle et non par is_staff) ; backend/tests/test_auth.py (can_edit_employee_record de /api/auth/me/) ; e2e/fiche-salarie.spec.ts, dont les profils RH et encadrant portent désormais ce drapeau.
23. Désactivation automatique à la fin du contrat
Tickets : APAJH-213
Rôle : RH / Admin avec Statut équipe pour le back-office, puis Manager n1 pour le planning.
Prérequis : make seed-all, une fiche en CDD dont le compte est actif, et une seconde fiche sans aucun contrat.
Dans le back-office, ouvrez le contrat de la fiche en CDD et fixez sa date de fin à hier. Lancez
make close-ended-accounts.✅ La commande annonce « 1 compte(s) désactivé(s) ». Sur la fiche, Compte actif est décoché et le bandeau affiche « Compte désactivé ». Le journal des actions du compte porte l'entrée au nom de « Système ». La fiche sans contrat garde son compte actif.
Essayez de vous connecter avec ce salarié.
✅ La connexion est refusée. Une session déjà ouverte à son nom est renvoyée vers la page de connexion à la requête suivante.
Relancez
make close-ended-accounts.✅ « 0 compte(s) désactivé(s) » : la tâche ne refait rien. Elle tourne aussi seule chaque nuit, après l'import CEGI.
Recochez Compte actif sans saisir de nouveau contrat, puis relancez la commande.
✅ Le compte est de nouveau désactivé : sans contrat en cours ni à venir, la réouverture ne tient pas. Donnez-lui un contrat qui commence le mois prochain et recochez la case : la commande ne le referme plus.
Sur une autre fiche, dont le contrat court toujours, décochez Compte actif. Connectez-vous en
Manager n1, ouvrez Planning sur une semaine où ce salarié est affiché, et cliquez une case Poste à pourvoir.✅ Le salarié n'apparaît pas dans la recherche. Sur sa propre ligne, Modifier la journée et Affecter un poste échouent avec « Le compte de ce salarié est désactivé : il ne peut plus être planifié. ». L'assistant Planification refuse de lui affecter une trame avec le même message.
Rejouez l'étape 5 sur un téléphone.
✅ Le salarié reste absent de la recherche et le même message s'affiche.
Couverture automatique : backend/tests/test_account_auto_closing.py (qui est fermé, qui est épargné, convergence, trace, commande) ; backend/tests/test_auth.py (connexion et jeton d'un compte fermé) ; backend/tests/test_planning_override.py, backend/tests/test_planning.py, backend/tests/test_planning_calendar.py, backend/tests/test_employee.py (refus d'affectation et liste « Poste à pourvoir »).
24. Consulter son profil personnel en lecture seule
Tickets : APAJH-290 · Rôle : Salarié, Intérimaire, Manager, RH / Admin · Prérequis : un compte lié à un dossier comprenant contrat, enfant, personne à prévenir et équipe ; un second compte sans dossier pour l'état vide.
Critères d'acceptation
- Mon profil reprend les volets existants Informations, Contrat, Famille & social, Urgence, Lieux de travail & équipes.
- Le dossier ouvert est exclusivement celui du compte connecté, déterminé par le profil d'authentification ; aucun identifiant de salarié n'est dans l'URL.
- Tous les volets restent en lecture seule, même pour les RH et administrateurs.
- Le bandeau présente identité, fonction, établissement, contrat, quotité et ancienneté. Les compteurs restent sur leur page dédiée : aucun chiffre ni appel de compteurs n'est ajouté au profil.
Ouvrez Mon profil depuis le menu de compte sur ordinateur ou la puce utilisateur du tiroir sur téléphone.
✅ Résultat attendu : le bandeau décrit votre dossier et le rail propose exactement les cinq volets. Le tiroir mobile se referme.
Parcourez les cinq volets, rechargez la page et utilisez le retour du navigateur.
✅ Résultat attendu : le volet suit l'adresse
/mon-profil/<volet>. Les données reprennent la fiche existante, sans Enregistrer, Annuler, ajout, suppression, remplacement de pièce ou contrôle modifiable. Appuyer sur Entrée dans un champ n'enregistre rien. Le document reste consultable selon les droits existants.Rejouez avec un compte Manager puis RH / Admin, en largeur bureau et en 390 × 844.
✅ Résultat attendu : leur propre profil reste en lecture seule. Les droits de modification de la fiche manager sont conservés sur son parcours habituel. Les volets sont accessibles sans débordement horizontal de la page.
Avec un compte Salarié, consultez Informations puis Famille & social.
✅ Résultat attendu : le n° de sécurité sociale est absent ; seuls les âges des enfants sont restitués lorsque l'API masque leurs détails. Le profil n'élargit aucune permission de lecture.
Ouvrez le profil d'un compte sans dossier, puis un dossier sans contrat, enfant, contact ou équipe. Simulez enfin une erreur de lecture et rechargez.
✅ Résultat attendu : chaque absence de données est explicitée. Un compte sans dossier est invité à contacter les ressources humaines. Une erreur API affiche un message demandant de recharger, sans dossier fictif.
Consultez des données complètes en session RH, déconnectez-vous puis connectez-vous comme salarié. Rejouez avec Assumer l'identité depuis l'administration.
✅ Résultat attendu : seules les données autorisées à la nouvelle session apparaissent, y compris si une lecture RH était encore en cours.
Couverture automatique : e2e/personal-profile.spec.ts vérifie les cinq volets sur bureau et téléphone pour les quatre catégories de comptes, l'absence d'écritures, de lectures de compteurs et de dossier tiers, les états vides, l'erreur et la reconnexion. e2e/personal-profile-session.spec.ts vérifie le changement d'identité avec une réponse RH en cache ou retardée. Ces tests utilisent des réponses API simulées ; backend/tests/test_employee.py et backend/tests/test_roles.py couvrent les permissions serveur existantes.
25. Protéger le droit paie dans le volet Compteurs
Tickets : APAJH-66, APAJH-282 · Rôle : RH et gestionnaire.
Initialiser un acquis CA de 34 jours sur une période ayant déjà 5 jours pris et 3 planifiés. Ouvrir la fiche, puis Compteurs, sur téléphone et ordinateur.
✅ Une seule ligne Congés annuels affiche 34 acquis et 26 restants, avec la provenance et la date paie. Les consommations sont conservées.
Cliquer sur Éditer, modifier un autre compteur puis enregistrer.
✅ Acquis, pris et planifiés CA restent en lecture seule. La sauvegarde globale conserve le droit paie. Une écriture directe API ou admin est refusée.
Déposer le cumul mensuel de l'année suivante, puis le même total marqué
final.✅ L'acquisition suivante est datée et indicative jusqu'à finalisation. Son passage à définitif ne produit aucun double crédit ni nouveau débit.
Couverture automatique : e2e/fiche-salarie.spec.ts → « acquis paie non éditable » ; backend/tests/test_payroll_leave.py et backend/tests/test_payroll_leave_migration.py.
26. Refuser un CET au-delà de son plafond
Tickets : APAJH-181 · Rôle : RH n1 · Prérequis : un salarié avec un solde CET sur l'année affichée
Dans l'onglet Compteurs de la fiche, saisissez un épargné supérieur au plafond du CET et enregistrez.
✅ L'enregistrement est refusé avec « Le CET ne peut pas dépasser son plafond (… j). » Rien n'est modifié.
Ramenez l'épargné au plafond, ou en dessous, et enregistrez.
✅ Les compteurs sont enregistrés.
Le plafond est celui saisi sur le solde CET du salarié (accord d'entreprise du 01/03/2022, ch. V art. 3 — ALT-CET-01) : l'accord l'a fait évoluer, et c'est la RH qui sait lequel s'applique. Le back-office n'applique pas ce contrôle.
Couverture automatique : tests/test_counters.py (au-delà du plafond, au plafond).
27. Borner la période d'essai d'un agent polyvalent
Tickets : APAJH-181 · Rôle : RH n1 · Prérequis : un salarié dont le contrat est hors convention collective (agent polyvalent)
Dans l'onglet Contrat, saisissez une période d'essai du 01/01 au 01/07.
✅ L'enregistrement est refusé sous le champ de fin : « La période d'essai d'un contrat d'accompagnement par le travail est de 3 mois, renouvelable une fois 3 mois au plus. »
Ramenez la fin au 30/06 et enregistrez ; faites la même saisie sur un contrat CDI.
✅ Les deux sont enregistrés : la borne ne vaut que pour l'agent polyvalent.
Décret 2025-845 (CASF art. R.243-2 — ALT-PE-01/ALT-PE-03). La fin d'essai saisie comprend le renouvellement : 3 + 3 mois est la borne vérifiable.
Couverture automatique : tests/test_outside_collective_agreement.py.
28. N'ajouter une équipe que dans son périmètre
Tickets : APAJH-298 · Rôle : Manager n1 restreint à un établissement, puis Manager n2, Direction d'établissement et Service paie · Prérequis : make seed-all, des équipes dans l'établissement du manager et dans un autre établissement
Ouvrez la fiche d'un salarié de son établissement, onglet Lieux de travail & équipes, et dépliez Ajouter une équipe.
✅ Résultat attendu : seules les équipes du périmètre sont proposées, sans celles déjà attribuées au salarié. Une équipe hors périmètre déjà attribuée reste affichée dans la liste des équipes du salarié.
Rejouez l'étape 1 sur un téléphone.
✅ Résultat attendu : même liste.
Rejouez avec les autres profils.
✅ Résultat attendu : chacun ne se voit proposer que les équipes de son propre périmètre ; le Service paie, en lecture seule, n'a pas le bloc.
Couverture automatique : e2e/fiche-salarie.spec.ts → « « Ajouter une équipe » ne propose que les équipes du périmètre » ; côté serveur, le refus d'une équipe hors périmètre (APAJH-285) est couvert par tests/test_employee.py → test_the_fiche_cannot_post_to_a_team_beyond_the_perimeter.
Points d'attention
- Un matricule doit être unique. Le réutiliser sur une autre fiche est refusé avec un message lisible, à la création comme à la modification.
- Aucune règle serveur n'interdit un matricule sur la fiche d'un intérimaire, et c'est volontaire : le matricule appartient à la personne. Un intérimaire embauché ensuite en CDI garde sa fiche et reçoit alors un matricule. C'est le champ en lecture seule qui porte la règle, pas une contrainte qui bloquerait l'embauche.
- Les tests end-to-end tournent sans backend : ils prouvent que l'interface respecte les droits que l'API lui annonce, pas que l'API les applique. Les deux volets d'un même contrôle sont à vérifier — la partie serveur a ses propres tests (
backend/tests/test_employee.py,backend/tests/test_roles.py). - Le volet Diplômes & formations existe dans les maquettes mais n'est pas livré comme volet séparé : son absence du rail est normale. Ce qui existe aujourd'hui est plus étroit — un bloc Qualifications au bas de l'onglet Informations (scénario 13), sans document ni justificatif.
- Le back-office, lui, montre le n° de sécurité sociale à qui peut ouvrir la fiche, comme il montre le détail des enfants. Ce n'est pas une faille du masquage : le back-office exige le Statut équipe, qu'on ne coche en principe qu'aux comptes
Admin,RH n1etRH n2. Rien ne l'impose : un compte d'encadrement à qui on le cocherait lirait ce numéro dans le back-office. - L'ancienneté, la date d'entrée et le nombre d'heures mensuelles sont calculés. Un écart sur ces valeurs se corrige en amont — sur les contrats — jamais sur la fiche.
- Un contrôle de format juge ce qu'on écrit, jamais ce qui est déjà au dossier. Une règle capable de rendre une fiche enregistrée non enregistrable n'est pas une validation, c'est une panne qui attend une date. Deux conséquences visibles en recette : une fiche dont le n° de sécurité sociale importé de CEGI contredit sa date de naissance reste modifiable — les règles croisées ne se déclenchent que sur les champs que l'enregistrement touche —, et il n'existe aucune borne d'âge haute sur les enfants (scénario 18).
- Les deux dates de l'attestation d'honorabilité ne portent pas la même règle, et ce n'est pas un oubli : une remise est un événement passé, une date de renouvellement est l'échéance à laquelle le contrôle doit être refait, donc à venir. Seule la première refuse une date future (scénario 17).
29. Préparer une invitation à la création du compte
Tickets : — · Rôle : RH / Admin · Prérequis : dépilage activé.
Créez un salarié avec un Email professionnel. Rejouez en 390 × 844.
✅ La fiche s'ouvre et la confirmation annonce une invitation prochaine, sans prétendre que le mail a déjà été envoyé. Une seule invitation est en file.
Créez un salarié avec un email personnel uniquement.
✅ Aucune invitation automatique n'est préparée. La confirmation indique le parcours Mot de passe oublié, qui utilise l'adresse du compte.
Renseignez ensuite l'email professionnel du second salarié.
✅ Aucun envoi n'est déclenché par la modification. Le gestionnaire peut préparer une invitation explicitement dans le back-office.
Couverture automatique : e2e/nouvel-utilisateur.spec.ts → « annonce l’invitation prochaine après création sur ordinateur », « annonce l’invitation prochaine après création sur mobile », « conserve le parcours sans email professionnel sur ordinateur » et « conserve le parcours sans email professionnel sur mobile ». backend/tests/test_account_invitations.py couvre l'éligibilité et la file.