From 27263d7766a2e37686953dde75fedf9ce33d51bd Mon Sep 17 00:00:00 2001 From: louispaulb Date: Tue, 7 Jul 2026 08:50:01 -0400 Subject: [PATCH] =?UTF-8?q?tickets:=20distinguer=20par=20d=C3=A9partement?= =?UTF-8?q?=20+=20technicien=20assign=C3=A9=20;=20r=C3=A9ponse=20courriel?= =?UTF-8?q?=20=3D=20Received?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - TicketCard : badge Département (issue_type) + technicien (assigned_staff) → distingue les tickets quasi identiques (ex. 3 « Cancellation » = Support/Facturation/Rétention). - migrate_tickets.py : importe assigned_staff (ticket.assign_to → nom du staff) — le rattrapage ne le posait pas. Backfill des ~7,5k tickets assignés en F mais sans technicien côté ERPNext. - logToLinkedTicket : direction Received si l'expéditeur = adresse destinataire du ticket (une réponse depuis un domaine interne @targo n'est plus mal classée « Sent »). Co-Authored-By: Claude Opus 4.8 --- apps/ops/src/components/customer/TicketCard.vue | 4 ++++ scripts/migration/migrate_tickets.py | 11 ++++++++--- services/targo-hub/lib/conversation.js | 6 +++++- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/apps/ops/src/components/customer/TicketCard.vue b/apps/ops/src/components/customer/TicketCard.vue index 8fd7a28..90e34ee 100644 --- a/apps/ops/src/components/customer/TicketCard.vue +++ b/apps/ops/src/components/customer/TicketCard.vue @@ -23,6 +23,8 @@ + {{ ticket.issue_type }} + {{ ticket.assigned_staff }} #{{ ticket.legacy_ticket_id || ticket.name }} {{ ticket.opening_date }} @@ -64,6 +66,8 @@ defineEmits(['open']) .tk-badge { font-size: 10px; padding: 1px 8px; } .tk-meta { display: flex; align-items: center; flex-wrap: wrap; gap: 6px 10px; margin-top: 6px; padding-left: 0; } .tk-id { font-size: 11px; color: #94a3b8; font-variant-numeric: tabular-nums; } +.tk-dept { background: #eef2ff; color: #4338ca; font-size: 10px; padding: 1px 8px; display: inline-flex; align-items: center; } +.tk-assignee { font-size: 11px; color: #64748b; display: inline-flex; align-items: center; gap: 2px; white-space: nowrap; } .tk-date { font-size: 11px; color: #94a3b8; white-space: nowrap; display: inline-flex; align-items: center; gap: 2px; } .avatar-stack { display: flex; align-items: center; } .avatar-stack .avatar-chip { color: #fff; font-size: 9px; font-weight: 600; border: 1.5px solid #fff; margin-left: -8px; } diff --git a/scripts/migration/migrate_tickets.py b/scripts/migration/migrate_tickets.py index d09fadb..b5464b1 100644 --- a/scripts/migration/migrate_tickets.py +++ b/scripts/migration/migrate_tickets.py @@ -115,10 +115,14 @@ def main(): user_by_email = {r[1]: r[0] for r in pgc.fetchall() if r[1]} staff_to_user = {} + staff_name_by_id = {} # ticket.assign_to → « Prénom Nom » (custom field assigned_staff, cf update_assigned_staff.py) for s in staff_list: email = s.get("email", "") if email and email in user_by_email: staff_to_user[s["id"]] = user_by_email[email] + nm = ((s.get("first_name") or "") + " " + (s.get("last_name") or "")).strip() or (s.get("username") or "") + if nm: + staff_name_by_id[s["id"]] = nm # Existing Issue Types pgc.execute('SELECT name FROM "tabIssue Type"') @@ -208,6 +212,7 @@ def main(): dept_name = dept_map.get(t.get("dept_id"), None) cust_name = cust_map.get(t.get("account_id")) is_important = 1 if t.get("important") else 0 + assigned_staff = staff_name_by_id.get(t.get("assign_to")) if t.get("assign_to") else None opening_date = ts_to_dateonly(t.get("date_create")) opening_time = None @@ -233,17 +238,17 @@ def main(): naming_series, subject, status, priority, issue_type, customer, company, opening_date, opening_time, legacy_ticket_id, is_incident, is_important, - parent_incident, service_location + parent_incident, service_location, assigned_staff ) VALUES ( %s, %s, %s, %s, %s, 0, 0, 'ISS-.YYYY.-', %s, %s, %s, %s, %s, %s, %s, %s, - %s, %s, %s, %s, %s + %s, %s, %s, %s, %s, %s ) """, (issue_name, ts, ts, ADMIN, ADMIN, subject[:255], status, priority, dept_name, cust_name, COMPANY, opening_date, opening_time, - tid, 0, is_important, None, None)) + tid, 0, is_important, None, None, assigned_staff)) ticket_to_issue[tid] = issue_name i_ok += 1 diff --git a/services/targo-hub/lib/conversation.js b/services/targo-hub/lib/conversation.js index 679b0bc..2022b9e 100644 --- a/services/targo-hub/lib/conversation.js +++ b/services/targo-hub/lib/conversation.js @@ -765,7 +765,11 @@ async function logToLinkedTicket (conv, msg, subject) { if (!conv || !conv.linkedTickets || !conv.linkedTickets.length) return const issue = conv.linkedTickets[conv.linkedTickets.length - 1] // le plus récent if (!issue || !issue.name) return - const sent = msg.from === 'agent' + // Sortant seulement si NOUS écrivons (from=agent) ET pas depuis l'adresse destinataire du ticket + // (sinon une réponse depuis un domaine interne @targo — ex. test/employé destinataire — serait mal classée « Sent »). + const custEmail = String(conv.email || '').toLowerCase() + const fromEmail = String(msg.fromEmail || '').toLowerCase() + const sent = msg.from === 'agent' && (!custEmail || fromEmail !== custEmail) try { await createCommunication({ communication_type: 'Communication', communication_medium: 'Email',