gigafibre-fsm/docs/features/roster.md
louispaulb f1204ed459 roster(planif): assignation drag-drop + timeline ressource + occupation + nettoyage
Panneau « jobs à assigner » v2 : multi-sélection (cases), groupes parent-enfant
surlignés, heuristique terrain/à distance (activation/netadmin pré-décochés),
pré-total d'heures, aperçu d'occupation PROJETÉE au survol (barre fantôme + badge).

Fix barre d'occupation figée après drop : /roster/assign-job pose désormais un
start_time (premier-trou-libre dans le shift) + garantit duration_h, sinon le job
compte dans les heures mais n'affiche aucun bloc. Nouvel endpoint
/roster/backfill-start-times (idempotent) pour rattraper l'historique.

Infobulle de cellule : nb de jobs + liste triée par priorité (occupancyByTechDay
renvoie jobs[]). Timeline contextuelle par ressource (dialogue, 0 appel réseau).

Lisibilité du drag : fantôme compact semi-transparent décalé sous le curseur
(ne masque plus l'aperçu) + source estompée.

Scoring de priorité : hook proximité (neutre — secteur géré manuellement),
réservé à 20% du score quand la géoloc arrivera.

Refactor hub : helper partagé firstFitStart (assign-job + backfill).
Nettoyage : retrait code mort (onDeleteRosterTag, projUsedH), carte des sections
en tête de PlanificationPage. Doc : docs/features/roster.md + index.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 15:50:17 -04:00

6.8 KiB
Raw Blame History

Planification (Roster AI) & assignation dispatch — Handoff dev

Grille hebdomadaire ressources × jours pour planifier les quarts, la garde, et prendre en charge les jobs dispatch (glisser-déposer, timeline, occupation). Aucune paie : on planifie, on approuve, on assigne.

Surfaces

Couche Fichier Rôle
UI Ops apps/ops/src/pages/PlanificationPage.vue Grille, garde, panneau « jobs à assigner », timeline ressource, dialogues d'impact
Client API apps/ops/src/api/roster.js Wrappers fetch vers targo-hub /roster/*
Backend services/targo-hub/lib/roster.js Endpoints, accès ERPNext, moteur de créneaux, orchestration solveur
Copilote services/targo-hub/lib/roster-assistant.js Actions d'écriture en langage naturel (réassign / SMS / escalade)
Solveur services/roster-solver/ (conteneur OR-Tools, réseau erpnext_erpnext:8090) Propose un horaire (ne publie jamais)
RDV client apps/ops/src/pages/RendezVousPage.vue, portail Lovable Prise de rendez-vous (booking) roster-aware

Doctypes ERPNext (site facturation erp.gigafibre.ca)

  • Dispatch Technician — ressources. Custom fields clés (cf. dispatch-app/frappe-setup/setup_dispatch_custom_fields.py) : efficiency (cadence globale), skills (CSV), skill_levels (JSON {compétence:1-5} = maîtrise), skill_eff (JSON {compétence:facteur} = vitesse par compétence), cost_salary_h/cost_charges_pct/cost_other_h.
  • Shift Template — quarts. on_call (garde : capacité de réserve, jamais offerte au booking).
  • Shift Requirement — besoins de couverture (« dispo vs requis »).
  • Shift Assignment — assignations (statut Proposé/Publié). Le solveur propose, /publish écrit.
  • Tech Availability — congés/absences (Approuvé). long_term = à remplacer (≠ vacances ponctuelles).
  • Dispatch Job — les tickets. Champs utilisés ici : assigned_tech, scheduled_date, start_time, duration_h, priority, parent_job/depends_on/step_order (groupes), booking_*, required_skill.

Concepts UI (PlanificationPage.vue — voir la carte des sections en tête de <script setup>)

  • Garde LIVE : jamais matérialisée. gardeOverlay (règles de rotation) ⊕ manualGarde (touche G, localStorage) → gardeEffective. Recalculée à chaque rendu ⇒ pas de désync. La garde est exclue des heures travaillées et du coût.
  • Filtre compétences (ET) : skillFilter — un tech n'apparaît que s'il a toutes les compétences cochées.
  • Score de priorité : priorityScores = maîtrise (techCompetence 1-5) ⊕ vitesse (techSpeed, efficacité par compétence) ⊕ coût. Hook proximité (techProximity) câblé mais neutre (renvoie null) — secteur géré manuellement via les zones ; réservé à 20 % du score quand la géoloc arrivera.
  • Occupation : occCells combine la fenêtre de shift (bookableH) et les jobs (occByTechDay du hub) → usedH, pct, blocks (barres colorées), jobs (liste triée priorité→heure). ⚠️ un bloc n'apparaît que si le job a un start_time (sinon il compte dans usedH mais reste invisible — d'où le premier-trou-libre, voir hub).
  • Timeline ressource : openTimeline(t) → dialogue listant les jobs de la semaine visible (barre + liste priorisée). 0 appel réseau (réutilise les helpers de cellule). Lien « Dispatch » vers le tableau (/dispatch).
  • Panneau « jobs à assigner » (Outils) : flottant, déplaçable. Multi-sélection (cases), groupes parent-enfant surlignés, heuristique terrain vs à distance (jobIsOnsite : activation/config/netadmin = à distance → pré-décoché), pré-total d'heures, glisser la sélection sur une case → assignation séquentielle, aperçu d'occupation projetée (barre fantôme + badge +Xh → Y%) au survol.
  • Dialogues d'impact : retirer une compétence ou poser une absence sur un tech avec jobs assignés → liste des jobs affectés + candidats classés (qualifiés + libres au créneau) ou « À recontacter ».

Endpoints hub /roster/* (extrait)

Méthode Chemin Usage
GET /technicians /templates /requirements /assignments /coverage /stats /occupancy /absences Lecture grille
POST /generate Solveur OR-Tools (propose)
POST /publish-week Écrit les Shift Assignment (diff : crée/retire seulement le delta)
POST /technician/:id/skills /pause /efficiency /cost Édition ressource
GET /unassigned-jobs Jobs ouverts/On Hold sans tech (+ groupe/dépendances) pour le panneau
POST /assign-job Assigne un job (pose start_time premier-trou-libre + garantit duration_h)
POST /backfill-start-times Pose un start_time sur les jobs déjà assignés sans heure (idempotent)
GET /skill-impact /absence-impact /job-candidates Données des dialogues d'impact
POST /skill-impact/redistribute plan (explicite) / auto / requeue
GET/POST /book/slots /book/fit /book/confirm /book/hold /book/meta /policy Prise de RDV (booking)

Moteur de créneaux partagé : loadBookingDatatechGaps (trous libres) → firstFitStart (premier trou pour une durée, réutilisé par assign-job et backfill-start-times) → bookingSlots / fitBooking.

Pièges (à NE PAS réintroduire)

  • frappe_pg ne supporte PAS la concurrence : erp.list/écritures en séquentiel, jamais Promise.all (sous charge concurrente la connexion PG se réinitialise → « load fail »). Les sauvegardes d'édition sont debouncées.
  • Occupation invisible sans start_time : un job assigné sans heure ne dessine aucun bloc. assign-job pose donc toujours une heure (premier-trou-libre). Le backfill rattrape l'historique. Si le tech n'a aucun quart ce jour-là, on ne peut pas placer de bloc (écart de planification, pas un bug).
  • Scheduler ERPNext en pause : la facturation legacy reste autoritaire jusqu'au cutover.
  • Déploiement : front via apps/ops/deploy.sh (DEPLOY_BASE=/ops/ quasar build + tar/scp → /opt/ops-app/) ; hub via scp lib/roster.js → /opt/targo-hub/lib/ + docker restart targo-hub. Custom fields via le script frappe-setup.

TODO / dette

  • Bloquer réellement (≠ 🔒 visuel) le dépôt d'un job « On Hold » avant son prérequis.
  • Brancher la proximité (lat/lng Service Location ↔ base/secteur du tech) dans techProximity.
  • Deep-link Dispatch filtré tech/date (DispatchPage ne lit pas encore route.query).
  • Apprentissage IA compétence ↔ type de cas (historique des jobs).
  • Refactor : PlanificationPage.vue (~1,5k lignes) gagnerait à extraire des composables (useGarde, useOccupancy, useAssignPanel, useSkillEditor) — non fait (risque sur fichier prod).