diff --git a/apps/ops/src/modules/dispatch/components/RightPanel.vue b/apps/ops/src/modules/dispatch/components/RightPanel.vue
index a156496..04f19ba 100644
--- a/apps/ops/src/modules/dispatch/components/RightPanel.vue
+++ b/apps/ops/src/modules/dispatch/components/RightPanel.vue
@@ -116,6 +116,7 @@ const onDeleteTag = inject('onDeleteTag')
📝 Répondre dans legacy
+ 📺 Activer STB (Ministra)
diff --git a/apps/ops/src/stores/dispatch.js b/apps/ops/src/stores/dispatch.js
index 1edd46c..8e358e6 100644
--- a/apps/ops/src/stores/dispatch.js
+++ b/apps/ops/src/stores/dispatch.js
@@ -58,6 +58,7 @@ export const useDispatchStore = defineStore('dispatch', () => {
jobType: j.job_type || null,
legacyDept: j.legacy_dept || null, // département osTicket legacy → coloriage par type
legacyTicketId: j.legacy_ticket_id || null, // n° ticket legacy (affiché dans le panneau détail)
+ legacyActivationUrl: j.legacy_activation_url || null, // lien connect_ministra (activation STB TV)
parentJob: j.parent_job || null,
stepOrder: j.step_order || 0,
onOpenWebhook: j.on_open_webhook || null,
diff --git a/services/targo-hub/lib/legacy-dispatch-sync.js b/services/targo-hub/lib/legacy-dispatch-sync.js
index 0e4c3b8..aafa21f 100644
--- a/services/targo-hub/lib/legacy-dispatch-sync.js
+++ b/services/targo-hub/lib/legacy-dispatch-sync.js
@@ -63,11 +63,19 @@ function pool () {
return _pool
}
+// Lien d'activation STB/Ministra : DÉJÀ posté dans le fil du ticket par le wizard legacy à la vente.
+// On le ré-extrait tel quel (zéro reconstruction). Sous-requête = le ticket_msg le plus récent qui le contient.
+const ACTIVATION_RE = /https?:\/\/[^\s"'<>]*connect_ministra\.php[^\s"'<>]*/i
+function extractActivationUrl (msg) { if (!msg) return ''; const m = String(msg).match(ACTIVATION_RE); return m ? m[0] : '' }
+
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,
- a.first_name, a.last_name, a.company, a.address1, a.address2, a.city, a.state, a.zip
+ 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%'
+ ORDER BY mm.id DESC LIMIT 1) AS activation_msg
FROM ticket t
LEFT JOIN ticket_dept dd ON dd.id = t.dept_id
LEFT JOIN account a ON a.id = t.account_id
@@ -126,6 +134,7 @@ async function buildJob (t) {
legacy_ticket_id: String(t.id),
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 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
@@ -138,7 +147,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'], limit: 1 })
+ const r = await erp.list('Dispatch Job', { filters: [['legacy_ticket_id', '=', legacyId]], fields: ['name', 'status', 'assigned_tech', 'scheduled_date', 'legacy_dept', 'legacy_activation_url'], limit: 1 })
return (r && r[0]) || null
}
@@ -158,6 +167,7 @@ async function sync ({ dryRun = false } = {}) {
// s'il est encore au pool (open + non assigné) → on ne clobbe jamais le travail du répartiteur.
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.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++