Solveur OR-Tools (services/roster-solver) : couverture, compétences, équité, coût chargé, cadence/efficacité, capacité-par-job ; contraintes dures/souples façon Timefold. Hub (lib/roster.js) : génération via solveur, publication par réécriture de semaine (anti-doublons), demande (effectif ou nb de jobs), cadence/coût/ compétences par tech, pause, congés (Tech Availability + approbation), booking (slots roster-aware / fit 3-dispos / confirm) + portail public /book. Réessai sur serialization failures frappe_pg ; appels ERP séquentiels. Ops : page Planification (grille compacte « J8 », multi-shift, drag-select + undo/redo, modèles de semaine, éditeur cadence&coût, congés, SMS opt-in), page Rendez-vous (répartiteur), jobColor tech en pause → tickets rouges. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
6.7 KiB
Module Shifts — spécification (intégré au Dispatch, sans paie)
Statut : spec v2 (2026-06-03) — architecture résolue. Parcours : on a d'abord exploré Frappe HR sur PostgreSQL (déployé+validé à hr.gigafibre.ca, cf.
reference_frappe_hr_postgres.md) — mais (1) son UI Desk n'est pas assez conviviale (l'usager la compare à Odoo Planning) et (2) elle est déconnectée des vrais techs/jobs qui vivent dans l'ERPNext de FACTURATION (Dispatch Technician/Dispatch Job). → On revient donc à la conclusion d'origine : build-our-own, intégré au domaine dispatch.Décisions arrêtées :
- Moteur (« Roster AI ») = OR-Tools CP-SAT (Python), BÂTI + validé →
services/roster-solver/. Contraintes dures/souples « façon Timefold ».- Données du roster = dans l'ERPNext de facturation (custom doctypes, à côté de Dispatch Technician/Job) → intégration triviale des features dispatch.
- UI = page « Planification » dans Ops (Vue/Quasar, style Odoo : grille Gantt, drag-drop, bouton « Générer »), à côté du Dispatch.
- L'instance Frappe HR (hr.gigafibre.ca) n'est PAS le foyer du roster (techs ailleurs). Gardée seulement si on veut de la vraie RH (soldes de congés) plus tard.
Features demandées par l'usager (2026-06-03) : modèles de shifts → dispo vs requis par jour ; mettre un tech en pause → ses tickets dispatch passent en ROUGE ; demande de vacances ; demande de modification de shift.
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_idsur 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 laShiftAssignment. 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 →
approverapprouve → majAttendanceDay. - 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,
AttendanceDayest calculé depuis les punchs vs leShiftType(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 desShiftAssignmentproposé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.