Subscription — Case thường gặp
"Khách book bị báo trùng lịch"
Hệ thống cố ý chặn double-booking. Khi submit, nó so schedule với enrollment/booking đang có của attendee. Nếu trùng → cả request fail, không tạo state một phần.
- Edit một enrollment: EnrollmentId hiện tại được loại trừ khỏi check (không tự xung đột).
- Direct-confirm / future-confirm: dùng cùng guard → cũng bị chặn nếu trùng.
Spec:
subscription-booking-double-booking-guard+ requirement "validates existing enrollment schedules before runtime state creation". Phía normal booking xem Booking → Case thường gặp.
Ví dụ thực tế: Trẻ đã có enrollment After School Thứ 4; phụ huynh book thêm gói trùng đúng chiều Thứ 4. → Tránh ghi nhận trẻ ở hai chỗ cùng giờ. → Cả request bị fail lúc submit, không tạo enrollment một phần.
"Manage Add-ons không thấy kỳ tôi muốn"
Popup chỉ hiện các kỳ thuộc EnrollmentBillingSchedule (đã book), không hiện kỳ chưa book của subscription term. Nếu kỳ chưa được book cho enrollment đó → nó không xuất hiện, và submit nó sẽ bị từ chối.
- Bulk "select all" chỉ hiện khi nhóm có > 3 kỳ.
- Period rows lấy từ enrollment schedule, không từ price type của add-on.
Ví dụ thực tế: Admin muốn thêm add-on bữa ăn cho kỳ Term 3 nhưng trẻ chưa book Term 3. → Chỉ cho gắn add-on vào kỳ đã thực sự book. → Term 3 không hiện trong popup; nếu cố submit sẽ bị từ chối.
"Skip một kỳ thì hóa đơn ra sao?"
Skip đưa invoice về Initialised (0) và hủy scheduler đang active (nếu có). Tùy trạng thái invoice trước đó, hệ thống regenerate invoice hoặc issue credit note để giữ nhất quán tài chính, đồng thời archive booking line liên quan.
Ví dụ thực tế: Gia đình đi nghỉ, xin bỏ kỳ tháng 7 đã xuất hóa đơn. → Không thu phí kỳ trẻ không dùng. → Skip đưa invoice về
Initialised, phát credit note đảo phần đã issue và archive booking line tháng 7.
"Tạo gói BillingOnly mà không cần product"
BillingOnly (TypeId=1):
- Ẩn Program section; không chặn save/publish.
- Publish được dù không có
SubscriptionProduct. - Link term không cần product planned check.
Trong khi BookingAndBilling vẫn bắt buộc có product để publish + planned check khi link term.
Lưu ý: chọn loại trước khi tạo; sau khi subscription tồn tại thì read-only.
Ví dụ thực tế: Trung tâm thu phụ phí cơ sở vật chất hàng kỳ, không gắn buổi học cụ thể nào. → Lập gói chỉ để tính tiền, khỏi cấu hình product. → Tạo gói
BillingOnly, publish ngay dù không cóSubscriptionProduct.
"Không archive được gói"
Chỉ archive được gói unpublished (Unbookable). Gói đã publish/bookable → Current tab không cho archive, và request trực tiếp bị server từ chối. Archive không đổi publish status; restore cũng giữ nguyên (không tự publish).
Ví dụ thực tế: Admin muốn dọn gói "Term 1 2025" đang mở bán khỏi danh sách. → Tránh ẩn nhầm gói khách vẫn book được. → Phải unpublish trước; gói còn bookable thì nút archive bị chặn và server từ chối.
"Unlink term có booking xong vẫn thấy trong Billing nhưng mất ở Booking"
Đúng như thiết kế. Unlink một term đang có booking usage → hệ thống archive (giữ IsActive = true, set StatusId = 600), không xóa schedule.
- Booking UI: term archived không còn để khách chọn kỳ mới.
- Subscription Manager & Subscription Billing Manager: vẫn thấy term + schedule archived để xử lý booking/billing đang chạy.
- Nếu unlink term không có booking → soft delete bình thường (
IsActive = false), biến mất khỏi cả ba surface.
Term
IsActive = falselà soft delete, không phải archive — không restore được từ UI.
Ví dụ thực tế: Admin gỡ Term 2 khỏi gói nhưng Term 2 đã có 30 booking đang chạy. → Giữ booking/billing đang chạy không bị mất, mà ngừng bán kỳ mới. → Term bị archive: ẩn ở Booking UI, vẫn hiện ở Subscription/Billing Manager.
"Re-link lại term cũ bị hỏi chọn product / ra bản trùng"
Re-link đúng term đã archived (cùng subscription + term) sẽ restore, không tạo mới:
- Set
StatusIdtừ600→500; bỏ qua popup chọn product. - Giữ nguyên
ProductIdsvàSubscriptionTermBillingSchedulecũ — không mất sản phẩm. - Không tạo
SubscriptionTerm/schedule trùng; reload mỗi surface chỉ thấy một bộ active. - Nếu không có archived row active → mới mở popup chọn product và tạo linked term mới.
Ví dụ thực tế: Admin lỡ unlink Term 2 (đã archive) rồi muốn gắn lại. → Khôi phục đúng sản phẩm
- schedule cũ, không tạo bản trùng. → Re-link đổi
StatusId600 → 500, bỏ qua popup product, giữ nguyênProductIds.
"Booking Cut-Off nhập số lẻ bị lỗi"
UI nhận ngày nguyên; lưu thành phút (CutOffMinute, 1 ngày = 1440). Nhập 1.5 → bị từ chối (không tự làm tròn). 0/null = không có cut-off → booking không bị chặn bởi rule này.
Ví dụ thực tế: Admin muốn chốt đăng ký trước 1,5 ngày nên gõ
1.5. → Cut-off chỉ tính theo ngày nguyên để tránh mốc phút mơ hồ. → Hệ thống từ chối1.5; admin nhập1hoặc2(lưuCutOffMinute= 1440/2880).
"Đổi tab Subscription Manager bị nhảy data cũ"
Đã xử lý: đổi tab tự reload theo filter hiện tại; response của tab cũ (request còn pending) không ghi đè data tab mới. Nếu thấy data nhảy → kiểm tra logic "latest active tab wins".
"Period booking khách chọn không khớp"
UI phải lấy period từ GET .../Subscriptions/{id}/BillingSchedules (canonical), không tự tính ở client. Mỗi row có BookingStart/BookingEnd ưu tiên hơn billing period start/end. Nếu endpoint trả rỗng → hiện trạng thái "unavailable" và chặn submit; nếu lỗi → trạng thái lỗi retryable và chặn submit với selection cũ.
Ví dụ thực tế: Gói có kỳ tính phí theo tháng nhưng booking lại bắt đầu giữa tháng. → Cho khách chọn đúng khoảng được phép book, không lệch ngày. → UI lấy
BookingStart/BookingEndtừBillingSchedules; rỗng thì hiện "unavailable" và chặn submit.
"Rollover alert biến mất sau khi link term"
Cố ý: alert rollover bị mute cho tới khi tính năng rollover sẵn sàng. Việc mute là notification-only — không đổi state Subscription/Booking/Enrollment/Billing/Invoice/Term Planner/queue/worker. Link-term success vẫn hoàn tất + refresh linked-term như cũ; error handling giữ nguyên.
"Quyền chọn start/end của khách"
Phụ thuộc setting gói (Booking Start Selection, Booking End Selection):
| Setting | Khách được chọn? |
|---|---|
Staff Only (mặc định, 0) | Không |
Staff & Customer | Có |
Staff luôn chọn được cả start lẫn end.
Ví dụ thực tế: Trung tâm muốn khách tự chọn ngày bắt đầu gói để chủ động lịch. → Mở quyền chọn mốc cho phụ huynh thay vì chỉ staff. → Đặt
Booking Start Selection=Staff & Customer; khách thấy và chọn được ngày start.