Appearance
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:
project_task_insertquery lại task/order/project (project_task_insert.go:60),- gom doctor assignees và tổng
surgery_money(project_task_insert.go:112), - query doctor accounts và existing
request_working_scheduletheoreference_id = projectTask.ID(project_task_insert.go:122), - 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).
- nếu
order.PaidAmount >= totalSurgeryMoney:- code hiện cố cancel request đang có (
project_task_insert.go:223).
- code hiện cố cancel request đang có (
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ánh | Vai trò |
|---|---|
updateAllCustomerVisits | xử lý khi DoneAt đổi ngày, move nguồn visit giữa ngày cũ và ngày mới |
ProjectTaskCustomerVisitInsertUpdate | xử 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:
- lấy
order_itemthuộc order, - bỏ qua non-internal-service và product unit
NT, - lấy related subtasks từ product,
- lấy overrides
order_service_tour_override, - đế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).
- số assignee có
Đ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
| Trigger | Runtime |
|---|---|
| Main assignee insert | MetricTotalWorkload tăng điểm task (project_task_assignee_insert.go:118) |
| Main assignee delete | MetricTotalWorkload âm điểm task (project_task_assignee_delete.go:62) |
| Task overdue daily | MetricOverdueTask 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
| ID | Mức độ | Mô tả |
|---|---|---|
| TI-F01 | Rất cao | Cancel 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-F02 | Rất cao | ProjectTaskCustomerVisitInsertUpdate 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-F03 | Cao | Đổ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-F04 | Cao | GetListTourLimit 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). |