Booking — Booking Reconciliation (đối soát điểm danh)
Booking Reconciliation so sánh điểm danh thực tế của một buổi (
TermAttendance) với lịch đã book: trẻ đến sớm, đón muộn hoặc vắng mặt. Khi lệch vượt ngưỡng châm chước, hệ thống sinh phí thêm (invoice) hoặc hoàn (credit note) theoBookingReconciliationRule.
Khác với billing thông thường (sinh từ booking đã confirm), reconciliation đọc attendance thực tế sau khi sign in/out rồi mới phát sinh chứng từ bổ sung. File gốc: Tux/src/Services/Tux.Service/BookingReconciliationService.cs; stored proc spBooking_GetBookingReconciliation; enums Tux/src/Tux.Core/Enums/BookingReconciliationType.cs.
Các loại đối soát (BookingReconciliationType)
| Type | Mã | Khi nào phát sinh |
|---|---|---|
Early | 1 | Đến sớm hơn TimeStart (drop-off sớm). |
Late | 2 | Đón muộn hơn TimeEnd (pickup muộn). |
Absent | 3 | Vắng mặt (Absent/AbsentBatch). |
Refund | 4 | Hoàn phí. |
Trạng thái đối soát (BookingReconciliationStatus)
Lưu ở TermAttendance.ReconciliationStatusId:
| Status | Mã | Ý nghĩa |
|---|---|---|
Unreconciled | 0 | Chưa xử lý (candidate tiềm năng). |
Ignored | 1 | Bỏ qua, không sinh chứng từ. |
Invoiced | 2 | Đã sinh invoice. |
Credited | 3 | Đã sinh credit note. |
SystemReconciled | 4 | Hệ thống tự xử lý. |
Cấu hình BookingReconciliationRule
Rule gắn ở cấp Business Unit (mở rộng theo OrgId/ProgramId/TypeId); Program trỏ tới qua Program.BookingReconciliationRuleId. Blob JSON chứa chi tiết phí/hoàn.
| Field | Ý nghĩa |
|---|---|
EarlyFeeEnabled / EarlyFeeGracePeriod | Bật phí đến sớm + ngưỡng châm chước (phút). |
LateFeeEnabled / LateFeeGracePeriod | Bật phí đón muộn + ngưỡng châm chước (phút). |
RefundEnabled | Bật hoàn phí khi vắng. |
ReconciliationRuleBlob | JSON của BookingReconciliationChargeRuleModel (Early/Late/Refund). |
Mỗi charge rule (BookingReconciliationChargeRuleModel) chứa:
| Field | Ý nghĩa |
|---|---|
GracePeriod | Châm chước (chỉ Early/Late). |
ChargeCap | Trần phí. |
SessionRefundRate / EnrichmentRefundRate | Tỷ lệ hoàn theo session / add-on (chỉ Refund). |
AdminAbsenceFee | Phí cố định cho 1 buổi vắng (áp lên session line). |
DescriptionTemplate / ReferenceTemplate | Mô tả & reference dòng chứng từ. |
ChargeRuleLines | Các mốc tính phí (BookingReconciliationChargeRuleLineModel). |
Mỗi rule line có RangeTypeId (Relative=0 theo phút từ giờ booked / Absolute=1 theo giờ tuyệt đối, chỉ Late), Start/End, ChargeType (Per_X_Minute=1, Fixed=2), ChargeUnit và ChargeRate.
Ví dụ thực tế: BU cấu hình buổi kết thúc 15:00,
LateFeeGracePeriod = 15, phí$1.5/phút. → Tính phí khi phụ huynh đón trễ. → Trẻ đón lúc 17:00 → lệch 120 phút, trừ châm chước còn 105 phút → invoice dòng "Late Pickup Charge" tính theo mốc rule.
Chọn candidate (stored proc)
spBooking_GetBookingReconciliation chọn TermAttendance thỏa đồng thời:
IsActive = 1,ReconciliationStatusId = Unreconciled(0), trong khoảng ngày, thuộc Term active của Business Unit.TypeId ∈ { Booking(0), ClosureDay(9), Subscription(4) }.- Điều kiện theo loại:
| Loại | Điều kiện roll call | Điều kiện thời gian |
|---|---|---|
Early | EarlyFeeEnabled; status ∈ {In_SignedOut 50, SignedOut 60, In_SignedOutBatch 70, SignedOutBatch 80} | DATEDIFF(MINUTE, ActualTimeStart, TimeStart) > EarlyFeeGracePeriod |
Late | LateFeeEnabled; status ∈ | DATEDIFF(MINUTE, TimeEnd, ActualTimeEnd) > LateFeeGracePeriod |
Refund | RefundEnabled; status ∈ {Absent 30, AbsentBatch 40} | — |
⚠️ Chỉ attendance đã sign out mới là candidate phí (đón muộn/về sớm). Buổi đang sign in mà chưa sign out chưa sẵn sàng đối soát — không đưa vào danh sách.
Ví dụ thực tế: Cuối tuần ops bấm "Run Reconciliation" cho khoảng ngày. → Gom các buổi trẻ đón muộn/đến sớm/vắng để duyệt phí. → Danh sách candidate chỉ gồm attendance đã sign out còn
Unreconciled, không kéo buổi đang có mặt chưa về.
Xử lý reconcile (ReconcileAttendancesAsync)
Quy tắc gom & split (dùng ProgramCategory.InvoiceSplitRuleId):
| Split rule | Mã | Gom theo |
|---|---|---|
Account | 1 | Cả nhóm (mặc định). |
Attendee | 2 | Từng attendee. |
Course | 3 | Từng TermProgramSet. |
Attendee_Course | 4 | Từng cặp attendee × TermProgramSet. |
Các ràng buộc xử lý:
- Một batch = một status duy nhất: mọi model phải cùng
StatusId; trộn status hoặc danh sách rỗng →INVALID_MODEL_STATE. - Cùng một Org: mọi attendance trong batch phải cùng
OrgId. - Bắt buộc có tax: BU phải cấu hình
DefaultTaxRateId+ TaxRate active, nếu không →DEFAULT_TAX_RATE_NOT_FOUND. Thuế tính theoLineAmountType(TaxInclusive/TaxExclusive/NoTax). Ignoredđơn giản: setReconciliationStatusId = Ignored,ReconciliationAmount = 0, không sinh chứng từ.- Một attendance có thể sinh nhiều dòng (nhiều credit note) nếu model chứa nhiều mục cho cùng buổi.
Chứng từ sinh ra
| Đầu ra | Giá trị |
|---|---|
Invoice / CreditNote TypeId | BookingReconciliation (3). |
Line TypeId | BookingReconciliation (3), Quantity = 1, UnitPrice = ReconciliationAmount. |
ConfirmationTypeId | Admin_Confirm (401). |
PaymentOptionId | Kế thừa từ Billing của booking gốc. |
| Attendance sau khi invoice | ReconciliationStatusId = Invoiced, ReconciliationAmount = tổng line. |
| Attendance sau khi credit note | ReconciliationStatusId = Credited, ReconciliationAmount = −tổng line. |
Hậu xử lý:
- Ghi
TermBookingInvoiceLog/TermBookingCreditNoteLogmỗi dòng (liên kếtTermBookingId,TermBookingLineId) để Booking ID resolve trên FinanceTransaction. - Schedule
SyncFinanceTransaction(Sync_Invoice_BookingNumbers/Sync_CreditNote_BookingNumbers) refresh projection booking numbers. - Tạo
InvoiceScheduler/CreditNoteScheduler(statusReady) và gửi entity eventCreate_Invoice_BookingReconciliation/Create_CreditNote_BookingReconciliation.
Điểm cần nhớ
- Reconciliation đọc attendance thực tế, sinh invoice phí thêm hoặc credit note hoàn — không sửa invoice gốc của booking.
- Ngưỡng châm chước & mức phí lấy từ
BookingReconciliationRule; candidate chỉ tính attendance đã sign out và cònUnreconciled. Ignoredlà lựa chọn hợp lệ của ops (không sinh chứng từ), dù attendance từng là candidate phí.- Tax bắt buộc: thiếu
DefaultTaxRateId→ toàn bộ batch fail trước khi persist.
Tiếp theo: Dữ liệu & bảng đọc/ghi.