Skip to content

Type Deep Dive — Tour Income, KPI And Report Boundary

1. tour_money là cross-module contract

Ở module này, tour_money không chỉ là field dùng để hiển thị tiền tour trong form:

  • FE dùng nó để render và edit trong TaskForm / detail surfaces.
  • Event assignee update dùng nó để sync ecommerce_task_log (project_task_assignee_update.go:146).
  • Salary flow đọc nó qua handleUpdateSalaryProjectTask(...) (project_task_assignee_update.go:196).
  • Customer note metadata cũng bị rewrite theo nó (project_task_assignee_update.go:215).
  • Report tour limit tính một phần dựa trên số assignee có tour_money > 0 (get_list_tour_limit_task_info.go:199).

2. Doctor commission flow

Logic chung

Khi doctor_task = true và task có parent:

  1. project_task_insert query lại task/order/project (project_task_insert.go:60),
  2. gom doctor assignees và tổng surgery_money (project_task_insert.go:112),
  3. query doctor accounts và existing request_working_schedule theo reference_id = projectTask.ID (project_task_insert.go:122),
  4. nếu order.PaidAmount < totalSurgeryMoney:
    • insert request mới hoặc update capture request cũ,
    • đồng thời có thể đẩy task new_branch -> waiting_branch (project_task_insert.go:167).
  5. nếu order.PaidAmount >= totalSurgeryMoney:
    • code hiện cố cancel request đang có (project_task_insert.go:223).

Bug rất mạnh

Nhánh cancel query request theo:

request_working_schedule.reference_id = projectTask.ID

nhưng mutation lại update theo:

request_working_schedule.id = projectTask.ID

project_task_insert.go:229. Nếu request_working_schedule.id khác project_task.id như thông thường, cancel path sẽ không chạm đúng record.

3. Customer visit / tour completion coupling

project_task_update_1 có hai nhánh liên quan khi task đổi trạng thái hoặc done time:

NhánhVai trò
updateAllCustomerVisitsxử lý khi DoneAt đổi ngày, move nguồn visit giữa ngày cũ và ngày mới
ProjectTaskCustomerVisitInsertUpdatexử lý khi task vừa đổi sang done_branch và là task_service

Bug rất mạnh

Trong ProjectTaskCustomerVisitInsertUpdate, nếu chưa có row all_customer_visits, code insert:

  • VisitDate = time.Now().In(util.LocationVN()).Format("2006-01-02")
  • CreatedAt = time.Now()
  • UpdatedAt = time.Now()

thay vì dùng taskNew.DoneAt (project_task_update_1.go:242). Kết quả là task hoàn tất backdate hoặc được chỉnh done_at lịch sử sẽ tạo visit cho ngày hiện tại, làm sai report visits, customer status theo ngày và các reconciliation downstream.

4. Tour limit report thực chất đọc task runtime

Action GetListTourLimit đang:

  1. lấy order_item thuộc order,
  2. bỏ qua non-internal-service và product unit NT,
  3. lấy related subtasks từ product,
  4. lấy overrides order_service_tour_override,
  5. đếm:
    • số assignee có tour_money > 0,
    • số assignee tổng,
    • đều chỉ tính trên subtasks done thuộc order_item/service/subtask tương ứng (get_list_tour_limit_task_info.go:195).

Điều này có nghĩa report tour limit thực chất phụ thuộc vào chất lượng dữ liệu assignee/task done runtime, không độc lập.

5. KPI coupling

Workload KPI

TriggerRuntime
Main assignee insertMetricTotalWorkload tăng điểm task (project_task_assignee_insert.go:118)
Main assignee deleteMetricTotalWorkload âm điểm task (project_task_assignee_delete.go:62)
Task overdue dailyMetricOverdueTask ghi từ scheduler task_expired (notification_task_expired.go:128)

Rủi ro dữ liệu

Nếu assignment bị reset bởi cron hoặc bị sửa sai qua UI helpers, KPI workload và overdue không còn phản ánh đúng ownership thực.

6. Findings kỹ thuật

IDMức độMô tả
TI-F01Rất caoCancel doctor commission request nhiều khả năng update sai record vì dùng request_working_schedule.id = projectTask.ID thay vì request id/reference query được (project_task_insert.go:229).
TI-F02Rất caoProjectTaskCustomerVisitInsertUpdate insert visit bằng time.Now() thay vì taskNew.DoneAt, gây lệch ngày visit/report khi done task backdate hoặc sửa lịch sử (project_task_update_1.go:242).
TI-F03CaoĐổi tour_money không chỉ sửa UI mà chạm ít nhất 3 downstream surfaces: ecommerce_task_log, salary và customer note metadata (project_task_assignee_update.go:164, :196, :215).
TI-F04CaoGetListTourLimit dùng task done + assignee counts làm source-of-truth report, nên mọi drift ở project_task_assignee sẽ phản ánh thẳng sang reporting (get_list_tour_limit_task_info.go:199).