diff --git a/services/targo-hub/lib/dispatch.js b/services/targo-hub/lib/dispatch.js index c07cd00..9a0fead 100644 --- a/services/targo-hub/lib/dispatch.js +++ b/services/targo-hub/lib/dispatch.js @@ -207,12 +207,12 @@ async function suggestSlots ({ duration_h = 1, latitude, longitude, after_date, const shift = winBy[tech.technician_id + '|' + dateStr] || winBy[tech.name + '|' + dateStr] if (!shift) continue - // Day's pinned jobs (only those with a real start_time — floating jobs - // without a time are ignored since we don't know when they'll land). + // Day's pinned jobs (only those with a real start_time) — servent à découper les trous. + // Match par technician_id OU docname (les jobs legacy portent parfois l'un ou l'autre). + const belongs = (j) => (j.assigned_tech === tech.technician_id || j.assigned_tech === tech.name) && + j.scheduled_date === dateStr && !(ignoreReserved && j.job_type === 'Réservation') const dayJobs = allJobs - .filter(j => j.assigned_tech === tech.technician_id && - j.scheduled_date === dateStr && j.start_time && - !(ignoreReserved && j.job_type === 'Réservation')) // mode urgence/réparation → les blocs RÉSERVÉS (soft) ne bloquent pas + .filter(j => belongs(j) && j.start_time) .map(j => { const s = timeToHours(j.start_time) return { @@ -223,6 +223,16 @@ async function suggestSlots ({ duration_h = 1, latitude, longitude, after_date, }) .sort((a, b) => a.start_h - b.start_h) + // « Humainement possible » : un tech ne peut pas dépasser son quart. Les jobs assignés SANS heure fixée + // (legacy osTicket) occupent quand même la journée → on retranche leur charge + celle déjà placée ; si la + // capacité restante ne suffit pas pour cette durée, PAS de créneau ce jour (sinon on suroffre un tech déjà plein). + const shiftH = shift.end_h - shift.start_h + const clampD = (j) => Math.max(0, Math.min(j.end_h, shift.end_h) - Math.max(j.start_h, shift.start_h)) + const timedBusy = dayJobs.reduce((a, j) => a + clampD(j), 0) + const untimedLoad = allJobs.filter(j => belongs(j) && !j.start_time) + .reduce((a, j) => a + (parseFloat(j.duration_h) || 1), 0) + if (timedBusy + untimedLoad + duration > shiftH + 0.01) continue + // Build gaps bounded by shift_start/end (shift = quart réel du tech ce jour, résolu ci-dessus). const gaps = [] let cursor = shift.start_h, prevCoords = homeCoords