fix(legacy-sync): corrige TOUS les jobs à mauvaise ville + retire l'outil previewTicket
Suite du correctif d'adressage : le sync ne rafraîchissait l'adresse que sur les jobs open+non assignés. Il CORRIGE maintenant adresse + coords + Service Location + sujet dès que la VILLE stockée diffère de l'adresse de service recalculée (wrongCity via cityMatch), même sur un job déjà assigné (le ticket F est encore ouvert → le tech doit avoir la bonne ville). Le cron tourne déjà en allDates, donc tous les jobs ouverts sont corrigés en continu. previewTicket retiré (pas besoin d'outil). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
00b13e06fd
commit
03368b0a7b
|
|
@ -824,17 +824,21 @@ async function syncImpl ({ dryRun = false } = {}) {
|
|||
// existantes (souvent issues du Service Location, moins précises). delivery écrase ; SL/RQA non.
|
||||
const hasCoord = (v) => v != null && v !== '' && Math.abs(parseFloat(v)) > 0.0001
|
||||
const exHas = hasCoord(ex.latitude) && hasCoord(ex.longitude)
|
||||
// MAUVAISE VILLE : le job existant porte une ville ≠ de l'adresse de service recalculée (ex. #255465 stocké
|
||||
// Sainte-Clotilde vs service réel Saint-Cyprien). On CORRIGE alors adresse + coords + SL + sujet MÊME si le job
|
||||
// est déjà assigné : le ticket F est encore ouvert (intervention à venir) → le tech doit avoir la BONNE ville.
|
||||
const cityOfAddr = (s) => { const p = String(s || '').split(','); return p.length >= 2 ? p[p.length - 2].trim() : '' }
|
||||
const wrongCity = !!(b.payload.address && ex.address && (() => { const a = cityOfAddr(ex.address); const c = cityOfAddr(b.payload.address); return a && c && !cityMatch(a, c) })())
|
||||
// delivery (point exact) ET camping (géoloc fixe du camping vs résidence) ÉCRASENT des coords existantes différentes ; SL/RQA/Mapbox non.
|
||||
const isUpgrade = (b.matched.coord_src === 'delivery' || b.matched.coord_src === 'camping') && exHas &&
|
||||
(Math.abs(parseFloat(ex.latitude) - b.payload.latitude) > 1e-5 || Math.abs(parseFloat(ex.longitude) - b.payload.longitude) > 1e-5)
|
||||
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
|
||||
// Adresse de SERVICE : backfill si jamais écrite (cas des 175 jobs historiques) ; et rafraîchit pour les jobs encore au pool (open + non assigné).
|
||||
if (b.payload.address && (!ex.address || (ex.status === 'open' && !ex.assigned_tech && ex.address !== b.payload.address))) patch.address = b.payload.address
|
||||
if (b.payload.latitude != null && (!exHas || isUpgrade || wrongCity)) { patch.latitude = b.payload.latitude; patch.longitude = b.payload.longitude; coordsFilled++ }
|
||||
if (b.payload.service_location && (!ex.service_location || wrongCity)) patch.service_location = b.payload.service_location // backfill / CORRIGE le lien Service Location (mauvaise ville)
|
||||
// Adresse de SERVICE : backfill si jamais écrite (jobs historiques) ; rafraîchit au pool (open + non assigné) ; et CORRIGE TOUJOURS une mauvaise ville.
|
||||
if (b.payload.address && (!ex.address || wrongCity || (ex.status === 'open' && !ex.assigned_tech && ex.address !== b.payload.address))) patch.address = b.payload.address
|
||||
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
|
||||
// Rafraîchit le sujet (qui inclut la ville de service) au pool (open + non assigné), OU quand la ville était FAUSSE.
|
||||
if ((wrongCity || (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 }) }
|
||||
|
|
@ -2793,31 +2797,4 @@ async function provisionIdentities ({ dryRun = true, scope = 'ledger' } = {}) {
|
|||
return res
|
||||
}
|
||||
|
||||
// Dry-run CIBLÉ d'UN ticket legacy (0 écriture) → adresse/coords résolues + ville sujet/delivery/facturation.
|
||||
// Outil de débogage adressage/routage : `require('./lib/legacy-dispatch-sync').previewTicket(255465)`.
|
||||
async function previewTicket (id) {
|
||||
const p = pool(); if (!p) return { error: 'legacy DB indisponible' }
|
||||
const [rows] = await p.query(
|
||||
`SELECT t.id, t.subject, t.dept_id, dd.name AS dept, t.due_date, t.due_time, t.priority,
|
||||
COALESCE(NULLIF(t.account_id, 0), NULLIF(pt.account_id, 0)) AS account_id,
|
||||
COALESCE(NULLIF(t.delivery_id, 0), NULLIF(pt.delivery_id, 0)) AS delivery_id,
|
||||
t.parent, t.date_create, t.last_update,
|
||||
a.first_name, a.last_name, a.company, a.email, a.cell, a.tel_home, a.address1, a.address2, a.city, a.state, a.zip,
|
||||
dv.latitude AS dv_lat, dv.longitude AS dv_lon, dv.placemarks_id AS dv_pmid, dv.address1 AS dv_addr, dv.city AS dv_city, dv.zip AS dv_zip,
|
||||
(SELECT mm3.msg FROM ticket_msg mm3 WHERE mm3.ticket_id = t.id ORDER BY mm3.id ASC LIMIT 1) AS first_msg
|
||||
FROM ticket t
|
||||
LEFT JOIN ticket pt ON pt.id = t.parent
|
||||
LEFT JOIN ticket_dept dd ON dd.id = t.dept_id
|
||||
LEFT JOIN account a ON a.id = COALESCE(NULLIF(t.account_id, 0), NULLIF(pt.account_id, 0))
|
||||
LEFT JOIN delivery dv ON dv.id = COALESCE(NULLIF(t.delivery_id, 0), NULLIF(pt.delivery_id, 0),
|
||||
(SELECT d2.id FROM delivery d2 WHERE COALESCE(NULLIF(t.account_id,0),NULLIF(pt.account_id,0))>0 AND d2.account_id=COALESCE(NULLIF(t.account_id,0),NULLIF(pt.account_id,0)) 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 COALESCE(NULLIF(t.account_id,0),NULLIF(pt.account_id,0))>0 AND d3.account_id=COALESCE(NULLIF(t.account_id,0),NULLIF(pt.account_id,0)) ORDER BY d3.id DESC LIMIT 1))
|
||||
WHERE t.id = ? LIMIT 1`, [id])
|
||||
if (!rows || !rows[0]) return { error: 'ticket ' + id + ' introuvable' }
|
||||
const t = rows[0]
|
||||
const subjCity = await cityFromSubject(t.subject)
|
||||
const b = await buildJob(t, { dryRun: true })
|
||||
return { legacy_id: b.legacy_id, subject: b.payload.subject, subject_city: subjCity, dv_city: t.dv_city, bill_city: t.city, addr_ticket: b.addr, job_address: b.payload.address, latitude: b.payload.latitude, longitude: b.payload.longitude, coord_src: b.matched.coord_src, service_location: b.payload.service_location }
|
||||
}
|
||||
|
||||
module.exports = { handle, sync, previewTicket, reimportAddresses, fillMissingCoords, fixGeocoding, purgeStaleOrphans, pushAssignments, returnToPool, postTicketLegacy, ticketThread, ticketAssignState, watchLegacy, techSyncReport, techSyncApply, mineDurations, legacyTechTickets, legacyWindowLoad, ingestAssigned, reconcileLegacyJobs, backfillServiceLocations, backfillIssueCustomers, backfillSourceIssue, backfillDependsOn, geolocateJobs, fibreByDelivery, startSync, stopSync, fetchTargoTickets, staleTickets, staleNudge, publishPreview, publishJob, techHistoryBackfill, techHistory, provisionIdentities, coord, prio, startTime, jobType, inTerritory, geocodeRQA, persistGeocodeToSL, reverseNearestFibre } // parseurs purs exposés pour les tests
|
||||
module.exports = { handle, sync, reimportAddresses, fillMissingCoords, fixGeocoding, purgeStaleOrphans, pushAssignments, returnToPool, postTicketLegacy, ticketThread, ticketAssignState, watchLegacy, techSyncReport, techSyncApply, mineDurations, legacyTechTickets, legacyWindowLoad, ingestAssigned, reconcileLegacyJobs, backfillServiceLocations, backfillIssueCustomers, backfillSourceIssue, backfillDependsOn, geolocateJobs, fibreByDelivery, startSync, stopSync, fetchTargoTickets, staleTickets, staleNudge, publishPreview, publishJob, techHistoryBackfill, techHistory, provisionIdentities, coord, prio, startTime, jobType, inTerritory, geocodeRQA, persistGeocodeToSL, reverseNearestFibre } // parseurs purs exposés pour les tests
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user