Subscription — Luồng xử lý trong code
Subscription trải trên cả frontend (Tuke/Topi) và backend (Tux). Trang này nêu các đường chính; tên API/spec là điểm neo để tra cứu.
Luồng booking (khách hàng submit)
Guard trùng lịch (overlap validation)
Khi submit/edit/confirm, hệ thống so các schedule rows submit với:
- Enrollment đang
Submitted/Confirmed(soEnrollmentPattern). - TermBookingOrder normal đang
Submitted/Quote/Submitted_Change. - TermBooking normal đang
Approved/Attendance_Approved.
Trùng bất kỳ → toàn request fail (atomic, không tạo state một phần). Edit loại trừ EnrollmentId hiện tại để không tự xung đột. Direct-confirm & future-confirm dùng cùng guard.
Direct-confirm & Submit & Confirm (admin)
- Chỉ admin/staff được dùng direct-confirm; khách/non-admin → từ chối.
- Dùng Subscription làm template, nhưng mutate runtime chỉ qua service confirmation hiện có (không sửa trực tiếp Enrollment/Invoice/Booking từ UI setup).
- Response không báo "confirmed success" trừ khi runtime đã hoàn tất confirmation semantics.
- Submit & Confirm: submit với direct-confirm intent; thành công → confirmed ngay, không cần vào Booking Manager. Submit thường giữ pending/submitted.
- Edit mode:
Submit & Confirmdừng lại sau submit để xem Billing Difference; chỉ confirm khi bấmConfirmtrong modal. - Confirm/Terminate/Cancel/Submit đều nhận mute notification intent (Mute Notifications); mute chỉ bỏ email, không đổi lifecycle/validation/billing.
Billing Details: Schedule / Approve / Skip
| Hành động | Backend làm |
|---|---|
| Schedule | Tạo một InvoiceScheduler / invoice; ScheduledOn = Invoice.Date (UTC). |
| Approve | Update invoice → Approved; tạo InvoiceScheduler với ScheduledOn = DateTime.UtcNow. |
| Skip | Deactivate scheduler đang active (nếu có); update invoice → Initialised (0). |
Trang derive intent nút từ trạng thái invoice, không từ
EnrollmentInvoiceLog.
Subscription Billing Manager
- Filter Site + Subscription (bắt buộc) + Term (tùy chọn); nguồn options:
GET /Subscription/WithTerms(không/Subscription/List, không đọcPeriods). Bấm Apply để load bảng. - Mỗi row = một
SubscriptionTermBillingSchedule(gồm term archivedStatusId=600): Term, Period, Invoice Date, Due Date, Stats (generated/expectedtheo enrollment active-approved), Action. - View → redirect Billing Details với context site/subscription/term. Non-SuperAdmin (
BusinessUnit.TypeId ≠ 99): manager read-only, không có approve/skip/generate. - SuperAdmin (
TypeId = 99): row có Calculate → drawerGET /api/subscription-billing/ScheduleSummary/{id}(+POSTkhi chưa có summary) rồi Generate Subscription Booking (POST /api/ConsumerEnrollment/BillingEnrollment/{scheduleId}); đóng drawer → refresh grid. - Sinh invoice yêu cầu schedule có
SubscriptionPriceId+SubscriptionPriceLineactive (kể cảUnitPrice = 0.00); thiếu → validation fail trước khi persist, không fallback theo schedule amount.
Manage Add-ons / Discounts
Chỉ mutate state add-on selection thuộc enrollment; billing side effect đi qua service backend (popup không trực tiếp approve/skip/schedule/regenerate invoice).
Setup: các quy ước cần giữ khi code
| Quy ước | Ghi chú |
|---|---|
| Archive chỉ khi unpublished | Gói đã publish → server từ chối archive. |
CutOffMinute lưu phút, UI ngày | Decimal bị reject; 0/null = không có cut-off. |
Billing Type khóa Flat Rate | Request Dynamic bị reject/normalize. |
BillingOnly editable trước khi tạo | Sau khi tồn tại → read-only. |
| Add Price mới = blank | Không prefill từ price đã xem/lưu. |
| Rollover alert bị mute | Notification-only: không đổi state booking/enrollment/billing/term planner. |
Thông báo vòng đời booking
- Submitted (còn pending) →
New Subscription Booking (Client)cho khách +(Staff)cho admin/staff. - Confirmed →
Subscription Booking Confirmed (Client); Cancelled →(Client)kèm site contact phone/email. - Submit & Confirm → không queue submitted/pending; chỉ confirmed.
- Mute intent (admin action) → không queue/send email đáng lẽ gửi; lifecycle vẫn persist.
- Notification được tạo sau khi lifecycle đã persist; render chỉ đọc, không mutate state.
- Placeholder resolve từ context:
,,,,,, v.v.
Frontend (Subscription Manager)
- Filter subscription:
GET /api/Subscription(admin). Site filter giới hạn options theo site. - Đổi filter → không tự reload; bấm Apply mới load bảng theo filter hiện tại. Load đầu trang tự chạy; mutation (confirm/cancel/delete/terminate/rollover) thành công cũng refresh. Response request cũ không ghi đè data mới (chống race khi switch nhanh).
- Expanded row: hiện add-ons rồi discounts; có nút Update mở Manage Add-ons.
Nơi tìm code
Frontend ở Tuke/ (Next.js) và một phần Topi/ (admin cũ). Backend + API ở Tux/ (ConsumerEnrollment, Subscription, Invoice services).
Tiếp theo: Case thường gặp.