tickets: distinguer par département + technicien assigné ; réponse courriel = Received
- 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 <noreply@anthropic.com>
This commit is contained in:
parent
417ef08f58
commit
27263d7766
|
|
@ -23,6 +23,8 @@
|
||||||
</template>
|
</template>
|
||||||
</InlineField>
|
</InlineField>
|
||||||
</span>
|
</span>
|
||||||
|
<span v-if="ticket.issue_type" class="ops-badge tk-dept"><q-icon name="apartment" size="11px" class="q-mr-xs" />{{ ticket.issue_type }}</span>
|
||||||
|
<span v-if="ticket.assigned_staff" class="tk-assignee"><q-icon name="person" size="12px" /> {{ ticket.assigned_staff }}</span>
|
||||||
<span class="tk-id">#{{ ticket.legacy_ticket_id || ticket.name }}</span>
|
<span class="tk-id">#{{ ticket.legacy_ticket_id || ticket.name }}</span>
|
||||||
<span v-if="ticket.opening_date" class="tk-date"><q-icon name="event" size="12px" /> {{ ticket.opening_date }}</span>
|
<span v-if="ticket.opening_date" class="tk-date"><q-icon name="event" size="12px" /> {{ ticket.opening_date }}</span>
|
||||||
<q-space />
|
<q-space />
|
||||||
|
|
@ -64,6 +66,8 @@ defineEmits(['open'])
|
||||||
.tk-badge { font-size: 10px; padding: 1px 8px; }
|
.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-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-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; }
|
.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 { 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; }
|
.avatar-stack .avatar-chip { color: #fff; font-size: 9px; font-weight: 600; border: 1.5px solid #fff; margin-left: -8px; }
|
||||||
|
|
|
||||||
|
|
@ -115,10 +115,14 @@ def main():
|
||||||
user_by_email = {r[1]: r[0] for r in pgc.fetchall() if r[1]}
|
user_by_email = {r[1]: r[0] for r in pgc.fetchall() if r[1]}
|
||||||
|
|
||||||
staff_to_user = {}
|
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:
|
for s in staff_list:
|
||||||
email = s.get("email", "")
|
email = s.get("email", "")
|
||||||
if email and email in user_by_email:
|
if email and email in user_by_email:
|
||||||
staff_to_user[s["id"]] = user_by_email[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
|
# Existing Issue Types
|
||||||
pgc.execute('SELECT name FROM "tabIssue Type"')
|
pgc.execute('SELECT name FROM "tabIssue Type"')
|
||||||
|
|
@ -208,6 +212,7 @@ def main():
|
||||||
dept_name = dept_map.get(t.get("dept_id"), None)
|
dept_name = dept_map.get(t.get("dept_id"), None)
|
||||||
cust_name = cust_map.get(t.get("account_id"))
|
cust_name = cust_map.get(t.get("account_id"))
|
||||||
is_important = 1 if t.get("important") else 0
|
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_date = ts_to_dateonly(t.get("date_create"))
|
||||||
opening_time = None
|
opening_time = None
|
||||||
|
|
@ -233,17 +238,17 @@ def main():
|
||||||
naming_series, subject, status, priority, issue_type,
|
naming_series, subject, status, priority, issue_type,
|
||||||
customer, company, opening_date, opening_time,
|
customer, company, opening_date, opening_time,
|
||||||
legacy_ticket_id, is_incident, is_important,
|
legacy_ticket_id, is_incident, is_important,
|
||||||
parent_incident, service_location
|
parent_incident, service_location, assigned_staff
|
||||||
) VALUES (
|
) VALUES (
|
||||||
%s, %s, %s, %s, %s, 0, 0,
|
%s, %s, %s, %s, %s, 0, 0,
|
||||||
'ISS-.YYYY.-', %s, %s, %s, %s,
|
'ISS-.YYYY.-', %s, %s, %s, %s,
|
||||||
%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,
|
""", (issue_name, ts, ts, ADMIN, ADMIN,
|
||||||
subject[:255], status, priority, dept_name,
|
subject[:255], status, priority, dept_name,
|
||||||
cust_name, COMPANY, opening_date, opening_time,
|
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
|
ticket_to_issue[tid] = issue_name
|
||||||
i_ok += 1
|
i_ok += 1
|
||||||
|
|
|
||||||
|
|
@ -765,7 +765,11 @@ async function logToLinkedTicket (conv, msg, subject) {
|
||||||
if (!conv || !conv.linkedTickets || !conv.linkedTickets.length) return
|
if (!conv || !conv.linkedTickets || !conv.linkedTickets.length) return
|
||||||
const issue = conv.linkedTickets[conv.linkedTickets.length - 1] // le plus récent
|
const issue = conv.linkedTickets[conv.linkedTickets.length - 1] // le plus récent
|
||||||
if (!issue || !issue.name) return
|
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 {
|
try {
|
||||||
await createCommunication({
|
await createCommunication({
|
||||||
communication_type: 'Communication', communication_medium: 'Email',
|
communication_type: 'Communication', communication_medium: 'Email',
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user