feat(dispatch): bouton « Activer STB (Ministra) » — lien d'activation TV extrait du ticket legacy (Phase 1)
- pont : extrait le lien connect_ministra.php du ticket_msg (déjà posté par le wizard legacy à la vente)
→ champ legacy_activation_url sur le Dispatch Job (backfill des 10 tickets TV). Read-only legacy, aucun 2e
chemin d'écriture → zéro risque de double-facturation (cf. analyse processus).
- store _mapJob : expose legacyActivationUrl ; RightPanel : bouton violet « 📺 Activer STB (Ministra) »
(MÊME lien que le tech reçoit, ouvre connect_ministra.php avec tid/did/sid/cr/m intacts).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
15976342e4
commit
4b377eb887
|
|
@ -116,6 +116,7 @@ const onDeleteTag = inject('onDeleteTag')
|
|||
<button class="sb-rp-btn" @click="emit('move', panel.data.job, panel.data.tech?.id)">↔ Déplacer / Réassigner</button>
|
||||
<button class="sb-rp-btn" @click="emit('geofix', panel.data.job)">📍 Géofixer sur la carte</button>
|
||||
<a v-if="legacyReplyUrl(panel.data?.job)" class="sb-rp-btn" :href="legacyReplyUrl(panel.data.job)" target="_blank" rel="noopener" style="text-decoration:none;text-align:center">📝 Répondre dans legacy</a>
|
||||
<a v-if="panel.data?.job?.legacyActivationUrl" class="sb-rp-btn" :href="panel.data.job.legacyActivationUrl" target="_blank" rel="noopener" style="text-decoration:none;text-align:center;background:#7e3ff2;color:#fff;border-color:#7e3ff2" title="Connecter / activer le(s) STB sur Ministra (lien legacy du ticket)">📺 Activer STB (Ministra)</a>
|
||||
<button v-if="panel.data?.job?.assignedTech" class="sb-rp-btn sb-ctx-warn" @click="emit('unassign', panel.data.job)">✕ Désaffecter</button>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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++
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user