Skip to content

Type Deep Dive — Negative Payment Boundary

1. Tại sao phải tách file này?

Trong UI, negative payment thường bị nhìn như “một dạng refund”. Trong code thực tế, điều đó sai.

Negative payment:

  • không tạo wallet.transaction_request,
  • không đi qua action changeStatusTransaction,
  • không dùng state machine R / S / C / Reject,
  • không dùng page stack WithdrawRequest*.

Nó là một flow riêng:

  • tạo từ payment flow của order/prepaid,
  • lưu ở hrm.request_working_schedule,
  • sinh invoice.negative = true,
  • duyệt qua approval flow của HRM/ecommerce.

2. FE Entry Points

2.1 Tạo request âm

Không tạo từ page refund/withdraw.

Nó được gọi từ payment flows:

  • OrderAddMultipleInvoiceDialog.tsx
  • AddPrepaidPaymentForm.tsx

thông qua action createNegativePayment.

2.2 List / detail approval

PageVai trò
NegativePaymentRequests.tsxList request âm
NegativePaymentRequestDetail.tsxDetail + approve/reject

GraphQL chính:

  • negative_payment_request.graphql
  • useChangeApprovalStatusMutation

3. Backend Flow

text
Payment flow phát hiện cần negative payment
  -> ecommerce-api action createNegativePayment
  -> insert hrm.request_working_schedule (type = negative_payment)
  -> insert negative invoice (negative = true, request_negative_id = ...)
  -> approver xử lý request
  -> ecommerce-api changeStatusRequestWorkingSchedule
  -> ecommerce-api event request_working_schedule_update

4. Dữ liệu cốt lõi

ObjectVai trò
hrm.request_working_scheduleApproval container chính
ecommerce.invoiceNegative invoice, link ngược bằng request_negative_id
wallet.transaction_requestChỉ là nguồn đọc commission đã success, không phải request engine của flow này

5. Vì sao flow này vẫn liên quan refund docs?

negative payment có coupling thực tế với vùng refund/commission:

  • cùng đụng order và invoice,
  • cùng có ngữ nghĩa “điều chỉnh âm”,
  • đọc commission transaction đã success của order từ wallet,
  • dễ bị team hiểu nhầm là một subtype của refund.

Vì vậy feature deep dive này phải nhắc tới nó, nhưng nhắc như boundary riêng, không như một subtype.

6. Logic đáng chú ý trong createNegativePayment

Action negative_payment.go làm các việc chính:

  1. kiểm tra order tồn tại,
  2. chặn nếu order đã paid_at,
  3. chặn nếu đã có request_working_schedule pending cùng order,
  4. query commission transaction đã success của order,
  5. phân nhánh theo order_kind:
    • cosmetic
    • service
    • prepaid
  6. tạo request âm + negative invoice.

Điểm quan trọng:

  • flow này đọc dữ liệu commission từ wallet,
  • nhưng approval / lifecycle lại nằm ngoài wallet.

7. Status Model

Khác refund family, negative payment dùng status của request_working_schedule:

  • pending
  • approved
  • rejected
  • canceled
  • recalled
  • approved_step_one

FE hiện chủ yếu xử lý pending / approved / rejected.

8. Boundary Rules

NP-001

Không được mô tả negative paymenttransaction_request.

NP-002

Không được join report/debug theo transaction_request.reference_id khi đang phân tích negative payment.

NP-003

Nếu cần lần theo invoice của negative payment, phải đi qua invoice.request_negative_id.

NP-004

Nếu cần lần theo approval object, phải bắt đầu từ hrm.request_working_schedule, không phải wallet tables.

9. Rủi ro / Findings

IDMứcFinding
NP-01P1request_working_schedule_update có dấu hiệu mark invoice paid_at/payment_verified_at trên mọi status change cho negative payment, không đợi approved.
NP-02P1FE list/detail cũng có dấu hiệu check sai actor approve tương tự refund flow.
NP-03P1Vì cùng đụng commission/order/invoice, team rất dễ gộp nhầm negative payment vào refund docs nếu không tách boundary ngay từ đầu.