suggestSlots was skill-blind and assumed a default 8-17 shift for every
tech. Now:
- SKILL FILTER: only techs whose skills (or _user_tags) include the
requested skill are considered. Dialog passes the picked chip's skill.
- REAL SHIFTS: slot windows come from published Shift Assignment ×
Shift Template (garde/on_call excluded). A tech with no regular shift
that day yields no slot — so it reflects reality (needs shifts created).
- WEEKENDS reserved for repairs: Sat/Sun skipped unless the skill is a
repair/dépannage. Removed SLOT_DEFAULT_SHIFT.
- Empty-state message hints to publish a shift / weekend rule.
Verified: with only garde shifts in the horizon, correctly returns 0
(regular_count=0). Skill/weekend/garde-exclusion logic confirmed via
data diagnostic. Produces slots once a regular day-shift is published for
a qualified tech.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Planificateur « Suggérer » : 4 stratégies (smart / meilleurs d'abord / équilibré /
juste ce qu'il faut), compétences+niveaux par tech (édition inline), niveau requis
par compétence + par job, carte des tournées (1 couleur/tech, domicile→arrêts,
sélecteur de jour), fenêtre de dispatch auj.+demain (dates sélectionnées), règle
week-end + placeholder « en attente du quart », clustering + lasso + filtre-date
sur la carte, accès rapide « À assigner » (badge).
Boîte : liste /conversations allégée (45 Mo → ~1 Mo, 17×) + messages chargés à
l'ouverture. Rapports : cache SWR sur revenue-explorer (22×). Session : keep-alive
+ timeout fetch global + authFetch durci → fin des rechargements manuels.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Inbox identity/grouping (fixes mislabeled threads):
- customerName = real From display name, then a customer matched BY EMAIL,
then the raw address — never the AI's content-guessed name (a gilles@ relay
showed as "Sylvie Juteau").
- never group a thread by one of our own domains (support@targo.ca,
*@targointernet.com): re-ingested outbound was collapsing unrelated threads
into one mislabeled "Guylaine Gagnon" thread.
Refactor: queues/TYPES/QUEUE_OF/CAT centralized in lib/categories.js (were
drifting across conversation.js + inbox-triage.js; telephonie/television had
no matching triage type). Removed dead export findConversationByEmail.
Feat: addMessage stamps msg.agent on outbound replies -> per-agent stats.
Historique (/historique, HistoriquePage.vue): tournées par technicien (ALL
statuses incl. Completed/Cancelled — the board hid them), tech leaderboard,
inbox leaderboard. Hub: /dispatch/history, /dispatch/leaderboard,
/conversations/leaderboard.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- contracts.js: _inferPlanName now reads the Quotation's first positive-rate
item ("Internet Megafibre 80 Mbps") instead of generic fallback.
- contracts.js: subPayload writes service_contract back-ref so an active/
pending sub blocks its parent contract's deletion (LinkExistsError).
- contracts.js: GET /contract/audit-orphans[?fix=1] scans for orphaned subs
(dangling contract link or no link at all) and contracts without a sub;
filters out 2026-03-29 legacy-migration batch via LEGACY_CUTOFF.
- dispatch.js: deleteJobSafely() rewires children's depends_on to the
victim's parent, re-parents descendants if victim was chain root, then
deletes. POST /dispatch/job-delete exposes it. Fixes LinkExistsError
when users delete a middle step in the UI.
- TaskNode.vue: confirmDelete calls /dispatch/job-delete and surfaces a
warning when dependents will be rewired.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- contracts.js: built-in install chain fallback when no Flow Template matches
on_contract_signed — every accepted contract now creates a master Issue +
chained Dispatch Jobs (fiber_install template) so we never lose a signed
contract to a missing flow config.
- acceptance.js: export createDeferredJobs + propagate assigned_group into
Dispatch Job payload (was only in notes, not queryable).
- dispatch.js: chain-walk helpers (unblockDependents, _isChainTerminal,
setJobStatusWithChain) + terminal-node detection that activates pending
Service Subscriptions (En attente → Actif, start_date=tomorrow) and emits
a prorated Sales Invoice covering tomorrow → EOM. Courtesy-day billing
convention: activation day is free, first period starts next day.
- dispatch.js: fix Sales Invoice 417 by resolving company default income
account (Ventes - T) and passing company + income_account on each item.
- dispatch.js: GET /dispatch/group-jobs + POST /dispatch/claim-job for tech
self-assignment from the group queue; enriches with customer_name /
service_location via per-job fetches since those fetch_from fields aren't
queryable in list API.
- TechTasksPage.vue: redesigned mobile-first UI with progress arc, status
chips, and new "Tâches du groupe" section showing claimable unassigned
jobs with a "Prendre" CTA. Live updates via SSE job-claimed / job-unblocked.
- NetworkPage.vue + poller-control.js: poller toggle semantics flipped —
green when enabled, red/gray when paused; explicit status chips for clarity.
E2E verified end-to-end: CTR-00007 → 4 chained jobs → claim → In Progress →
Completed walks chain → SUB-0000100002 activated (start=2026-04-24) →
SINV-2026-700012 prorata $9.32 (= 39.95 × 7/30).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>