Apparence
Annuaire Utilisateurs
La liste du personnel (/utilisateurs) : les effectifs en tête, la recherche, le filtre par établissement, et l'ouverture d'une fiche.
Pour qui : les rôles d'encadrement (Admin, RH n1, RH n2, DA, Manager n1, Manager n2, Service Paie). Un compte Salarié ou Intérimaire n'y accède pas — voir Portail § 7.
Prérequis communs : make seed-establishments puis make seed-workforce.
Aucun critère d'acceptation ne porte sur cet écran. Les deux critères d'APAJH-208 concernent la fiche salarié (organisation en onglets, masquage des données d'enfants) et sont vérifiés dans Fiche salarié § 2 et § 4. Les comportements de l'annuaire décrits ci-dessous — tuiles d'effectif, recherche insensible aux accents, filtre par établissement — ont donc été livrés sans critère qui les cadre : ils sont recettés tels que construits, et un écart avec l'intention du ticket ne serait pas détecté ici.
1. Lire les effectifs par famille de contrat
Tickets : APAJH-208, APAJH-264 · Rôle : Admin · Prérequis : make seed-workforce
Ouvrez Utilisateurs dans le groupe Personnel.
✅ Résultat attendu : quatre tuiles en tête de page — Effectif total, CDI, CDD, Intérimaires.
Additionnez les trois tuiles de contrat.
✅ Résultat attendu : la somme égale l'Effectif total. Chaque tuile compte ce que le back-office dit du type de contrat, pas son libellé : un agent polyvalent compte avec les CDD — un contrat d'accompagnement par le travail s'arrête toujours à une date, celle de la notification MDPH — et une stagiaire aussi.
Couverture automatique : e2e/utilisateurs.spec.ts → « compte les effectifs par famille de contrat ».
2. Classer un type de contrat depuis le back-office
Tickets : APAJH-264 · Rôle : Admin · Prérequis : make seed-workforce
Ouvrez l'administration, Types de contrat, puis Ajouter type de contrat. Saisissez le libellé Contrat d'apprentissage, cochez Contrat à durée déterminée, laissez Contrat non géré par l'APAJH décoché, et enregistrez.
Depuis la SPA, ouvrez la fiche d'un salarié et donnez-lui un contrat de ce type, puis revenez sur Utilisateurs.
✅ Résultat attendu : le salarié est compté dans la tuile CDD. Aucun déploiement n'a été nécessaire — c'est la case cochée qui l'y range.
Retournez sur la ligne du type et décochez Contrat à durée déterminée, en la laissant vide (« Inconnu »). Rechargez Utilisateurs.
✅ Résultat attendu : le salarié reste dans l'Effectif total mais ne figure plus dans aucune des trois tuiles — un type non classé n'est jamais rangé au hasard. La liste des types permet de filtrer sur Contrat à durée déterminée pour retrouver ceux qui restent à trancher, notamment ceux que l'import CEGI a créés.
Couverture automatique : e2e/utilisateurs.spec.ts → « n'affecte à aucune tuile un type de contrat non classé » (l'étape 3).
3. Lire une ligne de l'annuaire
Tickets : APAJH-208, APAJH-177, APAJH-242 · Rôle : Admin · Prérequis : make seed-all
Repérez la ligne de Sophie Martin.
✅ Résultat attendu : la ligne porte sa fonction (Éducatrice spé.), son établissement (IME Les Tilleuls), son type de contrat (CDI) et son ETP en pourcentage (60 %).
Repérez Camille Annualisation dans la colonne de solde.
✅ Résultat attendu : la colonne s'intitule Annualisation — le même mot que le bandeau de la fiche salarié et que l'onglet Compteurs, jamais « Modulation », qui désigne autre chose (la journée de planning réécrite à la main). Son solde calculé s'affiche
−6 h, avec un vrai signe moins typographique et non un trait d'union. L'affichage positif reste couvert par le test navigateur.Repérez un salarié sans ligne d'annualisation pour l'année.
✅ Résultat attendu : la colonne affiche un tiret cadratin (
—), pas0 h— l'absence de donnée et un solde nul ne se disent pas pareil.
Couverture automatique : e2e/utilisateurs.spec.ts → « liste les salariés avec leur contrat et leur ETP ».
4. Rechercher un salarié
Tickets : APAJH-208 · Rôle : Admin · Prérequis : make seed-workforce
Dans Rechercher un salarié, tapez
ines— sans accent, en minuscules.✅ Le champ reste lisible lorsque la fenêtre est réduite ; le filtre et les actions passent à la ligne plutôt que de comprimer la recherche.
✅ Résultat attendu : Inès Fabre ressort. La casse et les accents sont ignorés.
Tapez
spe educatrice— deux mots, dans le désordre, l'un abrégé.✅ Résultat attendu : Sophie Martin (Éducatrice spé.) ressort ; une Stagiaire éducatrice ne ressort pas. La recherche porte donc aussi sur la fonction, et chaque mot doit correspondre.
Couverture automatique : e2e/utilisateurs.spec.ts → « la recherche ignore la casse et les accents ».
5. Filtrer par établissement
Tickets : APAJH-208, APAJH-177 · Rôle : Admin · Prérequis : make seed-workforce
Déroulez Filtrer par établissement et cochez IME Les Tilleuls.
✅ Résultat attendu : la liste ne contient plus que les salariés rattachés à cet établissement. Un salarié d'un autre établissement, par exemple Karim Mokrani (FAM Les Cigales), disparaît.
Retirez le filtre.
✅ Résultat attendu : l'effectif complet revient.
Couverture automatique : e2e/utilisateurs.spec.ts → « le filtre établissement restreint la liste ».
6. Ouvrir la fiche d'un salarié
Tickets : APAJH-208 · Rôle : Admin · Prérequis : make seed-workforce
Cliquez sur la ligne d'un salarié.
✅ Résultat attendu : la fiche s'ouvre sur le volet Informations, à l'adresse
/utilisateurs/<id>/informations.
Couverture automatique : e2e/utilisateurs.spec.ts → « ouvrir une ligne mène à la fiche du salarié ».
7. Consulter l'annuaire sur téléphone
Tickets : APAJH-208 · Rôle : Admin · Prérequis : make seed-workforce
En 390 × 844, ouvrez
/utilisateurs.✅ Résultat attendu : toutes les lignes sont présentes. Le tableau défile horizontalement dans son cadre ; la page elle-même ne déborde pas — aucune barre de défilement horizontale sur le document.
Couverture automatique : e2e/utilisateurs.spec.ts → « mobile : le tableau défile au lieu de déborder de la page ».
8. Créer un salarié avant réception de son matricule paie
Tickets : APAJH-205 · Rôle : Admin · Prérequis : make seed-establishments
Critères d'acceptation du ticket
- Étant donné un chef de service, quand il crée un compte provisoire, alors il renseigne type de contrat et volume horaire.
- Étant donné ce compte, quand le matricule paye devient disponible, alors il y est rattaché. (Recetté dans Fiche salarié § 8.)
- Étant donné la liste CDD/CDI, quand consultée, alors elle est synchronisée depuis la paye. (Voir les Points d'attention : il n'existe aucune synchronisation automatique avec le logiciel de paye.)
Ouvrez Utilisateurs dans le groupe Personnel, puis cliquez sur Nouvel utilisateur à droite de la barre de filtres.
Tout en haut du formulaire, choisissez le Type de contrat
CDI.✅ Résultat attendu : le formulaire complet apparaît — Identité, Coordonnées, Autres, Attestation d'honorabilité, Statut aidant, Rattachement & paie, Contrat, Régime de temps et Temps de travail. C'est le type de contrat qui décide de ces blocs : voir § 8 pour l'intérim.
Dans Identité, renseignez Nom, Prénom, la Date de naissance et la Situation familiale. Laissez le Matricule vide.
Dans Rattachement & paie, choisissez l'Établissement de paie, le Lieu de travail principal, et autant de Lieux de travail secondaires que nécessaire — le principal n'y est jamais proposé.
Dans Contrat, renseignez la Qualification (le diplôme :
DEI) et la Fonction (le poste :IDE) — ce sont deux questions différentes — puis la Date de début du contrat. Laissez la Date de fin du contrat vide — c'est le cas d'un CDI.Dans Temps de travail, saisissez
28en Heures hebdomadaires.✅ Résultat attendu : l'ETP contractuel affiche
80 %et le Nombre d'heures mensuelles121,33 h, tous deux verrouillés. Les deux se recalculent à chaque frappe : ils ne sont jamais saisis.Cliquez sur Créer l'utilisateur.
✅ Résultat attendu : la fiche du nouveau salarié s'ouvre sur le volet Informations, son bandeau porte le badge rouge Non synchronisée paie, et sa ligne dans l'annuaire porte la pastille Provisoire à côté de son type de contrat.
Couverture automatique : e2e/nouvel-utilisateur.spec.ts → « crée un salarié sans matricule et mène à sa fiche », « l'ETP et les heures mensuelles suivent les heures hebdomadaires », « un dossier sans matricule paie porte la pastille « Provisoire » ».
9. Créer un intérimaire, qui n'entre pas dans la paie
Tickets : APAJH-205 · Rôle : Admin · Prérequis : make seed-establishments
Un intérimaire est payé par son agence, jamais par l'APAJH. Son dossier n'a donc ni établissement de paie, ni matricule, ni la partie administrative d'un dossier de salarié : le formulaire se raccourcit dès que le type de contrat le dit.
Ouvrez Nouvel utilisateur et choisissez le Type de contrat
Intérim.✅ Résultat attendu : le formulaire ne présente plus que quatre blocs — Identité & mission, Coordonnées, Lieux de travail et Contrat. Les champs Matricule, Établissement de paie, Situation familiale, N° RPPS, Attestation d'honorabilité et Statut aidant ont disparu. Si vous en aviez déjà rempli un avant de basculer, sa valeur est effacée et ne partira pas.
Renseignez Nom, Prénom, la Qualification, la Fonction, le Lieu de travail principal, les Heures hebdomadaires et la Date de début du contrat. Laissez la Date de fin du contrat vide.
✅ Résultat attendu : le bouton Créer l'utilisateur reste désactivé. Une mission a toujours un terme — une date de fin vide voudrait dire « contrat en cours », ce qu'un intérim n'est jamais.
Renseignez la Date de fin du contrat, puis validez.
✅ Résultat attendu : sur la fiche, le bandeau porte le badge Intérim et aucun badge Non synchronisée paie ; dans l'annuaire, la ligne porte Intérim et aucune pastille Provisoire ; dans le volet Informations, le champ Matricule est en lecture seule. Dans le volet Contrat, l'Établissement de paye est vide.
Couverture automatique : e2e/nouvel-utilisateur.spec.ts → « choisir Intérim réduit le dossier à ce qu'une mission demande », « un intérim part sans établissement de paie », « un intérim sans date de fin ne peut pas être créé », « un champ saisi avant de basculer sur Intérim ne part pas » ; e2e/utilisateurs.spec.ts → « un intérimaire sans matricule n'est PAS provisoire » ; e2e/fiche-salarie.spec.ts → « un intérimaire sans matricule porte « Intérim », pas « Non synchronisée paie » ».
10. Ouvrir le dossier administratif complet dès la création
Tickets : APAJH-205 · Rôle : Admin · Prérequis : make seed-establishments
Ce qui manquait au formulaire livré : tout ce que la fiche sait afficher dans ses onglets Informations et Contrat se saisit maintenant d'un coup, plutôt qu'en rouvrant la fiche après coup. Aucun de ces champs n'est bloquant.
Ouvrez Nouvel utilisateur, choisissez un Type de contrat autre que
Intérim, et remplissez les champs obligatoires (§ 7).Dans Autres, cochez Salarié en situation de handicap.
Dans Attestation d'honorabilité, basculez l'interrupteur sur Oui, renseignez la Date de remise et joignez un PDF.
Dans Statut aidant, basculez sur Oui, cliquez sur Ajouter une personne aidée et saisissez
père.Dans Contrat, renseignez le Début et la Fin de période d'essai.
Dans Régime de temps, cochez Forfait jours et Astreinte.
✅ Résultat attendu : Non annualisé est décoché d'office — un salarié est créé annualisé — et Dimanches & jours fériés prévisionnels n'apparaît pas : un contrat annualisé porte déjà ces jours dans son calendrier. Cochez Non annualisé : le champ apparaît.
Saisissez
12dans Dimanches & jours fériés prévisionnels, puis validez.✅ Résultat attendu : la fiche s'ouvre. Dans Informations, la case Salarié en situation de handicap est cochée et modifiable, et l'attestation porte sa date et son justificatif. Dans Famille & social, le Statut aidant est à Oui avec
père. Dans Contrat, la période d'essai, le régime de temps et les12dimanches sont là.
Couverture automatique : e2e/nouvel-utilisateur.spec.ts → « le régime de temps et la période d'essai partent avec la création », « le statut aidant et ses personnes aidées partent avec la création », « joindre le justificatif d'honorabilité fait partir un multipart », « les dimanches ne sont demandés que hors annualisation ».
11. Vérifier qui peut saisir le n° de sécurité sociale
Tickets : APAJH-209 · Rôle : Encadrant (Manager n1 ou DA), puis RH / Admin · Prérequis : make seed-establishments
Le numéro est un identifiant national : seuls Admin, RH n1 et RH n2 le voient et le saisissent. Or l'ouverture d'un dossier est plus large que cela — un DA et les deux rangs de Manager créent aussi des salariés (§ 7). Le formulaire ne leur demande donc pas ce champ, et sa consultation ultérieure sur la fiche suit la même règle (voir Fiche salarié § 12).
Connectez-vous avec un profil Encadrant et ouvrez Nouvel utilisateur.
✅ Résultat attendu : le bloc Identité ne comporte aucun champ « N° sécurité sociale ». Les autres champs du bloc — nom de naissance, date de naissance, matricule — sont bien là.
Créez le salarié avec les champs obligatoires (§ 7).
✅ Résultat attendu : la création aboutit et mène à la fiche. L'absence du champ ne bloque rien : le numéro n'a jamais été obligatoire.
Refaites la même chose en RH / Admin, en renseignant cette fois le numéro.
✅ Résultat attendu : le champ est proposé, et le numéro saisi se retrouve dans le bloc Identité de la fiche qui s'ouvre.
Couverture automatique : e2e/nouvel-utilisateur.spec.ts → « le n° de sécurité sociale part quand le rôle a le droit de le lire » et « sans ce droit, le formulaire ne le demande pas et la clé ne part pas ». Côté serveur, le refus d'une saisie envoyée malgré tout est couvert par backend/tests/test_employee.py.
12. Un email déjà utilisé par un autre compte est refusé à la création
Tickets : APAJH-227 · Rôle : Encadrant ou RH/Admin · Prérequis : un compte existant (salarié ou non) dont l'adresse email est connue
User.email ne porte aucune contrainte d'unicité par lui-même (c'est le défaut de Django) : sans ce refus, deux comptes actifs pourraient partager la même adresse, et « mot de passe oublié » (Mot de passe) n'aurait alors aucun moyen fiable de savoir lequel des deux le lien concerne.
Ouvrez Nouvel utilisateur et renseignez, comme email professionnel ou personnel, une adresse déjà portée par un compte actif — salarié ou non.
Créez le salarié.
✅ Résultat attendu : la création échoue, un message signale que cette adresse est déjà utilisée par un autre compte — aucune fiche n'est créée.
Recommencez avec une adresse inédite.
✅ Résultat attendu : la création aboutit normalement.
Couverture automatique : le refus est une règle serveur, couverte par backend/tests/test_employee.py (email professionnel, email personnel, et le cas d'un compte désactivé — délibérément exclu, puisqu'il ne peut déjà plus recevoir de lien de réinitialisation). Son affichage dans le formulaire suit le même mécanisme générique que « un refus de l'API est affiché sans quitter la page » (e2e/nouvel-utilisateur.spec.ts).
13. Vérifier les contrôles de format à la création
Tickets : APAJH-246 · Rôle : RH/Admin (les étapes 1 à 3 demandent le droit de lire le n° de sécurité sociale) · Prérequis : aucun
C'est le seul formulaire de l'application où l'on tape un n° de sécurité sociale, donc le seul endroit où sa clé se contrôle à la saisie.
Le bouton Créer l'utilisateur reste actif : ce sont les champs obligatoires manquants qui le désactivent, pas les formats. Un message sous le champ fautif dit ce qui cloche ; un bouton qui refuse de s'enfoncer ne dit rien.
Remplissez le dossier minimal (scénario 7), puis saisissez
2 89 03 75 112 048— 13 chiffres, sans clé — dans N° sécurité sociale. Cliquez sur Créer l'utilisateur.✅ Résultat attendu : « Le n° de sécurité sociale doit comporter 15 chiffres, clé comprise (les espaces sont tolérés). » Aucune fiche n'est créée et la page ne change pas.
L'API, elle, accepte les 13 chiffres : l'export de paie CEGI écrit le numéro sans sa clé, et une fiche déjà enregistrée doit rester enregistrable. C'est la seule règle volontairement plus stricte dans le formulaire que sur le serveur — personne qui tape un numéro à la main n'a cette excuse.
Remplacez-le par
2 89 03 75 112 048 47.✅ Résultat attendu : « La clé du n° de sécurité sociale est incorrecte. » La clé vaut 97 − (numéro mod 97) ; pour la Corse,
2Acompte pour 19 et2Bpour 18.Remplacez-le par
2 89 03 75 112 048 48, et donnez une Date de naissance en 1990.✅ Résultat attendu : « Les chiffres 2 et 3 du n° de sécurité sociale ne correspondent pas à l'année de naissance. », affiché sous le numéro. Ici c'est lui qu'on saisit, alors c'est lui qu'on désigne ; sur la fiche il est cadenassé et le message porte sur la date (scénario 16 de Fiche salarié).
Les chiffres 4 et 5 sont comparés au mois de naissance seulement s'ils désignent un vrai mois. Un numéro de personne née à l'étranger porte un pseudo-mois (20 et au-delà) : le comparer refuserait un numéro correct.
Rétablissez un numéro et une date cohérents, puis saisissez
06 12 34dans Téléphone personnel.✅ Résultat attendu : « 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. »
Rétablissez le téléphone, puis saisissez
10100654321dans N° RPPS.✅ Résultat attendu : « La clé du n° RPPS est incorrecte. »
Rétablissez le n° RPPS, puis saisissez
sophie.martin— sans arobase — dans Email personnel.✅ Résultat attendu : « Saisissez une adresse e-mail valide. », en français, sous le champ. C'est le mot à mot de Django, donc un appel direct à l'API répond la même phrase. Une bulle grise au-dessus du champ, voire en anglais, signalerait le retour de la validation native du navigateur.
Corrigez l’adresse email.
✅ Résultat attendu : le message disparaît sans nouveau clic sur Créer l'utilisateur, et la création aboutit.
Rouvrez Nouvel utilisateur. Sans cliquer sur Créer l'utilisateur, saisissez
sophie.martindans Email personnel, sans quitter le champ.✅ Résultat attendu : aucun message tant que le curseur est dans le champ — une adresse à moitié tapée n'est pas encore une adresse fausse.
Passez au champ suivant (touche Tab, ou clic ailleurs). Faites de même avec
06 12 34dans Téléphone personnel.✅ Résultat attendu : « Saisissez une adresse e-mail valide. » puis « Le numéro de téléphone doit comporter au moins 10 chiffres… » apparaissent sous leur champ dès qu'on le quitte, sans avoir soumis le formulaire. Chaque message disparaît dès que son champ est corrigé.
Couverture automatique : e2e/nouvel-utilisateur.spec.ts → les scénarios des groupes « formats refusés (APAJH-246) » et « contrôle à la sortie du champ (APAJH-246) », dont « un format refusé s'affiche sous son champ sans élargir la page » et « sur un téléphone, un email invalide est signalé en quittant le champ » sur un écran de téléphone. Côté serveur, backend/tests/test_employee.py couvre les mêmes règles, et backend/tests/test_data_imports.py → test_import_is_not_held_to_the_fiches_format_rules la tolérance de l'import CEGI mentionnée à l'étape 1.
14. Créer un agent polyvalent, hors CCN-66
Tickets : APAJH-199 · Rôle : RH n1 · Prérequis : make seed-establishments
Critères d'acceptation du ticket
- Étant donné un agent polyvalent, quand son contrat est créé, alors il n'est pas soumis à la CCN-66 et seul le compteur CP est actif.
- Étant donné cet agent, quand son temps est suivi, alors il est limité à 35 h max/semaine sans annualisation — vérifié au planning, voir le scénario 30 de Planning manager.
L'agent polyvalent se crée par le même formulaire qu'un salarié. Ce qui le distingue est la case Hors convention collective (CCN-66) de son type de contrat, cochée dans le référentiel pour Agent polyvalent.
Ouvrez Nouvel utilisateur, choisissez le Type de contrat
CDIet cochez Forfait jours. Choisissez ensuiteAgent polyvalent.✅ Résultat attendu : le bloc Régime de temps ne présente plus ni Non annualisé ni Forfait jours, mais la phrase « Contrat hors CCN-66 : ni annualisation ni forfait jours, 35 h par semaine au plus. » Travail de nuit et Astreinte restent proposés.
Remplissez le dossier, saisissez 36 en Heures hebdomadaires et validez.
✅ Résultat attendu : la création est refusée, et « Un contrat hors CCN-66 est plafonné à 35 h par semaine. » s'affiche sous le champ.
Ramenez les heures à 35 et validez, puis ouvrez le volet Contrat de la fiche.
✅ Résultat attendu : le salarié est créé. Son volet Contrat porte la même phrase à la place des deux cases : un contrat d'agent polyvalent ne peut pas être annualisé après coup non plus.
Lancez l'acquisition des compteurs sur une période couverte par ce contrat —
make acquire-counters ARGS="--from <date de début> --as-of <aujourd'hui>"— puis ouvrez l'onglet Compteurs de la fiche.✅ Résultat attendu : seul Congés annuels (CP) a acquis des jours. Aucun congé trimestriel, aucun congé d'ancienneté, aucun droit enfant malade, et pas de solde d'annualisation, même sur une période où un salarié sous CCN-66 en aurait acquis.
Couverture automatique : e2e/nouvel-utilisateur.spec.ts → le groupe « agent polyvalent, hors CCN-66 (APAJH-199) », bureau et téléphone (étapes 1 à 3, sans l'ouverture de la fiche) ; le volet Contrat est vérifié par la spec de la fiche salarié (« un agent polyvalent ne propose ni annualisation ni forfait jours »). Côté serveur, backend/tests/test_outside_collective_agreement.py couvre les refus du contrat, l'acquisition limitée aux CP et le refus de tout autre compteur ouvert à la main.
15. Repérer les champs obligatoires et ce qu'il reste à remplir
Tickets : APAJH-246 · Rôle : Admin · Prérequis : make seed-establishments
Ouvrez Nouvel utilisateur.
✅ Résultat attendu : sous le titre, la phrase « Les champs marqués * sont obligatoires. » L'astérisque rouge, plus grand et en gras que le libellé qu'il suit, marque chaque champ obligatoire. Le même astérisque vaut dans tous les formulaires de l'application.
Sans rien saisir, regardez sous Créer l'utilisateur.
✅ Résultat attendu : le bouton est grisé et la ligne « Il reste 7 champs obligatoires à renseigner : » nomme Type de contrat, Nom, Prénom, Établissement de paie, Lieu de travail principal, Date de début du contrat et Heures hebdomadaires, dans l'ordre de la page.
Cliquez sur Heures hebdomadaires dans cette ligne.
✅ Résultat attendu : la page remonte jusqu'au champ, curseur dedans.
Renseignez les champs un à un.
✅ Résultat attendu : chaque nom quitte la liste dès que son champ est rempli. Au dernier, la ligne disparaît et le bouton s'active.
Choisissez le Type de contrat
Intérim, remplissez le dossier puis videz la Date de fin du contrat.✅ Résultat attendu : « Il reste 1 champ obligatoire à renseigner : Date de fin du contrat. » L'établissement de paie n'est jamais réclamé à un intérimaire.
Refaites les étapes 2 et 3 sur téléphone.
✅ Résultat attendu : la ligne tient dans l'écran, sans défilement horizontal, et chaque nom ramène au champ.
Couverture automatique : e2e/nouvel-utilisateur.spec.ts → le groupe « champs obligatoires », « un intérim sans date de fin ne peut pas être créé » et « tient sur un téléphone ».
Points d'attention
- Le bouton Nouvel utilisateur n'apparaît pas pour tous les rôles. Il est réservé à
Admin,RH n1,RH n2,DA,Manager n1etManager n2. Un compteService Paieconsulte l'annuaire sans pouvoir ouvrir de dossier. - Le matricule n'est pas obligatoire, mais il est saisissable dès la création. S'il est déjà connu, renseignez-le tout de suite : le salarié n'est alors pas provisoire et n'affiche aucun des deux marqueurs.
- Un matricule vide chez un intérimaire est l'état normal et définitif, pas un dossier en attente : les intérims n'entrent pas dans la paie. C'est pourquoi un intérimaire n'affiche jamais Provisoire ni Non synchronisée paie, et pourquoi son contrat n'a aucun établissement de paye.
- Qualification et Fonction ne sont pas le même champ. La qualification est le diplôme ou le niveau reconnu au titre duquel la personne est employée (
DEES,DEAS) ; la fonction est le poste qu'elle occupe (IDE,AS). Les deux sont demandées à la création, et l'annuaire affiche la fonction. - L'ETP contractuel et les heures mensuelles ne se saisissent pas. Ils sont calculés depuis les heures hebdomadaires (référence 35 h, convention 52/12) et affichés verrouillés. Un écart entre ce que montre le formulaire et ce que montre la fiche serait un défaut, pas une saisie à corriger.
- Il n'existe aucune synchronisation automatique avec le logiciel de paye. Le badge Non synchronisée paie signifie « aucun matricule paie n'a encore été saisi », et non « un échange automatique a échoué ». Le matricule est saisi à la main — voir DAT § 02.
- Un numéro envoyé sans le droit de le lire est refusé, pas ignoré. Le formulaire ne montre pas le champ aux rôles concernés, mais si un appel direct à l'API en porte un, la création échoue en 403 — plutôt que d'aboutir en laissant croire que le numéro a été enregistré.
- Le compte naît sans mot de passe utilisable. Avec un email professionnel, une invitation est préparée puis envoyée progressivement. Sans cet email, la personne utilise Mot de passe oublié avec l'adresse du compte.
- L'annuaire montre tous les salariés à tout rôle d'encadrement : il n'est pas restreint aux établissements sur lesquels votre rôle est attribué. Si cette restriction est attendue, c'est un écart à signaler, pas une régression connue.
- Les soldes affichés viennent des compteurs. La commande Django
seed_allseule n'écrit aucun solde automatique ;make seed-allenchaîne la préparation réelle qui les rend visibles. Une colonne vide entre les deux étapes n'est pas un défaut de l'écran. - Le formulaire exige la clé du n° de sécurité sociale, l'API non. Ce n'est pas une incohérence à signaler : l'export de paie CEGI livre le numéro sur 13 chiffres, et une fiche importée doit rester enregistrable depuis le back-office. Le formulaire, lui, est le seul endroit où une personne tape le numéro (scénario 12).
- Un message de format n'empêche pas le bouton de s'enfoncer. Seuls les champs obligatoires manquants désactivent Créer l'utilisateur ; un format invalide laisse le bouton actif, refuse l'envoi et explique pourquoi sous le champ. Quand le bouton est grisé, la ligne sous lui nomme les champs qui manquent (scénario 15) : un bouton grisé sans cette ligne serait un défaut.
16. L'annuaire ne liste que le périmètre du rôle
Tickets : APAJH-298 · Rôle : RH n1 (ou Admin, Manager n1) dont le rôle est rattaché à quelques établissements seulement · Prérequis : make seed-all, un salarié travaillant dans un établissement hors de ce périmètre
Ouvrez Utilisateurs, puis le filtre Établissement.
✅ Résultat attendu : seuls les établissements du périmètre sont proposés, et la liste ne montre que leurs salariés — ceux qui y travaillent ou sont affectés à l'une de leurs équipes ; le salarié hors périmètre n'apparaît pas. La colonne Établissement affiche ces établissements, et c'est sur eux que le filtre porte. Annualisation liste la même population.
Cochez un établissement, puis tapez un nom dans Rechercher un salarié.
✅ Résultat attendu : la recherche ne porte que sur les salariés que le filtre laisse.
Couverture automatique : e2e/utilisateurs.spec.ts → « n’affiche que le périmètre : ses établissements et ses salariés ».
17. Choisir séparément deux types de contrat homonymes
Tickets : GitHub #129 · Rôle : RH n1 · Prérequis : deux types actifs portant le même libellé, avec des codes CEGI distincts ; deux autres sans code
Ouvrez Nouvel utilisateur, puis Type de contrat, sur ordinateur et sur mobile.
Vérifiez que chaque homonyme apparaît avec son code CEGI ou, à défaut, son numéro GTA.
Sélectionnez chacun à tour de rôle et créez un dossier de test.
✅ Résultat attendu : le contrat reçoit exactement le type choisi. Un libellé unique reste inchangé. Les règles intérim et hors convention suivent le type choisi, même lorsque les libellés sont identiques. Un type devenu inactif dans le référentiel chargé empêche la soumission.
Couverture automatique : e2e/nouvel-utilisateur.spec.ts → « types homonymes », sur ordinateur et mobile.
18. Importer deux codes CEGI portant le même libellé
Tickets : GitHub #128 · Rôle : Admin · Prérequis : circuit SFTP local configuré, deux salariés fictifs
Déposez un CSV où les deux salariés ont respectivement les codes
CDI1etCDI2, tous deux libellés C.D.I., puis déclenchez la collecte.Consultez le journal des imports et Types de contrat.
✅ Résultat attendu : le fichier est importé, deux types distincts existent et chacun des deux contrats porte le bon type. Aucun classement métier n'est déduit du libellé.
Classez les types, rejouez le fichier puis importez un libellé corrigé pour
CDI1. Laissez ensuite ce libellé vide dans un nouvel export.✅ Résultat attendu : les IDs et classements restent identiques ; le libellé corrigé est conservé quand la source ne le fournit plus. Plusieurs types manuels homonymes ne sont jamais départagés : un code inconnu crée une nouvelle ligne. Un seul candidat manuel au libellé strictement identique est rapproché.
Ajoutez à un fichier valide une ligne avec un code inconnu sans libellé.
✅ Résultat attendu : le fichier entier est rejeté, sans modification partielle.
Couverture automatique : tests/test_data_import_contract_types.py et tests/test_contract_type_migration.py.
19. Un intérim n'est jamais annualisé
Tickets : APAJH-307 · Rôle : RH n1 · Prérequis : make seed-establishments
Le temps de travail d'un intérimaire est celui de son agence : ni l'annualisation ni le forfait jours, que l'APAJH applique sous CCN-66, ne le concernent. Ce qui en décide est la case Contrat non géré par l'APAJH du type de contrat, cochée dans le référentiel pour Intérim.
Ouvrez Nouvel utilisateur et choisissez le Type de contrat
Intérim.✅ Résultat attendu : le bloc Contrat ne propose ni Non annualisé ni Forfait jours ; seule la case Travail de nuit y figure.
Remplissez le dossier comme au scénario 9, validez, puis ouvrez le volet Contrat de la fiche.
✅ Résultat attendu : le bloc Régime de temps porte la phrase « Contrat d'intérim : ni annualisation ni forfait jours. » à la place des deux cases. Le contrat est enregistré non annualisé.
Dans le back-office, ouvrez ce contrat, cochez Annualisé et enregistrez.
✅ Résultat attendu : l'enregistrement est refusé avec « Un contrat d'intérim ne peut pas être annualisé. » Même chose pour Forfait jours.
Couverture automatique : e2e/nouvel-utilisateur.spec.ts → « choisir Intérim réduit le dossier à ce qu'une mission demande », « un intérim part sans établissement de paie » ; le volet Contrat est vérifié par la spec de la fiche salarié (« un intérim ne propose ni annualisation ni forfait jours »). Côté serveur, backend/tests/test_interim_contract_regime.py couvre les refus à la création, sur la fiche et au back-office, et la remise à zéro des contrats d'intérim déjà annualisés.