Finance — Case thường gặp
Cách đọc mỗi case: bắt đầu từ triệu chứng/câu hỏi, sau đó đọc behavior đúng, cách kiểm tra ở UI/API/dữ liệu/code, source anchor và Ví dụ thực tế. Các case ở đây ưu tiên behavior đang chạy; nếu cần contract gốc, mở spec/code được dẫn ở cuối case.
Invoice không hiện đúng trong manager
Kiểm tra theo thứ tự: invoice gốc active, lock/processing không chặn, projection đã refresh, search key/booking number sync không stale.
Ví dụ thực tế: Kế toán tìm hóa đơn theo booking number nhưng manager không hiện → khoanh vùng do dữ liệu hay projection chưa refresh → record gốc đúng nhưng projection stale → chạy reconcile, không sửa invoice.
Subscription invoice có bị cộng Enrichment không?
Không. Đối chiếu hai nguồn riêng: Subscription invoice lấy base schedule/Add-on từ EnrollmentBillingSchedule; Booking reconciliation lấy priced TermBookingLine Enrichment. Không được cộng TermBookingLine vào SubscriptionTermBillingSchedule, EnrollmentInvoiceLog hoặc Subscription InvoiceLine.
Ví dụ thực tế: Step 2 báo 10 Enrichment occurrences
$25, schedule Subscription$5. → Subscription invoice vẫn$5; Booking invoice/reconciliation xử lý$25. → Nếu Subscription invoice thành$30, dừng phát hành và kiểm tra service boundary.
Billing Details báo thiếu billing dù schedule đã invoiced
Completeness phải đếm active EnrollmentBillingSchedule thuộc đúng SubscriptionTermBillingSchedule đang mở. Không lấy toàn bộ EnrollmentPeriod của cùng Subscription/Term; period khác không được chặn schedule hiện tại.
Ví dụ thực tế: Enrollment có period A đã invoiced và period B chưa invoiced. → Mở Billing Details của schedule A. → Không hiện modal
Generate Billing Requiredchỉ vì period B còn thiếu.
Booking Enrichment bị tính hai lần
Đối chiếu semantic key TermBookingId + TermProductId + Date, active Booking lines và TermBookingInvoiceLog. Nếu Subscription invoice cũng chứa amount đó, đây là duplicate charge path; không xử lý bằng cách sửa tay invoice, cần sửa boundary/projection rồi reconcile.
Ví dụ thực tế: Worker retry tạo cùng occurrence hai lần. → Chỉ một active Booking line và một invoice log được giữ. → Lần retry còn lại converge/skip, không tạo Subscription invoice bổ sung.
Edit booking đã confirm làm thay đổi tiền
Preview diff trước, confirm mới persist invoice/credit-note thật. Preview không tạo scheduler, finance log hay credit note thật.
Ví dụ thực tế: Admin thêm 2 buổi vào booking đã confirm → preview hiện diff (không ghi sổ) → chỉ Confirm mới tạo invoice/credit-note thật.
Preview đã review nhưng billing state thay đổi
Nếu Confirm vẫn mang PreviewSnapshotToken nhưng trạng thái billing không còn đủ điều kiện preview, hệ thống trả PREVIEW_BILLING_DIFF_STALE, rollback transaction và yêu cầu Preview lại. Không được âm thầm chuyển sang direct-confirm sau khi admin đã review một preview.
Ví dụ thực tế: Admin xem preview có invoice tăng → invoice gốc bị inactive trước khi Confirm → hệ thống chặn Confirm không ghi dữ liệu → admin chạy Preview lại để xem số tiền hiện tại.
Preview có nhiều attendee hoặc nhiều booking group
- Chỉ attendee thuộc các order đã submit trong lần edit được hiển thị riêng. Đây là cùng scope mà Submit & Confirm popup xác nhận.
- Booking group khác dùng chung
BillingIdđược gom thànhOther booking groups; khoản không xác định được attendee nằm trongShared / Unallocated. - Tổng
Net billing impactvẫn tính trên toàn billing scope và phải reconcile với các row chi tiết. Vì vậy không lấy tổng này chia cho số attendee để gọi là giá per attendee.
Ví dụ thực tế: Form hiển thị
$65/attendee nhưng Preview báo+$68. →$65là estimate trên form;+$68là net thay đổi của toàn billing scope sau invoice mới, credit note và invoice scheduled-delete. → Đối chiếu các dòng breakdown và transaction summary, không đối chiếu hai số như cùng một loại fee.
Mixed invoice state trong Preview
Billing có invoice retained và Generated không còn bị block chỉ vì trạng thái mixed. Nếu retained invoice logs nhất quán, Preview reconstruct được context, giữ invoice retained và đưa Generated chưa thanh toán vào ScheduledToDelete. Chỉ context thiếu hoặc attribution/log không thể xác minh mới trả structured error; không hiển thị số tiền phỏng đoán.
Ví dụ thực tế: Một invoice đã giữ lại và một invoice Generated cùng nằm trong BillingId → log line map rõ ràng → Preview hiển thị đúng invoice giữ lại và khoản sẽ void. Nếu mapping invoice retained mơ hồ → dừng ở lỗi Preview để xử lý dữ liệu, không cho admin Confirm theo amount ước lượng.
Credit note không match invoice
Đối chiếu original invoice link, allocation, attendee/header sync và booking number. Projection sai nhưng record gốc đúng → refresh/reconcile projection.
Ví dụ thực tế: Hủy booking → credit note lệch số tiền invoice gốc → xác định lệch thật hay chỉ projection → allocation + original invoice link khớp → reconcile projection, không chỉnh tay credit note.
Payment đã thu nhưng invoice chưa cập nhật
Phân biệt Payment với Finance: provider transaction có thể paid, nhưng invoice/projection cần job/event cập nhật trạng thái. Đọc payment record, invoice status, scheduler/error.
Ví dụ thực tế: Phụ huynh trả PayNow, biên lai thành công, nhưng invoice vẫn "unpaid" → tiền đã vào (Payment) nhưng sổ chưa cập nhật (Finance) → provider paid nhưng event/job chưa chạy → kiểm tra scheduler/error, không thu lại tiền.
Xero sync lỗi
Lỗi có thể ở cấu hình provider, mapping account/tax, dữ liệu invoice/credit-note, retry state. Không sửa invoice gốc chỉ để "làm Xero pass" khi chứng từ AIMY đúng.
Ví dụ thực tế: Invoice không đẩy được sang Xero do tax code chưa map → sửa mapping, không bóp méo chứng từ → map account/tax đúng rồi retry sync, giữ nguyên invoice gốc.
Invoice email không gửi hoặc không có PDF
Kiểm tra template/channel setting theo site, trạng thái invoice (Approved/Paid) và permission. Email invoice luôn gửi không PDF; customer muốn xem phải dùng link View/Download có auth (INV-INVOICE-EMAIL-003/005).
Ví dụ thực tế: Khách báo "nhận email invoice nhưng không có file PDF" → đây là hành vi đúng: email không đính kèm → hướng dẫn khách mở link
; hệ thống yêu cầu đăng nhập rồi mới hiển thị/Download PDF.
Reminder không gửi cho hóa đơn còn nợ
Đối chiếu eligibility lúc chạy: active + AmountDue > 0 nhưng bị loại nếu trong DD collection flow, LockTypeId != null, hoặc site disable kênh email/template. Mỗi trường hợp có skip reason; skip không set Invoice.Sent (INV-INVOICE-REMINDER-003).
Ví dụ thực tế: Invoice AIN-0031 còn nợ nhưng không nhận reminder ngày due+7 → kiểm tra skip reason → hóa đơn trong DD collection flow nên bị loại đúng rule; không phải lỗi gửi.
Invoice đã gửi email nhưng không hiện tag Viewed
Viewed chỉ set khi customer authenticated mở View/Download PDF thành công; admin mở details, resend email, hay request unauthorized không set (INV-INVOICE-VIEW-002). Tag chỉ phản ánh customer access thật.
Ví dụ thực tế: Admin hỏi vì sao invoice đã Email hôm qua vẫn không có tag
Viewed→ email không tính là viewed → khách bấm link, đăng nhập mở thành công → refresh thấy tagViewed.
Invoice đang Processing — không vào được Add Payment/Apply Credit
LockTypeId != null → list hiện tag Processing, details hiện Payment in processing, Add Payment/Apply Credit bị disable nhưng vẫn hiển thị (INV-INV-MGR-LOCK-005). Không phải lỗi — đợi luồng payment xử lý xong.
Ví dụ thực tế: Kế toán muốn nhập thêm payment cho invoice AIN-0090 nhưng nút xám → list có tag Processing → payment đang xử lý nên hành động manual bị khóa đúng luật; chờ lock hết rồi mới thao tác.
Closure Day credit cao hơn tiền đã thu
Không lấy TermBookingLine.TotalAmount làm credit basis. Đối chiếu invoice log đã issue trừ credit-note log còn hiệu lực; line chưa invoiced hoặc đã offset sẽ bị skip, line partly credited chỉ credit phần còn lại.
Ví dụ thực tế: Booking line total $120 nhưng invoice đã issue $80, credit $20. → Credit lần này là $60, không phải $120; tax/currency theo source invoice và line sau convert giữ zero.
Invoice Manager không mở được Booking Info
Booking Info chỉ mở từ Booking ID có BillingId, là view read-only theo billing scope. Row không có billing giữ text/tooltip cũ; request ngoài BU hoặc account auth trả not-found, không trả dữ liệu billing.
Ví dụ thực tế: Click Booking ID trong invoice có nhiều program. → Popup gom toàn bộ booking active của billing, không chỉ booking nằm trên invoice đang mở; chọn Go to Booking Manager để giữ filter billing.
Booking ID trống nhưng invoice vẫn có booking
FinanceTransaction.BookingNumbers trống không chứng minh invoice không có association — có thể là gap integrity (TermBookingInvoiceLog thiếu InvoiceId, projection chưa sync). Đối chiếu source TermBookingInvoiceLog active; empty BookingNumbers hiển thị - ở Booking ID column (invoice-manager-booking-id-integrity).
Ví dụ thực tế: Invoice AIN-0163 hiển thị Booking ID
-dù vẫn có booking thật → tra source association thấyTermBookingInvoiceLogactive nhưng projection trống → chạy repair/backfill projection, không chỉnh tay invoice.
Invoice_Fully_Paid notification không gửi
Không gửi là đúng khi: invoice TotalAmount = 0 đánh Paid, invoice import/sync sẵn Paid, site disable kênh email, hoặc account thiếu primary contact email (INV-INVOICE-PAID-NOTIF-001/003/004).
Ví dụ thực tế: Phụ huynh trả hết tiền nhưng không nhận email xác nhận → kiểm tra invoice zero-total hay site tắt email → account thiếu primary contact → hệ thống không throw, ghi lý do "no recipient"; không phải lỗi thanh toán.