From b7b7da783b753a82b9a4e123308606229c876f91 Mon Sep 17 00:00:00 2001 From: louispaulb Date: Sat, 6 Jun 2026 13:07:15 -0400 Subject: [PATCH] =?UTF-8?q?feat(dispatch):=20date=20d'ouverture=20+=20M?= =?UTF-8?q?=C3=A0J=20en=20t=C3=AAte=20du=20d=C3=A9tail=20ticket=20(mouseov?= =?UTF-8?q?er)=20pour=20juger=20la=20fermeture?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - pont : SELECT date_create+last_update ; legacy_detail préfixé « 🗓 Ouvert · MàJ » puis description. Backfill = réécrit legacy_detail si différent (idempotent). Visible dans le mouseover du panneau roster + le détail Dispatch. Ex. révélé : LEG-111325 ouvert 2021 jamais touché → candidat fermeture évident. Co-Authored-By: Claude Opus 4.8 (1M context) --- services/targo-hub/lib/legacy-dispatch-sync.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/services/targo-hub/lib/legacy-dispatch-sync.js b/services/targo-hub/lib/legacy-dispatch-sync.js index 57ad947..e083ef1 100644 --- a/services/targo-hub/lib/legacy-dispatch-sync.js +++ b/services/targo-hub/lib/legacy-dispatch-sync.js @@ -84,6 +84,7 @@ async function fetchTargoTickets () { const p = pool(); if (!p) throw new Error('mysql2 indisponible sur le hub') 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, t.bon_id, t.account_id, + t.date_create, t.last_update, a.first_name, a.last_name, a.company, a.address1, a.address2, a.city, a.state, a.zip, (SELECT mm.msg FROM ticket_msg mm WHERE mm.ticket_id = t.id AND mm.msg LIKE '%connect_ministra%' @@ -149,7 +150,10 @@ async function buildJob (t) { legacy_dept: t.dept || '', // département legacy granulaire → coloriage « comme legacy » (Installation Fibre / Réparation Fibre / Télé / Téléphonie…) } const actUrl = extractActivationUrl(t.activation_msg); if (actUrl) payload.legacy_activation_url = actUrl // lien connect_ministra (déjà dans le fil) - const detail = stripHtml(t.first_msg); if (detail) payload.legacy_detail = detail // description/contenu du ticket legacy → visible dans Ops + // En-tête de dates (ouverture + dernière MàJ) pour juger l'ancienneté → décider de fermer ; puis la description. + const dateHdr = '🗓 Ouvert ' + (tzDate(t.date_create) || '?') + (t.last_update ? ' · MàJ ' + tzDate(t.last_update) : '') + const detail = [dateHdr, stripHtml(t.first_msg)].filter(Boolean).join('\n\n') + if (detail) payload.legacy_detail = detail // description + dates → visible dans Ops (mouseover panneau + détail Dispatch) const sd = tzDate(t.due_date); if (sd) payload.scheduled_date = sd const st = startTime(t.due_time); if (st) payload.start_time = st if (cust) payload.customer = cust.name @@ -183,7 +187,7 @@ async function sync ({ dryRun = false } = {}) { const patch = {} if (!ex.legacy_dept && b.payload.legacy_dept) patch.legacy_dept = b.payload.legacy_dept if (!ex.legacy_activation_url && b.payload.legacy_activation_url) patch.legacy_activation_url = b.payload.legacy_activation_url // backfill lien activation (sans risque) - if (!ex.legacy_detail && b.payload.legacy_detail) patch.legacy_detail = b.payload.legacy_detail // backfill description du ticket + if (b.payload.legacy_detail && ex.legacy_detail !== b.payload.legacy_detail) patch.legacy_detail = b.payload.legacy_detail // (re)backfill description + dates (idempotent : ne réécrit que si différent) 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 if (!dryRun && Object.keys(patch).length) { await erp.update('Dispatch Job', ex.name, patch); updated++; details.push({ legacy_id: b.legacy_id, action: 'update', job: ex.name, patch }) } else skipped++