Merge pull request #2 — corriger la spécification depuis l'audit des menus

Corriger la spécification à partir de l'audit des menus déroulants
This commit is contained in:
LogiFlow authored and GitHub committed 2026-08-07 17:00:33 +02:00
commit 82f5cc96f3
1 file changed
+123 -22
+123 -22
View File
@@ -2,7 +2,11 @@
> **Destinataire : un orchestrateur automatisé.** Ce document est la seule source d'instructions nécessaire pour construire l'application. Il est normatif : ce qui y est écrit fait foi, ce qui n'y est pas doit être demandé, jamais inventé. > **Destinataire : un orchestrateur automatisé.** Ce document est la seule source d'instructions nécessaire pour construire l'application. Il est normatif : ce qui y est écrit fait foi, ce qui n'y est pas doit être demandé, jamais inventé.
> >
> **Source fonctionnelle :** [`Audit Combo/`](Audit%20Combo/INDEX.md) — 58 écrans, cartographie fonctionnelle et inventaire de crawl du 7 août 2026. > **Sources fonctionnelles :**
> - [`Audit Combo/`](Audit%20Combo/INDEX.md) — 58 écrans, cartographie fonctionnelle et inventaire de crawl.
> - [`Audit Combo/dropdowns/`](Audit%20Combo/dropdowns/DROPDOWN-AUDIT.md) — 172 déclencheurs, 51 menus ouverts. **Fait foi sur les énumérations** : rôles, types de contrat, vues, statuts, actions.
>
> Le dossier `dropdowns/` à la racine du dépôt est un **doublon exact** de `Audit Combo/dropdowns/` (vérifié par comparaison binaire). Supprimer la copie racine pour qu'il n'existe qu'une source ; ce plan ne référence que celle sous `Audit Combo/`.
> >
> **Régime : clean room.** L'audit et ce plan décrivent des *capacités* et des *invariants*. Ne recopier ni code, ni CSS, ni icônes, ni illustrations, ni wording propriétaire au-delà des libellés métier nécessaires. Concevoir une API, un schéma, des textes et une interface originaux. > **Régime : clean room.** L'audit et ce plan décrivent des *capacités* et des *invariants*. Ne recopier ni code, ni CSS, ni icônes, ni illustrations, ni wording propriétaire au-delà des libellés métier nécessaires. Concevoir une API, un schéma, des textes et une interface originaux.
@@ -10,7 +14,7 @@
## 0. Mode d'emploi pour l'orchestrateur ## 0. Mode d'emploi pour l'orchestrateur
1. Lire les sections 1 à 9 en entier avant d'écrire la moindre ligne. Elles définissent des invariants transverses ; les découvrir au lot 3 impose de refaire les lots 0 à 2. 1. Lire les sections 1 à 9 en entier avant d'écrire la moindre ligne. Elles définissent des invariants transverses ; les découvrir à WP-03 impose de refaire WP-00 à WP-02.
2. Exécuter les lots de travail (§10) **dans l'ordre**. Chaque lot déclare ses dépendances, ses livrables et ses **critères d'acceptation**. Un lot n'est terminé que si tous ses critères passent en test automatisé. 2. Exécuter les lots de travail (§10) **dans l'ordre**. Chaque lot déclare ses dépendances, ses livrables et ses **critères d'acceptation**. Un lot n'est terminé que si tous ses critères passent en test automatisé.
3. **Signaux d'arrêt.** Interrompre et demander un arbitrage humain dans ces cas : 3. **Signaux d'arrêt.** Interrompre et demander un arbitrage humain dans ces cas :
- une valeur numérique de convention collective est nécessaire (§6) ; - une valeur numérique de convention collective est nécessaire (§6) ;
@@ -18,6 +22,7 @@
- une donnée personnelle réelle serait requise pour tester ; - une donnée personnelle réelle serait requise pour tester ;
- un point marqué **`À VALIDER`** dans ce document bloque l'avancement. - un point marqué **`À VALIDER`** dans ce document bloque l'avancement.
4. Ne jamais inventer de paramètre légal, de code de paie ou de règle métier. L'absence d'information est un signal d'arrêt, pas une invitation à choisir. 4. Ne jamais inventer de paramètre légal, de code de paie ou de règle métier. L'absence d'information est un signal d'arrêt, pas une invitation à choisir.
**Les énumérations de ce document proviennent de l'audit des menus déroulants et sont exhaustives.** Ne pas y ajouter de valeur « qui semble manquer » : une valeur absente de l'audit est une valeur à faire confirmer.
5. Langue : **interface et libellés en français**, **identifiants de code en anglais**, commentaires en anglais. 5. Langue : **interface et libellés en français**, **identifiants de code en anglais**, commentaires en anglais.
--- ---
@@ -26,16 +31,21 @@
Le dépôt ne contient que l'audit ; il n'y a aucun code. L'objectif est de construire **PlanFlow**, une application de gestion du personnel et des plannings multi-établissements, reprenant les capacités de Combo pour l'organisation auditée, avec la paie **exportée vers Silae**. Le dépôt ne contient que l'audit ; il n'y a aucun code. L'objectif est de construire **PlanFlow**, une application de gestion du personnel et des plannings multi-établissements, reprenant les capacités de Combo pour l'organisation auditée, avec la paie **exportée vers Silae**.
L'audit corrige trois hypothèses qu'une lecture de la documentation publique de Combo aurait induites : ### Ce que l'audit corrige
Cinq constats structurants, qu'une lecture de la documentation publique de Combo aurait manqués ou faussés. Ils sont listés ici parce qu'ils conditionnent des choix qui coûtent cher à reprendre.
**La convention collective n'est pas HCR.** Le compte audité est **FROUARD DISTRIBUTION / La Foir'Fouille**, configuré sur **« Commerces de détail non alimentaires (IDCC 1517) — JF 50 % et Dimanche 100 % »**. C'est du commerce de détail. Les durées maximales, coupures et majorations propres à l'hôtellerie-restauration ne s'appliquent pas. **La convention collective n'est pas HCR.** Le compte audité est **FROUARD DISTRIBUTION / La Foir'Fouille**, configuré sur **« Commerces de détail non alimentaires (IDCC 1517) — JF 50 % et Dimanche 100 % »**. C'est du commerce de détail. Les durées maximales, coupures et majorations propres à l'hôtellerie-restauration ne s'appliquent pas.
**L'autorisation est par capacités, pas par rôles.** L'audit relève des permissions granulaires et des rôles **configurables par le client** (`/settings/roles-permissions/:roleKey`). `Role`, `Permission` et `Scope` sont trois notions distinctes dès le lot 0. Aucun écran ne teste un nom de rôle. **L'autorisation est par capacités, pas par rôles.** L'audit relève des permissions granulaires et des rôles **configurables par le client** (`/settings/roles-permissions/:roleKey`). `Role`, `Permission` et `Scope` sont trois notions distinctes dès WP-01. Aucun écran ne teste un nom de rôle.
**Les compteurs de congés sont un registre d'écritures.** L'audit identifie `Counter` / `LedgerOperation` avec ajustements protégés et prévision. Un solde stocké serait un contresens : le solde est le cumul des écritures. **Les compteurs de congés sont un registre d'écritures.** L'audit identifie `Counter` / `LedgerOperation` avec ajustements protégés et prévision. Un solde stocké serait un contresens : le solde est le cumul des écritures.
**Le verrouillage d'une période de paie n'est pas terminal.** Le menu d'actions est dépendant de l'état : une période verrouillée propose « Déverrouiller », et reste supprimable. Concevoir le verrouillage comme définitif rendrait impossible le cas le plus courant — corriger une paie avant transmission — et laisserait des exports périmés circuler sans signalement (§4.6).
**Les énumérations sont fermées et connues.** Rôles, types de contrat, vues de planning, statuts et filtres sont relevés exhaustivement dans l'audit des menus. Il n'y a rien à deviner, et deviner produit des valeurs qui n'existent nulle part.
### Écart de documentation — `À VALIDER` ### Écart de documentation — `À VALIDER`
`Audit Combo/INDEX.md` référence `../../matrice-conformite-rh-france-2026.md`, absent du dépôt. Ce document conditionne §12. Le demander avant le lot 2. `Audit Combo/INDEX.md` référence `../../matrice-conformite-rh-france-2026.md`, absent du dépôt. Ce document conditionne §12. Le demander avant WP-03.
--- ---
@@ -52,13 +62,13 @@ L'audit corrige trois hypothèses qu'une lecture de la documentation publique de
**Hors périmètre v1**, à ne pas construire : moteur de paie, DSN, bulletins de paie, distribution de bulletins, signature électronique qualifiée, transmission DPAE à l'URSSAF, connecteurs de caisse, abonnement et facturation, planning prédictif, auto-assignation. **Hors périmètre v1**, à ne pas construire : moteur de paie, DSN, bulletins de paie, distribution de bulletins, signature électronique qualifiée, transmission DPAE à l'URSSAF, connecteurs de caisse, abonnement et facturation, planning prédictif, auto-assignation.
**Conservés mais différés au lot 5** : articles, conversations, analyses RH avancées. **Conservés mais différés en fin de parcours** : articles et conversations internes (WP-11, optionnel). Les analyses RH sont bien dans le périmètre v1 (WP-09).
--- ---
## 3. Architecture et invariants transverses ## 3. Architecture et invariants transverses
Ces sept invariants s'appliquent à tout le code. Ils ne sont pas négociables et chacun fait l'objet de tests dédiés. Ces huit invariants s'appliquent à tout le code. Ils ne sont pas négociables et chacun fait l'objet de tests dédiés.
### 3.1 Isolation multi-tenant ### 3.1 Isolation multi-tenant
Hiérarchie `Account` → `Location` → `Team`. Base unique, **scoping par ligne**. Hiérarchie `Account` → `Location` → `Team`. Base unique, **scoping par ligne**.
@@ -86,14 +96,28 @@ Point d'entrée unique `can(membership, permissionCode, resource?)` dans `src/do
### 3.5 Immutabilité et audit ### 3.5 Immutabilité et audit
- `LedgerOperation`, `AuditLog` et `PayrollExport` sont **append-only**. Une correction est une écriture inverse suivie d'une nouvelle écriture, jamais un `UPDATE` ni un `DELETE`. - `LedgerOperation`, `AuditLog` et `PayrollExport` sont **append-only**. Une correction est une écriture inverse suivie d'une nouvelle écriture, jamais un `UPDATE` ni un `DELETE`.
- `AuditLog` capture auteur, horodatage, entité, avant/après et justification pour : contrat, avenant, absence et décision, publication et dépublication de planning, validation d'heures, ouverture et verrouillage de période, export, changement de rôle ou de permission, ajustement de compteur, accès à une donnée sensible. - Corollaire : la péremption d'un export (§4.6) est **dérivée**, jamais stockée — un export est périmé si la période a été déverrouillée après sa génération. Écrire un drapeau sur `PayrollExport` violerait l'append-only.
- `AuditLog` capture auteur, horodatage, entité, avant/après et justification pour : contrat, avenant, absence et décision, publication et dépublication de planning, validation d'heures, ouverture, **verrouillage et déverrouillage** de période, export, changement de rôle ou de permission, ajustement de compteur, accès à une donnée sensible.
### 3.6 Données sensibles ### 3.6 Données sensibles
- NIR, IBAN, BIC et pièces jointes de santé **chiffrés au repos** (chiffrement applicatif par colonne, clé hors base). - NIR, IBAN, BIC et pièces jointes de santé **chiffrés au repos** (chiffrement applicatif par colonne, clé hors base).
- Fichiers servis par **URL signée à durée courte**, jamais par chemin public. - Fichiers servis par **URL signée à durée courte**, jamais par chemin public.
- Toute lecture d'une donnée de catégorie particulière est journalisée. - Toute lecture d'une donnée de catégorie particulière est journalisée.
### 3.7 États d'écran ### 3.7 Télémétrie — ce qu'il ne faut pas reproduire
L'audit des menus a intercepté **2102 requêtes non sûres**, toutes de télémétrie tierce : Segment, LinkedIn Ads, Google Ads et Analytics, DoubleClick, Microsoft Clarity, Bing, Bugsnag, Hotjar, SatisMeter, Zendesk. Aucune n'était une requête métier.
C'est un anti-modèle à ne pas reprendre. Une application RH fait naviguer des managers sur des écrans dont l'URL et le contexte trahissent des informations sur des salariés identifiables ; les envoyer à des régies publicitaires est un problème de conformité, pas une préférence.
**Règles :**
- **Aucun traceur publicitaire ni analytique tiers.** Interdit par défaut.
- La télémétrie technique (erreurs, performance) passe par une **interface abstraite** et reste auto-hébergée ou désactivable.
- Toute sortie réseau vers un tiers est déclarée explicitement et soumise à consentement.
- Une **Content-Security-Policy** restrictive est livrée dès WP-00 et testée : elle constitue le garde-fou qui rend cette règle vérifiable plutôt que déclarative.
À noter également : dans le produit audité, les tableaux de bord d'analyse RH sont servis par un outil de BI tiers embarqué (ToucanToco). PlanFlow les construit **nativement** (WP-09) ; c'est une divergence assumée, qui évite une dépendance et une sortie de données.
### 3.8 États d'écran
Chaque écran implémente **chargement, vide, erreur, interdit**. Accessibilité clavier et contraste conformes WCAG 2.2 AA. Les bandeaux transverses sont dismissibles et non bloquants — l'audit relève explicitement que des surcouches masquaient des contrôles dans le produit observé ; ne pas reproduire ce défaut. Chaque écran implémente **chargement, vide, erreur, interdit**. Accessibilité clavier et contraste conformes WCAG 2.2 AA. Les bandeaux transverses sont dismissibles et non bloquants — l'audit relève explicitement que des surcouches masquaient des contrôles dans le produit observé ; ne pas reproduire ce défaut.
--- ---
@@ -248,7 +272,18 @@ model UserContract {
version Int @default(0) version Int @default(0)
} }
enum ContractType { CDI CDD SAISONNIER APPRENTISSAGE PROFESSIONNALISATION STAGE INTERIM EXTRA } // Liste exhaustive relevée dans le filtre « Tous les types de contrats » (/members).
enum ContractType {
APPRENTISSAGE
CDD
CDI
DIRIGEANT_ASSIMILE_SALARIE
DIRIGEANT_NON_SALARIE
EXTRA
INTERIM
STAGIAIRE
SAISONNIER
}
enum ContractStatus { DRAFT ACTIVE ENDED } enum ContractStatus { DRAFT ACTIVE ENDED }
model Amendment { model Amendment {
@@ -334,7 +369,7 @@ model Holiday {
} }
``` ```
**`À VALIDER`** — l'audit décrit `Rest` comme « pause/repos, durée théorique, extension », ce qui est ambigu. Ce plan tranche : les pauses **dans** un shift sont `Shift.breakMinutes` ; `Rest` marque un repos **de journée** (hebdomadaire ou compensateur). Faire confirmer avant le lot 2. **`À VALIDER`** — l'audit décrit `Rest` comme « pause/repos, durée théorique, extension », ce qui est ambigu. Ce plan tranche : les pauses **dans** un shift sont `Shift.breakMinutes` ; `Rest` marque un repos **de journée** (hebdomadaire ou compensateur). Faire confirmer avant WP-04.
### 4.5 Absences et compteurs ### 4.5 Absences et compteurs
@@ -348,6 +383,7 @@ model AbsenceType {
isPaid Boolean isPaid Boolean
countsAsWorkTime Boolean countsAsWorkTime Boolean
affectsPaidLeaveAccrual Boolean affectsPaidLeaveAccrual Boolean
isSocialSecurity Boolean @default(false) // maladie, maternité, AT — filtre dédié au journal des absences
silaeCode String? // partie <code> de AB-<code> silaeCode String? // partie <code> de AB-<code>
requiresJustification Boolean @default(false) requiresJustification Boolean @default(false)
minNoticeDays Int? minNoticeDays Int?
@@ -434,6 +470,8 @@ model PayPeriod {
status PayPeriodStatus @default(OPEN) status PayPeriodStatus @default(OPEN)
lockedAt DateTime? lockedAt DateTime?
lockedBy String? lockedBy String?
unlockedAt DateTime? // dernier déverrouillage — sert à dériver la péremption des exports
unlockedBy String?
version Int @default(0) version Int @default(0)
@@unique([locationId, startDate, endDate, kind]) @@unique([locationId, startDate, endDate, kind])
} }
@@ -470,7 +508,18 @@ model PayrollExport {
enum ExportFormat { SILAE GENERIC_CSV RAW } enum ExportFormat { SILAE GENERIC_CSV RAW }
``` ```
**Invariant période** — le verrouillage écrit les `PayPeriodSnapshot` et interdit toute mutation de `Shift`, `TimeOff` ou heures réelles dont la `localDate` tombe dans la période. Une correction postérieure passe par une **régularisation sur la période ouverte suivante**, jamais par réécriture. **Invariant période** — le verrouillage écrit les `PayPeriodSnapshot` et interdit toute mutation de `Shift`, `TimeOff` ou heures réelles dont la `localDate` tombe dans la période.
**Le déverrouillage existe.** L'audit montre un menu d'actions dépendant de l'état : une période ouverte propose « Verrouiller la période de paie », une période verrouillée propose « **Déverrouiller** la période de paie ». Les deux proposent « Supprimer la période de paie ».
Conséquences normatives :
- `payroll.period.unlock` rouvre la période aux mutations. L'action est journalisée avec justification obligatoire.
- Un **nouveau verrouillage recalcule intégralement** les instantanés. Ils ne sont donc pas immuables au sens strict : c'est le couple (instantané, `PayrollExport`) qui porte la preuve, et `PayrollExport` reste append-only.
- Un export déjà généré pour une période ensuite déverrouillée est **périmé**. Sans ce signalement, un fichier transmis à Silae cesse de correspondre aux données sans que rien ne l'indique. La péremption est **dérivée**, jamais stockée : `PayrollExport.generatedAt < PayPeriod.unlockedAt`. `PayrollExport` reste ainsi strictement append-only (§3.5).
- La suppression reste possible sur une période verrouillée : elle exige `payroll.period.delete`, une confirmation explicite et une entrée d'audit conservant le périmètre supprimé.
- Une correction sur période close **sans** déverrouillage passe par une régularisation sur la période ouverte suivante.
**Carte de période** — chaque période s'affiche avec son libellé, ses bornes, les compteurs **Entrées**, **Sorties** et **Extras**, la liste des populations incluses, un menu **Actions** (verrouiller/déverrouiller, supprimer) et un menu **Exports** distinct.
### 4.7 Documents, conformité, transverse ### 4.7 Documents, conformité, transverse
@@ -550,7 +599,7 @@ model Notification { id String @id @default(cuid()); membershipId String; notifi
## 5. Catalogue des permissions ## 5. Catalogue des permissions
À semer en base au lot 0. Les codes sont **stables** : le code les référence, jamais les libellés. À semer en base à WP-01. Les codes sont **stables** : le code les référence, jamais les libellés.
**Planning** — `planning.view`, `planning.view_unpublished`, `planning.create`, `planning.create_on_published`, `planning.edit`, `planning.edit_published`, `planning.delete`, `planning.duplicate`, `planning.publish`, `planning.unpublish`, `planning.validate`, `planning.invalidate`, `planning.bulk_actions`, `planning.unassigned.view`, `planning.alerts.view`, `planning.alerts.acknowledge`, `planning.counters.view`, `planning.labels.manage`, `planning.notes.manage`, `planning.print` **Planning** — `planning.view`, `planning.view_unpublished`, `planning.create`, `planning.create_on_published`, `planning.edit`, `planning.edit_published`, `planning.delete`, `planning.duplicate`, `planning.publish`, `planning.unpublish`, `planning.validate`, `planning.invalidate`, `planning.bulk_actions`, `planning.unassigned.view`, `planning.alerts.view`, `planning.alerts.acknowledge`, `planning.counters.view`, `planning.labels.manage`, `planning.notes.manage`, `planning.print`
@@ -564,10 +613,20 @@ model Notification { id String @id @default(cuid()); membershipId String; notifi
**Administration** — `settings.access`, `settings.locations.manage`, `settings.teams.manage`, `settings.agreement.manage`, `settings.jobtitles.manage`, `settings.templates.manage`, `settings.integrations.manage`, `settings.notifications.manage`, `settings.roles.manage`, `role_config.assign_owner_level`, `audit.view` **Administration** — `settings.access`, `settings.locations.manage`, `settings.teams.manage`, `settings.agreement.manage`, `settings.jobtitles.manage`, `settings.templates.manage`, `settings.integrations.manage`, `settings.notifications.manage`, `settings.roles.manage`, `role_config.assign_owner_level`, `audit.view`
**Communication (lot 5)** — `articles.view`, `articles.manage`, `conversations.access` **Communication (WP-11)** — `articles.view`, `articles.manage`, `conversations.access`
### Rôles semés ### Rôles semés
`employee`, `team_manager`, `hr_manager`, `payroll_manager`, `admin`, `owner`. Ces jeux de permissions sont un **point de départ modifiable par le client** ; ils ne doivent jamais être codés en dur dans les écrans. Seul `owner` détient `role_config.assign_owner_level`. Liste exhaustive relevée dans le filtre « Tous les rôles » (`/members`) — **cinq rôles, pas davantage** :
| Clé | Libellé |
|---|---|
| `owner` | Propriétaire |
| `admin` | Admin |
| `director` | Directeur |
| `manager` | Manager |
| `employee` | Employé |
Ces jeux de permissions sont un **point de départ modifiable par le client** ; ils ne doivent jamais être codés en dur dans les écrans. Seul `owner` détient `role_config.assign_owner_level`.
--- ---
@@ -670,7 +729,7 @@ Structure de navigation cible. Les routes sont propres à PlanFlow ; l'audit ser
|---|---| |---|---|
| **Auth** | `/connexion`, `/mot-de-passe/oubli`, `/mot-de-passe/reinitialisation`, `/invitation/:token` | | **Auth** | `/connexion`, `/mot-de-passe/oubli`, `/mot-de-passe/reinitialisation`, `/invitation/:token` |
| **Accueil** | `/` — suivi hebdomadaire, raccourcis, alertes | | **Accueil** | `/` — suivi hebdomadaire, raccourcis, alertes |
| **Planning** | `/planning/semaine`, `/planning/jour`, `/planning/etiquettes`, `/planning/impression` | | **Planning** | `/planning/semaine`, `/planning/jour`, `/planning/etiquettes`, `/planning/mois`, `/planning/presences`, `/planning/impression` |
| **Équipe** | `/equipe`, `/equipe/:id/profil`, `/contrats`, `/documents`, `/absences`, `/temps`, `/acces`, `/compteurs/:counterId` | | **Équipe** | `/equipe`, `/equipe/:id/profil`, `/contrats`, `/documents`, `/absences`, `/temps`, `/acces`, `/compteurs/:counterId` |
| **Absences** | `/absences/a-traiter`, `/calendrier`, `/traitees`, `/expirees` | | **Absences** | `/absences/a-traiter`, `/calendrier`, `/traitees`, `/expirees` |
| **Rapports** | `/rapports/paies`, `/rapports/historique`, `/rapports/heures`, `/rapports/activite` | | **Rapports** | `/rapports/paies`, `/rapports/historique`, `/rapports/heures`, `/rapports/activite` |
@@ -679,6 +738,38 @@ Structure de navigation cible. Les routes sont propres à PlanFlow ; l'audit ser
**Écrans du produit audité volontairement absents** : pointeuse et ses réglages, bulletins de paie et distribution, signature électronique, abonnement et facturation, marketplace et connecteurs de caisse, ADP. **Écrans du produit audité volontairement absents** : pointeuse et ses réglages, bulletins de paie et distribution, signature électronique, abonnement et facturation, marketplace et connecteurs de caisse, ADP.
### Filtres et énumérations d'écran
Relevés à l'audit des menus, exhaustifs. À implémenter tels quels.
| Écran | Filtre | Valeurs |
|---|---|---|
| Équipe | Rôle | Tous · Propriétaire · Admin · Directeur · Manager · Employé |
| Équipe | Type de contrat | Tous · les 9 valeurs de `ContractType` |
| Établissements | État | Établissements actifs · Établissements archivés |
| Profils incomplets | Complétude | Informations RUP et DPAE manquantes · RUP manquantes · DPAE manquantes |
| Journal des absences | Nature | Toutes les absences · Uniquement les absences Sécurité sociale |
| Politiques RTT | Actions de ligne | Assigner des employés · Archiver |
| Période de paie | Actions | Verrouiller **ou** Déverrouiller selon l'état · Supprimer |
**Conséquence sur le modèle** — le filtre « profils incomplets » impose de distinguer, champ par champ, ce qui est **requis pour le registre du personnel (RUP)** de ce qui est **requis pour la DPAE**. Deux jeux de champs obligatoires distincts, tous deux calculables sur un dossier. À porter dans `src/domain/compliance/completeness.ts`.
**Modèles de documents** — les variables disponibles sont **scopées par établissement** (« Variables par établissement »). Un modèle rendu pour un salarié résout ses variables dans le contexte de l'établissement de son contrat.
**Statuts de signature** *(hors périmètre v1, à respecter si le module est construit plus tard)* : Échoué · En attente · En cours d'envoi · Expiré · Signé.
### Vues de planning
Le sélecteur de vue expose **cinq** entrées, liste exhaustive relevée à l'audit :
| Vue | Contenu |
|---|---|
| **Vue par employés** | Grille semaine, lignes = salariés. Vue par défaut. |
| **Vue par jour** | Chronologie horaire d'une journée, groupée par équipe, avec courbe d'effectif. |
| **Vue par étiquettes** | Grille semaine, lignes = étiquettes/postes plutôt que salariés. |
| **Vue par mois** | Vue mensuelle condensée. |
| **Vue des présences et absences** | Vue centrée sur qui est présent et qui est absent. |
Les cinq vues lisent le **même modèle** et partagent règles de validation et état de publication. Ne pas dupliquer la logique par vue — c'est la première source de divergence entre écrans.
### Grille de planning — comportement normatif ### Grille de planning — comportement normatif
Reproduire ces comportements observés à l'écran, avec une interface originale : Reproduire ces comportements observés à l'écran, avec une interface originale :
- Colonnes lundi→dimanche, numéro de semaine, jour courant distingué. - Colonnes lundi→dimanche, numéro de semaine, jour courant distingué.
@@ -699,11 +790,12 @@ Ordre imposé. Chaque lot est livrable, testé et mergeable seul.
### WP-00 — Socle ### WP-00 — Socle
**Dépend de :** rien **Dépend de :** rien
**Livre :** `docker-compose.yml` (app + Postgres 16), projet Next.js 15 TypeScript strict, Prisma, Tailwind + shadcn/ui, Vitest, Playwright, CI GitHub Actions (lint, typecheck, test, build), `.env.example`, `README.md`. **Livre :** `docker-compose.yml` (app + Postgres 16), projet Next.js 15 TypeScript strict, Prisma, Tailwind + shadcn/ui, Vitest, Playwright, CI GitHub Actions (lint, typecheck, test, build), **Content-Security-Policy restrictive** (§3.7), `.env.example`, `README.md`.
**Critères d'acceptation** **Critères d'acceptation**
- `docker compose up` démarre l'application et la base, migrations appliquées. - `docker compose up` démarre l'application et la base, migrations appliquées.
- CI verte sur un dépôt propre. - CI verte sur un dépôt propre.
- `pnpm typecheck` sans erreur en mode strict. - `pnpm typecheck` sans erreur en mode strict.
- Un test vérifie que la CSP interdit toute origine tierce et qu'aucune dépendance de traçage n'est installée.
### WP-01 — Tenancy, identité, autorisation ### WP-01 — Tenancy, identité, autorisation
**Dépend de :** WP-00 **Dépend de :** WP-00
@@ -735,9 +827,10 @@ Ordre imposé. Chaque lot est livrable, testé et mergeable seul.
### WP-04 — Planning ### WP-04 — Planning
**Dépend de :** WP-03 **Dépend de :** WP-03
**Livre :** `WeeklySchedule`, `Shift`, `Rest`, `DailyNote`, `Label` ; vues semaine, jour, étiquettes ; création, déplacement, redimensionnement, duplication, actions de masse ; shifts non assignés ; brouillon → validé → publié **par équipe** ; notifications de publication ; impression PDF. **Livre :** `WeeklySchedule`, `Shift`, `Rest`, `DailyNote`, `Label` ; **les cinq vues** (employés, jour, étiquettes, mois, présences et absences) ; création, déplacement, redimensionnement, duplication, actions de masse ; shifts non assignés ; brouillon → validé → publié **par équipe** ; notifications de publication ; impression PDF.
**Critères d'acceptation** **Critères d'acceptation**
- Grille conforme à §9, y compris lignes notes et non-assignés. - Grille conforme à §9, y compris lignes notes et non-assignés.
- Les cinq vues lisent le même modèle : une modification faite dans une vue est immédiatement correcte dans les quatre autres — test croisé obligatoire.
- La publication est par équipe et notifie les salariés concernés. - La publication est par équipe et notifie les salariés concernés.
- `planning.view_unpublished` absente masque les semaines non publiées côté serveur. - `planning.view_unpublished` absente masque les semaines non publiées côté serveur.
- Deux sessions modifiant la même semaine → conflit détecté, aucune perte silencieuse. - Deux sessions modifiant la même semaine → conflit détecté, aucune perte silencieuse.
@@ -772,7 +865,9 @@ Ordre imposé. Chaque lot est livrable, testé et mergeable seul.
**Critères d'acceptation** **Critères d'acceptation**
- Sans heures réelles, le prévu fait foi partout. - Sans heures réelles, le prévu fait foi partout.
- Le verrouillage fige les instantanés et **refuse** toute mutation dans la période. - Le verrouillage fige les instantanés et **refuse** toute mutation dans la période.
- Une correction après verrouillage produit une régularisation sur la période suivante, sans réécrire le passé. - Le déverrouillage rouvre les mutations, exige une justification et journalise l'action.
- Un nouveau verrouillage recalcule les instantanés ; tout export antérieur est marqué **périmé**.
- Une correction après verrouillage, sans déverrouiller, produit une régularisation sur la période suivante, sans réécrire le passé.
- Grille, rapport d'heures et instantané donnent des chiffres **identiques** sur un même jeu de données — test croisé obligatoire. - Grille, rapport d'heures et instantané donnent des chiffres **identiques** sur un même jeu de données — test croisé obligatoire.
### WP-08 — Export Silae ### WP-08 — Export Silae
@@ -783,6 +878,7 @@ Ordre imposé. Chaque lot est livrable, testé et mergeable seul.
- Un matricule ou un code manquant fait **échouer** l'export avec la liste des manques ; aucun fichier partiel. - Un matricule ou un code manquant fait **échouer** l'export avec la liste des manques ; aucun fichier partiel.
- Réexport de la même période → `checksum` identique. - Réexport de la même période → `checksum` identique.
- Export refusé sur une période non verrouillée. - Export refusé sur une période non verrouillée.
- Déverrouiller une période marque ses exports antérieurs comme périmés, et l'historique le montre.
### WP-09 — Tableau de bord RH ### WP-09 — Tableau de bord RH
**Dépend de :** WP-08 **Dépend de :** WP-08
@@ -822,15 +918,19 @@ Ordre imposé. Chaque lot est livrable, testé et mergeable seul.
**Tests d'intégration — base réelle** **Tests d'intégration — base réelle**
- Scoping multi-tenant au niveau requête. - Scoping multi-tenant au niveau requête.
- RLS active indépendamment du code applicatif. - RLS active indépendamment du code applicatif.
- Immutabilité du ledger imposée par la base. - Immutabilité du ledger **et de `PayrollExport`** imposée par la base.
- Conflits de version sur écriture concurrente. - Conflits de version sur écriture concurrente.
- Refus de mutation sur une période verrouillée ; acceptation après déverrouillage.
- Absence de dépendance de traçage tierce et CSP effective (§3.7).
**Playwright — parcours traversants** **Playwright — parcours traversants**
1. Construire une semaine, déclencher une alerte, publier après confirmation → acquittement et audit écrits, salarié notifié. 1. Construire une semaine, déclencher une alerte, publier après confirmation → acquittement et audit écrits, salarié notifié.
2. Demander un congé → accepter → barre visible sur la grille, ledger écrit, solde et prévision à jour → annuler → contre-passation, solde restauré. 2. Demander un congé → accepter → barre visible sur la grille, ledger écrit, solde et prévision à jour → annuler → contre-passation, solde restauré.
3. Saisir un écart d'heures → valider → période de paie → verrouiller → export Silae → réexport identique. 3. Saisir un écart d'heures → valider → période de paie → verrouiller → export Silae → réexport identique.
4. Deux sessions sur la même semaine → conflit rendu, pas de perte. 4. **Cycle de déverrouillage** : verrouiller → exporter → déverrouiller → corriger un shift → reverrouiller → l'export initial apparaît **périmé**, le nouvel export diffère du premier.
5. Un manager de l'établissement A tente d'atteindre une ressource de B → refus serveur. 5. Deux sessions sur la même semaine → conflit rendu, pas de perte.
6. Un manager de l'établissement A tente d'atteindre une ressource de B → refus serveur.
7. Passer d'une vue de planning à l'autre après modification → les cinq vues concordent.
**Jeu de données de départ** — un compte, **deux établissements**, plusieurs équipes, une trentaine de salariés mêlant CDI, CDD, temps partiels, un apprenti et **un mineur** (règles dédiées), dont au moins un salarié sans compte utilisateur et un rattaché à deux établissements ; quatre semaines publiées, des absences longues chevauchant des semaines, un jour férié en milieu de congé, des écarts d'heures, une semaine incluant un changement d'heure. **Données entièrement fictives.** **Jeu de données de départ** — un compte, **deux établissements**, plusieurs équipes, une trentaine de salariés mêlant CDI, CDD, temps partiels, un apprenti et **un mineur** (règles dédiées), dont au moins un salarié sans compte utilisateur et un rattaché à deux établissements ; quatre semaines publiées, des absences longues chevauchant des semaines, un jour férié en milieu de congé, des écarts d'heures, une semaine incluant un changement d'heure. **Données entièrement fictives.**
@@ -847,6 +947,7 @@ Ordre imposé. Chaque lot est livrable, testé et mergeable seul.
- **Paie et DSN** — hors périmètre. Responsabilité réglementaire majeure et veille législative permanente. - **Paie et DSN** — hors périmètre. Responsabilité réglementaire majeure et veille législative permanente.
- **Conservation des bulletins** — hors périmètre : PlanFlow ne détient pas de bulletins. - **Conservation des bulletins** — hors périmètre : PlanFlow ne détient pas de bulletins.
- **Auditabilité** — auteur, horodatage, avant/après et justification sur toutes les opérations sensibles ; agrégats explicables depuis leurs sources. - **Auditabilité** — auteur, horodatage, avant/après et justification sur toutes les opérations sensibles ; agrégats explicables depuis leurs sources.
- **Traceurs tiers** — voir §3.7. Interdits par défaut, CSP restrictive livrée et testée dès WP-00. C'est le point sur lequel PlanFlow diverge le plus délibérément du produit audité.
--- ---