Krok prirazeni resiteli a ukazkova data mimo zivy provoz
Opraveno: `ticket/assign` nemel vykonnou cast, takze krok "Prirad resiteli" vzdycky selhal hlaskou "operace nema vykonnou cast". Doplnen jako vnitrni krok vedle prirazeni nejvolnejsimu ze skupiny a prirazeni podle externiho ID. Ukazkova automatizace "Smerovani ticketu na resitele" je vypnuta. Zapnuta prebirala tickety, ktere uz nekomu patrily podle skutecne automatizace zakaznika, a prepsat rucni nebo cizi rozhodnuti je to nejhorsi, co muze automatizace udelat. Do udaju spoustece zaroven pribylo `assigned` a `knownCustomer`, aby slo napsat podminku "uz je prirazeny, nesahej na to". `ticket/upsert` prijima `status`: kdyz hodnota patri mezi nase ctyri stavy, nastavi stav, jinak se ulozi jako faze. Cizi aplikace posila svoje stavy hovoru a nas zivotni cyklus je pevny, protoze se z nej pocitaji statistiky. Do shrnuti kroku se napise, co se stalo. Overeno pripadem z provozu: callSid do externiho ID, voicebotId jako stitek, status jako stav. Ctyri zpravy o trech hovorech daly tri tickety, filtr na stitek vratil jen hovory daneho voicebota a widget je spocital vcetne prokliku na vyfiltrovany seznam. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
a57eca123e
commit
6abac82d5e
@@ -729,7 +729,14 @@ seed({
|
||||
*/
|
||||
seed({
|
||||
name: 'Směrování ticketů na řešitele',
|
||||
enabled: true,
|
||||
/*
|
||||
* Ukazkova automatizace, ktera **meni data**, je vypnuta.
|
||||
*
|
||||
* Zapnuta by prebirala tickety, ktere uz nekomu patri podle skutecne
|
||||
* automatizace zakaznika - ukazkova data nemaji sahat na zivy provoz.
|
||||
* Kdo si ji chce vyzkouset, zapne si ji.
|
||||
*/
|
||||
enabled: false,
|
||||
runsToday: 103,
|
||||
successRate: 100,
|
||||
avgDurationMs: 310,
|
||||
|
||||
+13
-1
@@ -516,6 +516,15 @@ export const services: Service[] = [
|
||||
{ value: 'critical', label: 'Kritická' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'status',
|
||||
label: 'Stav',
|
||||
kind: 'text',
|
||||
required: false,
|
||||
hint:
|
||||
'Nový, v řešení, čeká nebo vyřešeno. Jinou hodnotu (například stav hovoru ' +
|
||||
'od voicebota) uložíme jako fázi, protože to přesně je.',
|
||||
},
|
||||
{
|
||||
id: 'stage',
|
||||
label: 'Fáze',
|
||||
@@ -699,7 +708,10 @@ export const services: Service[] = [
|
||||
{
|
||||
id: 'assign',
|
||||
name: 'Přiřadit řešiteli',
|
||||
description: 'Předá ticket konkrétnímu člověku. Objeví se mu mezi jeho tickety.',
|
||||
description:
|
||||
'Předá ticket konkrétnímu člověku. Objeví se mu mezi jeho tickety a dostane ' +
|
||||
'upozornění. Když nechcete vybírat ručně, použijte Předat nejvolnějšímu ze skupiny.',
|
||||
implementation: 'script',
|
||||
inputs: [
|
||||
{
|
||||
id: 'ticketId',
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
*/
|
||||
|
||||
import { createIncident } from '../data/incidentStore.js';
|
||||
import { findGroup, findPersonByExternalId, listPeople } from '../data/people.js';
|
||||
import { findGroup, findPerson, findPersonByExternalId, listPeople } from '../data/people.js';
|
||||
import { findTicketType } from '../data/ticketTypes.js';
|
||||
import {
|
||||
assignTicket,
|
||||
@@ -92,19 +92,39 @@ const handlers: Record<string, Handler> = {
|
||||
});
|
||||
|
||||
/*
|
||||
* Faze se nastavi az po zalozeni: u noveho ticketu jeste neni typ, takze
|
||||
* by se nemela proti cemu overit. Prazdna hodnota nic nemeni.
|
||||
* Stav a faze se nastavuji az po zalozeni: u noveho ticketu jeste neni typ,
|
||||
* takze by se faze nemela proti cemu overit.
|
||||
*
|
||||
* `status` je shovivavy zamerne. Cizi aplikace posila svoje stavy
|
||||
* (`ringing`, `completed`) a nas zivotni cyklus ma pevne ctyri hodnoty.
|
||||
* Kdyz hodnota mezi ne patri, nastavi se stav; kdyz ne, ulozi se jako
|
||||
* **faze**, protoze to presne je - postup uvnitr typu. Do shrnuti kroku
|
||||
* se napise, co se stalo, aby to nebylo kouzlo.
|
||||
*/
|
||||
if (inputs.stage?.trim()) {
|
||||
const wanted = inputs.status?.trim();
|
||||
const asStage = inputs.stage?.trim();
|
||||
let note = '';
|
||||
|
||||
if (wanted && statuses.includes(wanted as TicketStatus)) {
|
||||
updateTicketStatus(result.ticket.id, wanted as TicketStatus, [context.tenantId]);
|
||||
note = `, stav ${wanted}`;
|
||||
}
|
||||
|
||||
const stage = asStage || (wanted && !statuses.includes(wanted as TicketStatus) ? wanted : '');
|
||||
if (stage) {
|
||||
const type = result.ticket.typeId ? findTicketType(result.ticket.typeId) : undefined;
|
||||
setTicketStage(result.ticket.id, inputs.stage.trim(), type?.statuses ?? [], [
|
||||
const changed = setTicketStage(result.ticket.id, stage, type?.statuses ?? [], [
|
||||
context.tenantId,
|
||||
]);
|
||||
note += changed
|
||||
? `, fáze ${stage}`
|
||||
: `, fáze ${stage} NEULOŽENA (není ve workflow typu)`;
|
||||
}
|
||||
|
||||
return {
|
||||
ok: true,
|
||||
summary: result.created ? `založen ${result.ticket.id}` : `doplněn ${result.ticket.id}`,
|
||||
summary:
|
||||
(result.created ? `založen ${result.ticket.id}` : `doplněn ${result.ticket.id}`) + note,
|
||||
detail: null,
|
||||
ticketId: result.ticket.id,
|
||||
outputs: {
|
||||
@@ -166,6 +186,40 @@ const handlers: Record<string, Handler> = {
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Preda ticket konkretnimu cloveku.
|
||||
*
|
||||
* Nejjednodussi zpusob prirazeni: vim, komu to patri, a reknu to. Kdyz je
|
||||
* lidi vic a nechci vybirat rucne, je na to `assign-least-busy`.
|
||||
*/
|
||||
'ticket/assign': (inputs, context) => {
|
||||
const ticketId = inputs.ticketId?.trim() || context.ticketId;
|
||||
if (!ticketId) return missing('ticket');
|
||||
|
||||
const assigneeId = inputs.assigneeId?.trim();
|
||||
if (!assigneeId) return missing('řešitele');
|
||||
|
||||
const person = findPerson(assigneeId);
|
||||
if (!person || person.tenantId !== context.tenantId) {
|
||||
return {
|
||||
ok: false,
|
||||
summary: `řešitel ${assigneeId} neexistuje`,
|
||||
detail: 'Zkontrolujte ID řešitele v kroku. Řešitelé se spravují v Nastavení.',
|
||||
outputs: {},
|
||||
};
|
||||
}
|
||||
|
||||
const updated = assignTicket(ticketId, person.id, [context.tenantId]);
|
||||
if (!updated) return { ok: false, summary: 'přiřazení se nepodařilo', outputs: {} };
|
||||
|
||||
return {
|
||||
ok: true,
|
||||
summary: person.name,
|
||||
detail: null,
|
||||
outputs: { assigneeId: person.id, assigneeName: person.name },
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Preda ticket cloveku podle ID z cizi aplikace.
|
||||
*
|
||||
|
||||
@@ -172,7 +172,14 @@ export function ticketPayload(ticket: Ticket): Record<string, unknown> {
|
||||
stage: ticket.stage,
|
||||
tags: ticket.tags,
|
||||
assigneeId: ticket.assignee?.id ?? null,
|
||||
/*
|
||||
* Aby slo napsat "uz je prirazeny, nesahej na to". Bez toho podminka
|
||||
* nema z ceho vychazet a automatizace prebere i ticket, ktery uz nekomu
|
||||
* patri - a prepsat rucni rozhodnuti je to nejhorsi, co muze udelat.
|
||||
*/
|
||||
assigned: ticket.assignee !== null,
|
||||
assigneeGroupId: ticket.assigneeGroupId,
|
||||
knownCustomer: ticket.customer.id !== null,
|
||||
company: ticket.customer.company,
|
||||
contact: ticket.customer.contact,
|
||||
reply: ticket.customer.reply,
|
||||
|
||||
Reference in New Issue
Block a user