Edit Booking: bật / tắt Preview Billing Diff
PreviewBillingDifferenceEnabled bật: admin xem trước thay đổi tiền rồi mới confirm.
Flag tắt hoặc booking không đủ điều kiện: dùng flow Edit Booking bình thường.
Đọc nhanh: các từ khóa
| Thuật ngữ | Hiểu nhanh | Dùng để làm gì? |
|---|---|---|
TermBookingOrderIds | ID chính thức của bản edit sau SubmitOrder | Là đầu vào chuẩn cho Preview và Confirm; không thay bằng payload UI hoặc TermBookingId. |
PreviewBillingDifference | Tính thử thay đổi tiền | Đọc dữ liệu đã lưu, dựng kết quả trong memory và không ghi dữ liệu thật. |
Billing Difference | Tổng tiền sẽ tăng hoặc giảm | + thường là cần thu thêm; - là giảm/credit; tổng theo billing scope. |
Billing context | Nhóm dữ liệu chung của một billing scope | Gồm BillingId, AccountId, BusinessUnitId; các order được preview phải cùng nhóm này. |
Original transaction | Invoice/credit note cũ làm mốc so sánh | Cho biết billing hiện tại để tính phần tăng, giảm hoặc credit. |
PreviewSnapshotToken | Mã “niêm phong” kết quả Preview | Confirm gửi lại mã này để kiểm tra kết quả admin đã xem vẫn còn khớp; thiếu hoặc sai thì chặn ghi. Mã này không cấp quyền. |
PREVIEW_BILLING_DIFF_STALE | Preview đã cũ | Booking, promo, setting hoặc option đã đổi; phải Preview lại, không tự động Confirm. |
muteNotification | Tạm không gửi notification khi Submit | Bản edit mới chỉ chờ admin review; notification và side effect chạy sau Confirm. |
1. So sánh nhanh
| Điểm cần biết | Bật Preview | Tắt Preview / không đủ điều kiện |
|---|---|---|
| Flow | SubmitOrder → PreviewBillingDifference → ConfirmOrder | SubmitOrder → ConfirmOrder |
| Review tiền | Xem invoice tăng/credit giảm trước Confirm | Xem kết quả sau Confirm |
| Ghi DB trước Confirm | Preview chỉ đọc, không ghi | Không có bước Preview |
| Confirm | Cần cùng IDs/options và PreviewSnapshotToken còn hợp lệ | Dùng IDs/options; không cần token |
| Không đồng ý | Edit lại hoặc mở pending booking | Quay lại Edit Booking nếu cần sửa |
Ví dụ thực tế: Admin thêm 2 session. → Bật Preview: xem trước invoice tăng bao nhiêu. → Tắt Preview: confirm trước, xem kết quả sau.
2. Khi nào có Preview?
| Tình huống | Có Preview? | Hành vi |
|---|---|---|
| Edit booking đã confirm, thỏa điều kiện bên dưới, flag bật | Có | Submit → Preview → Confirm |
| Edit booking đã confirm, flag tắt | Không | Confirm theo flow cũ |
| Edit booking thiếu một điều kiện bên dưới | Không | Đi thẳng tới popup confirm nếu action hợp lệ |
| Admin New Booking | Không | Giữ Submit & Confirm |
Submit & Confirm Attendance | Không | Submit rồi confirm attendance |
| Cancel / terminate / transfer / rollover | Không | Dùng flow lifecycle riêng |
| Bulk có edit hoặc mixed new/edit | Không được bypass | Từ chối cả batch; xử lý từng edit booking |
Preview không phải nút riêng. Nó chỉ mở từ action Submit/Confirm phù hợp.
2.1. Điều kiện để Preview chạy
Preview chỉ chạy khi tất cả nhóm điều kiện sau đều đạt:
| Nhóm | Điều kiện dễ hiểu |
|---|---|
| Flow | Flag bật; Admin đang Edit booking đã tồn tại; không phải New Booking, Attendance hoặc lifecycle action. |
| Order | Edit đã Submit; order còn active; có TermBookingOrderIds do backend trả về; status là Submitted hoặc Submitted_Change; tối đa 500 IDs/request. |
| Booking gốc | Mỗi order trỏ tới booking gốc còn active; status là Attendance_Approved, Quote hoặc Approved; status khác không Preview. |
| Billing context | Có BillingId, AccountId, BusinessUnitId, AttendeeId, TermProgramSetId; các order cùng BillingId/Account/BU. |
| Quyền & scope | Có quyền Booking Manager; tất cả order thuộc BU/account/enterprise mà admin đang được phép truy cập. |
| Dữ liệu tính tiền | Có invoice/credit note cũ để so sánh, cấu hình invoice của Business Unit và tax rate mặc định; mỗi line/extra có liên kết booking/order. |
Preview có điều kiện chặt hơn Confirm: Confirm chỉ đi tiếp không Preview khi policy cho phép rõ ràng.
Thiếu order ID, sai scope, mixed context, dữ liệu không đủ hoặc kết quả billing không thể tính chính xác → trả lỗi có cấu trúc, không hiển thị amount đoán.
Ví dụ thực tế: Admin sửa 1 session của booking đã confirm. → Submit tạo order có cùng billing context và trả
TermBookingOrderIds. → Preview mở; nếu booking mới hoặc chưa có invoice/credit note cũ thì chỉ direct-confirm khi policy cho phép.
3. Luồng dữ liệu
3.1. Bật Preview
3.2. Tắt Preview
4. Bốn mốc cần nhớ
| Mốc | Cả hai flow | Chỉ khi bật Preview |
|---|---|---|
| 1. Edit & validate | Chọn session/extra/discount; kiểm tra permission, scope và booking state | — |
| 2. Submit | Tạo hoặc cập nhật pending TermBookingOrder; backend trả đủ TermBookingOrderIds | muteNotification = true |
| 3. Review tiền | — | PreviewBillingDifference đọc-only → trả Billing Difference và PreviewSnapshotToken |
| 4. Confirm & persist | Dùng order IDs; ghi booking/billing trong một transaction | Kiểm tra token trước khi ghi; sai token → PREVIEW_BILLING_DIFF_STALE |
| 5. Sau commit | Refresh UI, history, notification, scheduler và external event | Giống nhau |
5. Các table dữ liệu sử dụng
Ký hiệu: READ chỉ đọc · WRITE tạo/cập nhật dữ liệu · — không tham gia.
| Table / nhóm dữ liệu | Dùng để làm gì | Submit | Preview | Confirm |
|---|---|---|---|---|
TermBookingOrder + line/extra/discount | Bản edit pending và nội dung dùng để tính billing | WRITE | READ | READ |
Billing | Billing scope và payment context | WRITE | READ | WRITE |
TermBooking + line/extra/discount | Booking hiện tại để so sánh và ghi kết quả | READ | READ | WRITE |
TermAttendance | Attendance để build confirmation plan | — | READ | WRITE |
Invoice + lines | Invoice hiện tại và invoice dự kiến | — | READ | WRITE |
CreditNote + lines | Khoản giảm/hoàn dự kiến | — | READ | WRITE |
Discount + DiscountApplication | Kiểm tra eligibility và áp discount | READ / WRITE | READ | READ / WRITE |
BusinessUnitSetting + invoice settings | Tax, currency, frequency, split rule | READ | READ | READ |
| Invoice/credit-note logs | Liên kết booking với finance record | — | READ | WRITE |
| Scheduler / history / notification | Side effect sau Confirm | — | — | WRITE |
Điểm quan trọng: PreviewBillingDifference được phép đọc dữ liệu hiện tại và dựng projection trong memory. Preview không ghi TermBooking, Billing, Invoice, CreditNote, attendance, scheduler, history, notification hoặc SaveChanges.
6. Preview trả kết quả gì?
| Field / phần hiển thị | Ý nghĩa |
|---|---|
Summary.Difference | Net change của cả billing scope; + là tăng, - là giảm/credit |
Current net billed | Tiền hiện tại sau credit note đã có |
Projected net billed | Current net billed + Difference |
Paid to date | Tổng Invoice.AmountPaid hiện tại; không tự chia cho attendee |
Open invoice balance | Tổng Invoice.AmountDue hiện tại |
AttendeeBreakdowns | Breakdown để kiểm tra, không phải phép tính riêng từng attendee |
Công thức:
Difference = invoice mới - credit note mới - invoice scheduled-deleteinvoice scheduled-delete là invoice cũ được đánh dấu sẽ xoá/giảm khi Confirm.
Other booking groups: booking khác cùngBillingId.Shared / Unallocated: không xác định được attendee.- UI dùng trực tiếp
Summary.Difference, không tự cộng lại từ transaction list.
| Case | Kết quả khi bật Preview |
|---|---|
| Thêm session / extra | Thấy invoice tăng trước Confirm |
| Bỏ session đã invoiced | Thấy credit/reduction trước Confirm |
| Đổi discount/promo | Tính lại theo rule chung |
| Không có thay đổi tiền | No work, không tạo giao dịch $0 giả |
| Nhiều attendee | Tổng vẫn theo billing scope; không chia đều tiền |
Ví dụ thực tế: Invoice hiện tại
$125, credit note cũ$1.50. →Current net billedlà$123.50. Không dùngDifferencelàm giá từng attendee.
7. Lỗi và case cần nhớ
| Case | Hành vi |
|---|---|
| Submit không trả IDs đầy đủ | Dừng; không Preview, không Confirm |
IDs thiếu, sai scope, mixed context, chưa Submitted | Structured error; không fallback về payload UI |
| Không tạo được projection chính xác | Không hiển thị amount đoán; block hoặc fallback theo policy |
PREVIEW_BILLING_DIFF_STALE | Không auto-confirm; chạy lại Preview |
| Admin không đồng ý | Edit this booking again hoặc mở pending Booking Manager |
| Preview lỗi tạm thời | Retry hoặc mở pending booking |
| Confirm lỗi khi đang persist | Rollback booking/billing trong transaction |
| Bulk có edit hoặc mixed new/edit | Reject toàn bộ, lỗi PREVIEW_BILLING_DIFF_BATCH_EDIT_NOT_SUPPORTED |
Preview error INELIGIBLE_ORDER / NO_ORIGINAL_TRANSACTION | Chỉ direct-confirm rõ ràng khi flow không có preview token; nếu đã review bằng token thì trả PREVIEW_BILLING_DIFF_STALE, không silent bypass |
8. FAQ ngắn
| Câu hỏi | Trả lời |
|---|---|
| Preview có ghi invoice/credit note thật không? | Không. Chỉ Confirm mới ghi. |
| Booking đã paid có preview được không? | Có nếu đủ dữ liệu; có thể sinh credit-note impact. |
| Preview có chia tiền theo attendee không? | Không. Tổng theo billing scope; row attendee chỉ để giải thích. |
| New Booking / Attendance có Preview không? | Không. Giữ flow riêng hiện tại. |
| Vì sao Confirm được nhưng Preview không được? | Preview cần baseline và context chính xác; Confirm chỉ bỏ qua Preview trong một số case được policy cho phép. |
| Preview và Confirm có phải ra cùng số? | Có, nếu cùng IDs/options và không có state change; khác IDs sinh ra hoặc timestamp là bình thường. |
Ví dụ thực tế: Preview báo
+$40, nhưng invoice setting đổi trước Confirm. → Token stale. → Không ghi$40cũ; Preview lại rồi admin xác nhận số mới.
9. Nhớ một câu
Bật Preview: Submit → xem chênh lệch → Confirm với token → persist.
Tắt Preview: Submit → Confirm trực tiếp.