Skip to content

Module Overview — CRM Ticket Workflow

1. Scope

Flow này không nằm gọn trong một page hay một bảng. Nó cắt qua:

  • frontend crm routes cho list, create, update, detail tabs, multiple create,
  • backend crm-api action/event/scheduler,
  • Hasura metadata của domain crm,
  • runtime call center qua incall_call, incall_call_log, incall_extension,
  • join sang account, appointment, labels và customer workspace.

Mental model nên giữ từ đầu:

  • ticket là work item trung tâm,
  • changeStatusTicket là lifecycle engine thật,
  • ticket_distribute chỉ lưu last-assignee pointer cho auto-distribution,
  • incall_* là runtime touchpoint, không chỉ là log phụ.

2. Bức tranh kiến trúc

text
Lead / hotline / scheduler / CRM staff action
  -> create or update ticket
  -> optional assign or unassign transition
  -> optional complete transition via changeStatusTicket
  -> optional Duplicate(...) tạo new ticket
  -> ticket history / customer note / related people enrichment
  -> reminder / distribution schedulers

Call center runtime
  SIP / extension / incall_call_log
    -> action secureIncall / callcenterCallData / callcenterCallOutEvent
    -> FE newCallObj / initiateCall / ticket binding
    -> update incall_call.ticket_id

3. Frontend Surface

3.1 Route families

FamilyRoute groupBoundary
Ticket list shell/crm/ticketSearch, filters, quick action, call integration
Ticket create/update/crm/ticket/create, /crm/ticket/:id/editMain save orchestration
Ticket detail shell/crm/ticket/:id/:typeDetail tabs được gắn chặt vào route param
Detail tabsticket_note, detail, ticket_history, edit_historyNote, summary, lifecycle history, edit history
Multiple create/crm/ticket/multiple-createBatch creation surface
Hotline mapping/crm/mapping-acc*incall_extension mapping cho extension runtime

3.2 Permission/runtime map thực tế

BoundaryGate hoặc runtime đáng chú ý
Ticket listCrmRoles, nhưng visibility thực tế còn bị bó bởi TicketBuildWhere.setSpecialDataByRole()
Unassigned vs assigned editTicketDetailForm cho edit nếu unassigned, hoặc là assignee/in-charge, hoặc có leader role
Missed ticketCó nhánh canEditMissingTicket riêng theo AssignedTicketRoles
Call center call-outFE check localStorage extension trước khi bật initiateCall()
Detail shell:type param quyết định tab, nên route semantics và UI semantics đang coupled chặt

4. Backend Boundary Map

LớpFile chínhVai trò
Actioncrm-api/action/change_status_ticket.goValidate transition và xử lý Duplicate(...) khi complete
Actioncrm-api/action/assign_ticket.goBulk assign tickets theo branch + target role
Eventcrm-api/event/ticket_insert.goEnrich keywords, KPI log, related people
Eventcrm-api/event/ticket_update.goCustomer note khi completed, enrich keywords, related people
Eventcrm-api/event/ticket_interest_delete.goRefresh service groups khi interest đổi
Schedulercrm-api/scheduler/distribute_ticket.goAuto-assign ticket chưa có assignee
Schedulercrm-api/scheduler/consolidate_ticket_4.goBatch synthesize source 4 tickets
Schedulercrm-api/scheduler/consolidate_ticket_8.goBatch synthesize source 8 tickets
Schedulercrm-api/scheduler/remind_ticket_today.goNotify assignee với ticket due/overdue
Schedulercrm-api/scheduler/remind_ticket_tomorrow.goNotify ticket có appointment ngày mai
Actioncrm-api/action/secure_incall.go, callcenter_calldata.go, callcenter_callout.goHotline/callcenter runtime contracts

5. Data Layer Map

LayerObject chínhVai trò
Core workflowticket, ticket_history_logWork item, history, edit trail
Result contextticket_interest, ticket_campaignSản phẩm dịch vụ/campaign context của ticket
Assignment memoryticket_distributeLast assignee pointer cho auto round-robin
Hotline runtimeincall_call, incall_call_log, incall_extension, incall_call_contact_mappingCall metadata, log, extension mapping
Knowledge / supportknowledge_question, knowledge_question_tagCall center knowledge base
Customer relationaccount.related_peopleSide effect enrichment từ assignee / in-charge

6. Semantic Rules Cần Nhớ

  1. Save ticket ở FE có thể kéo theo 2 mutation khác sau CRUD: changeStatusTicketupdateIncallCall (TicketCreate.tsx:197-320).
  2. changeStatusTicket không đồng nghĩa “đổi status hiện tại”; nhánh complete có thể nhân bản ticket mới (change_status_ticket.go:49-57).
  3. Source 4 và source 8 là batch-generated tickets qua cron, không phải manual create.
  4. Assignment thủ công và auto-distribution không dùng chung state pointer; chỉ scheduler mới persist vào ticket_distribute.
  5. Reminder jobs và source generation jobs đều là một phần của workflow thật, nên test lifecycle phải bao gồm cron side effects.

7. Scheduler / Metadata Snapshot

Job / ActionContractGhi chú
consolidate_ticket_4cron 30 18 * * *Source 4 synthesis
consolidate_ticket_8cron 20 18 * * *Birthday/revenue tickets
distribute_ticketcron 0 21 * * *Auto-assign ticket unassigned quá hạn trong ngày
remind_ticket_todaycron 0 1 * * *Nhắc ticket due/overdue đang assigned
remind_ticket_tomorrowcron 30 9 * * *Nhắc ticket gắn appointment ngày mai
assignMultipleTicketHasura actionBulk assignment từ UI
changeStatusTicketHasura actionTransition engine
callcenterCallData / callcenterCallOutEventHasura action + REST endpointPush call log runtime
secureIncallHasura actionCall initiation contract

8. Rủi ro / Findings

IDMứcFinding
F-01P1TicketBuildWhere.setSpecialDataByRole() có bug thật: _or.concat(...) không reassign, khiến condition assignee/in-charge bị rơi (useTicketBuilder.ts:162-175).
F-02P1FE create/update flow không thuần CRUD; nếu dev chỉ test mutation chính sẽ bỏ sót status transition và new_ticket_id chain (TicketCreate.tsx:223-320).
F-03P1Assignment engine đang tách đôi: manual bulk assign không persist ticket_distribute, còn scheduler có persist, nên fairness/history không đồng nhất.
F-04P1Source taxonomy nhìn như label UI, nhưng thực tế source 4 và 8 được materialize bằng cron job riêng.
F-05P2Call-center runtime phụ thuộc dữ liệu extension trong localStorage, nên environment/runtime mismatch có thể làm FE mất khả năng gọi dù permission đúng.