From 257924c9c607fae106910e7587d668a671123766 Mon Sep 17 00:00:00 2001 From: louispaulb Date: Fri, 24 Jul 2026 14:15:17 -0400 Subject: [PATCH] =?UTF-8?q?feat(legacy-sync):=20child=20ticket=20=E2=86=92?= =?UTF-8?q?=20remonte=20l'arborescence=20parent=20pour=20l'adresse=20li?= =?UTF-8?q?=C3=A9e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Un sous-ticket (child) sans compte/adresse sur lui-même NI sur son parent direct (déjà COALESCÉ dans la requête) récupère maintenant l'adresse LIÉE en remontant TOUTE l'arborescence parent : ancestorLink (MariaDB WITH RECURSIVE, cap 8 niveaux) trouve le 1er ancêtre avec account_id/delivery_id, puis on charge son compte + sa delivery. Ainsi le job du child pointe sur la bonne adresse de service. Co-Authored-By: Claude Opus 4.8 --- .../targo-hub/lib/legacy-dispatch-sync.js | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/services/targo-hub/lib/legacy-dispatch-sync.js b/services/targo-hub/lib/legacy-dispatch-sync.js index 7145b21..1210bb5 100644 --- a/services/targo-hub/lib/legacy-dispatch-sync.js +++ b/services/targo-hub/lib/legacy-dispatch-sync.js @@ -516,7 +516,35 @@ async function cityFromSubject (subject) { return '' } +// Child ticket : remonte TOUTE l'arborescence parent (MariaDB WITH RECURSIVE, cap 8 niveaux) → 1er ancêtre avec +// account_id / delivery_id. La requête principale ne COALESCE que le parent DIRECT ; ceci couvre les sous-tickets profonds. +async function ancestorLink (p, ticketId) { + try { + const [rows] = await p.query( + `WITH RECURSIVE anc AS ( + SELECT id, parent, account_id, delivery_id, 0 lvl FROM ticket WHERE id = ? + UNION ALL + SELECT pt.id, pt.parent, pt.account_id, pt.delivery_id, anc.lvl + 1 + FROM ticket pt JOIN anc ON pt.id = anc.parent WHERE anc.lvl < 8 AND anc.parent > 0) + SELECT (SELECT account_id FROM anc WHERE account_id > 0 ORDER BY lvl LIMIT 1) AS account_id, + (SELECT delivery_id FROM anc WHERE delivery_id > 0 ORDER BY lvl LIMIT 1) AS delivery_id`, [ticketId]) + return (rows && rows[0]) || null + } catch (e) { return null } // MariaDB < 10.2 ou erreur → on ne remonte pas (repli inchangé) +} +async function accountRow (p, id) { if (!id) return null; try { const [r] = await p.query('SELECT first_name, last_name, company, email, cell, tel_home, address1, address2, city, state, zip FROM account WHERE id = ? LIMIT 1', [id]); return (r && r[0]) || null } catch (e) { return null } } +async function deliveryRow (p, id) { if (!id) return null; try { const [r] = await p.query('SELECT latitude AS dv_lat, longitude AS dv_lon, placemarks_id AS dv_pmid, address1 AS dv_addr, city AS dv_city, zip AS dv_zip FROM delivery WHERE id = ? LIMIT 1', [id]); return (r && r[0]) || null } catch (e) { return null } } + async function buildJob (t, { dryRun = false } = {}) { + // Child ticket sans compte/adresse (ni sur lui, ni sur son parent DIRECT déjà COALESCÉ dans la requête) → REMONTE + // l'arborescence parent pour obtenir l'adresse LIÉE au ticket (compte + delivery de l'ancêtre le plus proche qui en a). + if (t.parent && (!t.account_id || (!t.dv_addr && !t.address1))) { + const p = pool() + const anc = p ? await ancestorLink(p, t.id) : null + if (anc) { + if (!t.account_id && anc.account_id) { t.account_id = anc.account_id; const ar = await accountRow(p, anc.account_id); if (ar) Object.assign(t, ar) } + if ((!t.delivery_id || (!t.dv_addr && !t.dv_lat)) && anc.delivery_id) { t.delivery_id = anc.delivery_id; const dr = await deliveryRow(p, anc.delivery_id); if (dr) Object.assign(t, dr) } + } + } const cust = await resolveOrCreateCustomer(t.account_id, dryRun) // crée le compte s'il manque (sauf dry-run) const subjCity = await cityFromSubject(t.subject) // ville RÉELLE du ticket (sujet) → apparie la BONNE Service Location // SL appariée par la ville de SERVICE (sujet > delivery > facturation) — PAS la facturation seule (bug #255465 :