From d8366ab0be53e795ae2c65d9dceddce97d5401eb Mon Sep 17 00:00:00 2001 From: louispaulb Date: Tue, 2 Jun 2026 18:06:19 -0400 Subject: [PATCH] =?UTF-8?q?docs:=20spec=20module=20Shifts=20(int=C3=A9gr?= =?UTF-8?q?=C3=A9=20dispatch,=20sans=20paie)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Spec v1 du module de gestion de shifts + approbations dans le dispatch (décision build-our-own vs Frappe HR déjà actée: PG + multitenant + géofence-job). Modèle de données, workflows d'approbation (ShiftRequest/Swap/AttendanceRequest), géofence vs adresse du job dispatch, auto-attendance, auto-roster Timefold, mapping validé vs Frappe HR, intégrations (Authentik/dispatch/ERPNext API), phasage. Co-Authored-By: Claude Opus 4.8 (1M context) --- docs/shifts-module-spec.md | 91 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 docs/shifts-module-spec.md diff --git a/docs/shifts-module-spec.md b/docs/shifts-module-spec.md new file mode 100644 index 0000000..d72a6a3 --- /dev/null +++ b/docs/shifts-module-spec.md @@ -0,0 +1,91 @@ +# Module Shifts — spécification (intégré au Dispatch, sans paie) + +> Statut : spec v1 (2026-06-02). Décision d'architecture déjà prise +> (cf. `memory/project_punch_shift_tool.md`) : **build-our-own dans le domaine +> dispatch**, PAS Frappe HR. Raisons : ERPNext sur **PostgreSQL** (hrms = MariaDB- +> first), multitenant **shared-DB par org** (hrms = site/company-per-tenant), et +> géofence sur **adresse de job dynamique** (hrms = Shift Locations fixes). + +## 1. Périmètre + +Gestion de shifts + **approbations**. **Pas de paie.** On pousse les heures +approuvées vers ERPNext par API (la paie/compta reste côté ERPNext/legacy). + +Validé contre la page Frappe HR « Shifts & Attendance » — toutes les fonctions +shift/approbation sont couvertes (voir mapping §7). + +## 2. Contraintes transverses +- **Multitenant** : `org_id` sur chaque doctype (shared-DB), pour usage interne + Targo/Gigafibre **et** revente future à des tiers. +- **SSO** : Authentik OIDC (comme toutes les apps — cf. `reference_authentik`). +- **Stockage** : Postgres (hub ou base dédiée). Pas de dépendance ERPNext pour le + fonctionnement; sync sortante par API. + +## 3. Modèle de données (doctypes / tables) + +| Entité | Champs clés | +|---|---| +| **ShiftType** | `org_id`, nom, heure_début, heure_fin, durée_pause, seuil_demi_journée (h), seuil_absent (h), **grâce_retard** (min), **grâce_sortie_hâtive** (min), marge_pointage_avant/après (min), couleur | +| **ShiftAssignment** | `org_id`, employee_id, shift_type_id, date_début, date_fin, statut (Active/Inactive), source (manual/schedule/swap) | +| **ShiftSchedule** | `org_id`, nom, patron récurrent (RRULE hebdo), shift_type, équipe/employés, fenêtre de génération | +| **ShiftRequest** | `org_id`, employee_id, shift_type_id, date(s), motif, **approver_id**, statut (Draft→Submitted→Approved/Rejected), historique | +| **ShiftSwapRequest** | `org_id`, requester_id, counterparty_id, assignment_a, assignment_b, statut (Proposé→Accepté_par_pair→Approuvé_gestionnaire/Rejeté) | +| **Punch** (EmployeeCheckin) | `org_id`, employee_id, type (IN/OUT), timestamp, lat, lon, **job_id** (réf dispatch), device, source (mobile/web), within_geofence (bool) | +| **AttendanceDay** | `org_id`, employee_id, date, shift_assignment_id, statut (Présent/Retard/Demi/Absent), heures_calculées, première_IN, dernière_OUT | +| **AttendanceRequest** (régularisation) | `org_id`, employee_id, date, type, valeur_demandée, motif, **approver_id**, statut | + +## 4. Workflows d'approbation +- **ShiftRequest** : employé soumet → `approver` (gestionnaire/département) approuve/rejette + → si approuvé, crée/maj la `ShiftAssignment`. Multi-niveaux possible (chaîne d'approbateurs). +- **ShiftSwapRequest** : demandeur propose → **le pair accepte** → **gestionnaire approuve** + → échange des deux `ShiftAssignment`. Tout refus annule. +- **AttendanceRequest** : employé demande correction → `approver` approuve → maj `AttendanceDay`. +- Approbateurs : configurables par employé / équipe / org. Notifs in-app + (option) SMS via le hub (Twilio). + +## 5. Différenciateur — géofence vs **adresse du job dispatch** +À chaque punch terrain, on calcule la distance entre `(lat,lon)` du punch et +l'**adresse géocodée du job dispatch assigné** (RQA address DB), pas un lieu fixe. +`within_geofence = distance ≤ rayon_org` (configurable, ex. 150 m). Le punch hors +rayon est accepté mais **flaggé** (preuve de présence pour le dispatch). + +## 6. Auto-attendance & auto-rostering +- **Auto-attendance** : à la clôture du shift, `AttendanceDay` est calculé depuis les + punchs vs le `ShiftType` (retard si IN > début+grâce ; sortie hâtive si OUT < fin−grâce ; + demi-journée/absent selon seuils d'heures). +- **Auto-rostering** : **Timefold Solver** (Apache-2.0, successeur d'OptaPlanner) — + même moteur que le dispatch par tags/compétences (`feedback_dispatch_tags`). + Contraintes : couverture des shifts, compétences requises, disponibilités, + équité, coûts. Génère des `ShiftAssignment` proposées (révisables). + +## 7. Mapping Frappe HR → ce build (validé) +| Frappe HR | Ici | +|---|---| +| Shift Type (seuils, grâce) | ShiftType | +| Shift Assignment (+ outil lot) | ShiftAssignment (+ assignation en lot) | +| Repeating schedules / rotating | ShiftSchedule + Timefold | +| Shift Request + approbation multi-niveaux | ShiftRequest (workflow) | +| Roster drag-and-drop + échange | Vue roster + ShiftSwapRequest | +| Check-in/out mobile + géoloc | Punch (app field-tech) | +| Auto-attendance, retard/sortie hâtive | AttendanceDay | +| Attendance regularization | AttendanceRequest | +| Rapports/calendrier | Vues calendrier + exports | +| Paie | ❌ hors périmètre — push heures → ERPNext API | + +## 8. Écrans +- **Ops (desktop)** : calendrier roster (drag-and-drop), définition ShiftType, + assignation en lot, file d'approbations (ShiftRequest/Swap/AttendanceRequest), + tableau de présence, déclencheur Timefold. +- **App field-tech** (déjà existante) : mon shift du jour, **punch IN/OUT** (géoloc + vs job), demander un shift / un échange, demander une régularisation. + +## 9. Intégrations +- **Authentik OIDC** : auth + rôles (employé / gestionnaire / admin org). +- **Dispatch** : `Punch.job_id` ↔ jobs ; le solver partage les contraintes de skills/tags. +- **ERPNext** : push des heures approuvées (par employé/période) via l'API REST + (le hub a déjà `erpFetch` + token). Aucune dépendance hrms. + +## 10. Phasage +- **MVP** : ShiftType, ShiftAssignment (+ lot), ShiftRequest + approbation, Punch + géoloc-vs-job, AttendanceDay auto, calendrier roster. Mono-org (Targo). +- **v2** : ShiftSchedule récurrent + **Timefold** auto-roster, ShiftSwap, + AttendanceRequest, multitenant `org_id` + revente, push ERPNext.