Pont : import basé sur l'ADRESSE DE SERVICE (delivery), pas la facturation (compte)

Problème de fond : quand ticket.delivery_id ne se joignait pas, le pont retombait sur l'adresse de
FACTURATION du compte (résidence) → sujet + géocodage faux. Or les 124 tickets ouverts-3301 ont tous un
delivery côté compte (98 via ticket.delivery_id, 26 via le compte).

- fetchTargoTickets : résolution delivery robuste — COALESCE(ticket.delivery_id, delivery du compte AVEC
  coords [plus récent], delivery du compte [plus récent]) → l'adresse de SERVICE est toujours disponible.
- buildJob préfère déjà svcAddr (delivery) au billAddr → sujet + géocodage utilisent le service.
- Rafraîchit le `subject` des jobs encore au pool (open + non assigné) pour refléter l'adresse de service
  (corrige les anciens sujets basés sur la facturation) ; ne touche pas un job déjà dispatché.

Résultat (re-sync) : delivery 26→37, no_coords 6→1, 0 erreur. Jobs ouverts affichent l'adresse de service
(« Camping Sandysun · 32 Bellevue », « Lac des pins · 25 Érable », « Franklin · 35 rue Hilltop »…).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
louispaulb 2026-06-07 17:36:17 -04:00
parent f804c2b49d
commit d56800805e

View File

@ -173,7 +173,11 @@ async function fetchTargoTickets () {
FROM ticket t
LEFT JOIN ticket_dept dd ON dd.id = t.dept_id
LEFT JOIN account a ON a.id = t.account_id
LEFT JOIN delivery dv ON dv.id = t.delivery_id
LEFT JOIN delivery dv ON dv.id = COALESCE(
NULLIF(t.delivery_id, 0),
(SELECT d2.id FROM delivery d2 WHERE d2.account_id = t.account_id AND d2.latitude IS NOT NULL AND d2.latitude <> 0 AND ABS(d2.latitude) > 1 ORDER BY d2.id DESC LIMIT 1),
(SELECT d3.id FROM delivery d3 WHERE d3.account_id = t.account_id ORDER BY d3.id DESC LIMIT 1)
)
WHERE t.status = 'open' AND t.assign_to = ?
ORDER BY t.due_date DESC`,
[TARGO_TECH_STAFF_ID],
@ -266,7 +270,7 @@ async function buildJob (t) {
}
async function findExisting (legacyId) {
const r = await erp.list('Dispatch Job', { filters: [['legacy_ticket_id', '=', legacyId]], fields: ['name', 'status', 'assigned_tech', 'scheduled_date', 'legacy_dept', 'legacy_activation_url', 'legacy_detail', 'latitude', 'longitude', 'service_location'], limit: 1 })
const r = await erp.list('Dispatch Job', { filters: [['legacy_ticket_id', '=', legacyId]], fields: ['name', 'status', 'assigned_tech', 'scheduled_date', 'subject', 'legacy_dept', 'legacy_activation_url', 'legacy_detail', 'latitude', 'longitude', 'service_location'], limit: 1 })
return (r && r[0]) || null
}
@ -313,6 +317,9 @@ async function syncImpl ({ dryRun = false } = {}) {
if (b.payload.latitude != null && (!exHas || isUpgrade)) { patch.latitude = b.payload.latitude; patch.longitude = b.payload.longitude; coordsFilled++ }
if (!ex.service_location && b.payload.service_location) patch.service_location = b.payload.service_location // backfill lien Service Location
if (ex.status === 'open' && !ex.assigned_tech && b.payload.scheduled_date && b.payload.scheduled_date !== ex.scheduled_date) patch.scheduled_date = b.payload.scheduled_date
// Rafraîchit le sujet (qui inclut l'adresse de SERVICE) pour les jobs encore au pool (open + non assigné),
// sans surprendre un tech sur un job déjà dispatché. Corrige les sujets anciens basés sur la facturation.
if (ex.status === 'open' && !ex.assigned_tech && b.payload.subject && ex.subject !== b.payload.subject) patch.subject = b.payload.subject
if (!dryRun && Object.keys(patch).length) {
const r = await erp.update('Dispatch Job', ex.name, patch)
if (r && r.ok) { updated++; details.push({ legacy_id: b.legacy_id, action: 'update', job: ex.name, patch }) }