Créneaux types : paramétrage + génération auto des créneaux

Le paramétrage des créneaux se fait dans « Règles & édition » : la coordination
définit des créneaux types (DayPart). À la création d'un poste, un créneau à
besoin 0/0 est généré pour chaque jour de l'édition et chaque créneau type ;
les coordinateurs ajustent ensuite min/idéal.

Back-end :
- DayPart CRUD (GET/POST/PATCH/DELETE /editions/{id}/day-parts). Code figé
  après création (slugifié depuis le libellé si absent), unique par édition.
- Shift.day_part_id (nullable, SET NULL) + migration 0004.
- create_position génère les créneaux ; POST .../positions/{pid}/generate-shifts
  recrée les manquants (idempotent, ne touche pas aux besoins déjà réglés).
- 13 tests (day-parts, génération, extension d'édition, ajout de type).

Front-end :
- api/hooks/mock : dayParts CRUD + positions.generateShifts.
- ConfigRules : panneau « Créneaux types » (libellé + horaires éditables,
  ajout, suppression).
- ConfigGrid : bouton « Générer les créneaux » sur l'en-tête du poste.
- GrillePage : case « à définir » (gris, glyphe ·) quand min=idéal=0, avec
  entrée de légende.

155 tests back OK ; typecheck + lint + build + smoke front OK.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017uUsRg7XzopkCPWimwg9ZM
This commit is contained in:
AdamThomas
2026-09-10 20:57:51 +02:00
co-authored by Claude Sonnet 5
parent cee581b832
commit f58ec00e30
21 changed files with 834 additions and 16 deletions
+19 -10
View File
@@ -57,10 +57,12 @@ les tables sauf les tables d'association.
- **`Edition`** : `year`, `name` (opt.), `start_date`, `end_date`,
`retention_days` (durée de rétention des données nominatives en jours,
`NULL` = pas de purge).
- **`DayPart`** : découpage d'une journée propre à l'édition (nombre et bornes
libres, paramétrable par la coordination). `code`, `label`, `start_time`,
`end_time` (`end_time < start_time` ⇒ passe minuit), `order`. Unique
(`edition_id`, `code`).
- **`DayPart`** (« créneau type ») : découpage d'une journée propre à l'édition
(nombre et bornes libres). `code`, `label`, `start_time`, `end_time`
(`end_time < start_time` ⇒ passe minuit), `order`. Unique (`edition_id`,
`code`) ; `code` figé après création (clé de l'import des disponibilités).
CRUD `GET/POST/PATCH/DELETE /editions/{id}/day-parts` (COORDINATION pour
l'écriture).
- **`Volunteer`** : `email` (unique **par édition**, nullable pour les
bénévoles créés à la main — plusieurs `NULL` autorisés), `first_name`,
`last_name`, `phone`, `tshirt_size`, `diet`, `comment`. Pas de date de
@@ -69,8 +71,13 @@ les tables sauf les tables d'association.
(`volunteer_skill`). N-N `Shift` (`shift_required_skill`).
- **`Position`** : `name`, `location`, `category` (`PositionCategory`),
`lead_account_id` (→ `Account`, `SET NULL` à la suppression du compte).
- **`Shift`** : `position_id`, `starts_at`, `ends_at` (peut finir le
lendemain), `min_staff`, `ideal_staff`, compétences requises (N-N `Skill`).
- **`Shift`** : `position_id`, `day_part_id` (nullable ; le créneau type dont
il est issu, `SET NULL` si le type est supprimé), `starts_at`, `ends_at`
(peut finir le lendemain), `min_staff`, `ideal_staff`, compétences requises
(N-N `Skill`). À la création d'un `Position`, un `Shift` à 0/0 est généré par
jour de l'édition et par `DayPart` ;
`POST /editions/{id}/positions/{pid}/generate-shifts` recrée les manquants
(après ajout d'un créneau type ou extension des dates).
- **`Availability`** : `volunteer_id`, `date`, `day_part_id`, `status`
(`AvailabilityStatus`). Unique (`volunteer_id`, `date`, `day_part_id`).
- **`Assignment`** : `volunteer_id`, `shift_id`, `source` (`AssignmentSource`,
@@ -253,9 +260,10 @@ fonctions d'IA. *(L'envoi d'emails par SMTP est en place — voir § 2.)*
│ │ ├── db.py # engine + session
│ │ ├── logging_config.py # RGPD : pas de PII dans les logs
│ │ ├── api/ # routers + schemas Pydantic + deps
│ │ │ # health, auth, accounts, imports,
│ │ │ # volunteers, positions, shifts,
│ │ │ # assignments, matrix, notifications
│ │ │ # health, auth, accounts, editions,
│ │ │ # day_parts, imports, volunteers,
│ │ │ # positions, shifts, assignments,
│ │ │ # matrix, notifications
│ │ ├── auth/ # mot de passe + lien magique + JWT + rôles
│ │ ├── email/ # SMTP (sender) + rendu des emails (templates)
│ │ ├── importing/ # import CSV/Excel (parser, values, service)
@@ -342,7 +350,8 @@ Aucune dépendance locale requise hors Docker.
| 8 | Second système : connexion mot de passe (bcrypt) pour la coordination | validée |
| 9 | Suppression du statut d'affectation (plus d'étape de confirmation) | validée |
| 10 | Envoi d'emails par SMTP : lien magique + récapitulatif de planning | validée |
| 11 | `GET`/`PATCH /editions/{id}` (nom, dates) — ajouter/retirer des jours ; UX config front | à valider |
| 11 | `GET`/`PATCH /editions/{id}` (nom, dates) — ajouter/retirer des jours ; UX config front | validée |
| 12 | Créneaux types (`DayPart` CRUD) + génération auto des créneaux 0/0 à la création d'un poste ; case « à définir » / « sureffectif » sur la grille | à valider |
**Reste hors périmètre** (modèle prêt à les accueillir) : solveur OR-Tools,
SMS, constructeur de formulaire, pointage sur site, IA. Frontend :