INTERNAL DEV ONLY SRS TXNG-F45: Cấu hình Gói Dịch vụ (WBS 2.33.1 & 2.35.1)

TÀI LIỆU ĐẶC TẢ YÊU CẦU PHẦN MỀM (SRS) ​

HỆ THỐNG: NỀN TẢNG TRUY XUẤT NGUỒN GỐC GOTRACE ​


TXNG-F45 -- Cấu hình Gói Dịch vụ ​

Thông tin Chi tiết
Phiên bản 1.0
Ngày tạo 01/07/2026
Ngày cập nhật 01/07/2026
Tác giả GoTrace Team
Người review
Người phê duyệt

Lịch sử thay đổi ​

Ngày Người cập nhật Mô tả thay đổi
01/07/2026 GoTrace Team Khởi tạo SRS v1.0 — Cấu hình động 4 gói dịch vụ (Cơ bản / Chuẩn hóa / Liên kết / Quản trị) theo bảng giá docs/sale/Sale_Pricing.md (hiệu lực 01/06/2026). Bảng mới service_plans: phí khởi tạo / chuẩn hóa / duy trì, quota QR-năm / SP / user / storage, phí tăng thêm, features JSONB (ma trận tính năng), chiết khấu 2y/3y, vòng đời SERVICE_PLAN_STATUS (DRAFT / ACTIVE / DEPRECATED). Chỉ PLATFORM_ADMIN CRUD. Seed 4 gói canonical. Module vận hành thương mại — KHÔNG có CTE/KDE truy xuất.

1. Tổng quan tài liệu ​

1.1. Mục đích tài liệu ​

Tài liệu mô tả chi tiết chức năng Cấu hình Gói Dịch vụ (TXNG-F45) trong Module Quản trị Nền tảng của hệ thống GoTrace.

Chức năng cho phép Quản trị viên Nền tảng (PLATFORM_ADMIN) cấu hình động danh mục các gói dịch vụ thương mại mà GoTrace bán cho Doanh nghiệp (DN), theo bảng giá gốc docs/sale/Sale_Pricing.md (hiệu lực 01/06/2026). Mỗi gói định nghĩa đầy đủ:

  1. Nhóm phí: phí khởi tạo (setup_fee), phí chuẩn hóa và liên kết (standardization_fee), phí duy trì hệ thống theo năm (maintenance_fee_year).
  2. Quota / giới hạn: sản lượng mã QR/năm (qr_quota_year), số sản phẩm tối đa (max_products), số người dùng tối đa (max_users), dung lượng lưu trữ (storage_gb).
  3. Phí phát sinh vượt hạn mức: phí mỗi mã QR tăng thêm (extra_qr_fee), phí mỗi mã sản phẩm tăng thêm (extra_product_fee).
  4. Đào tạo & hỗ trợ: hình thức đào tạo (training_type), số lần Onsite (training_onsite_count), số ngày hỗ trợ go-live (go_live_support_days), cam kết SLA (sla_support).
  5. Ma trận tính năng (features JSONB): so sánh chi tiết tính năng giữa 4 gói theo Phụ lục của Sale_Pricing.
  6. Chính sách chiết khấu duy trì: chiết khấu 2 năm (discount_2y_percent, mặc định 10%), 3 năm (discount_3y_percent, mặc định 20%), áp dụng trước VAT.

Gói dịch vụ được cấu hình tại F45 là nguồn dữ liệu chuẩn (canonical) cho luồng Kích hoạt & Vòng đời Dịch vụ DN (TXNG-F46): khi kích hoạt một DN, hệ thống chốt bản sao (plan_snapshot) cấu hình gói tại thời điểm kích hoạt để đảm bảo tính bất biến của hợp đồng.

Tài liệu là cơ sở cho đội ngũ phát triển (Developer), kiểm thử (QA), DBA và Business Analyst để triển khai chức năng phù hợp với BRD tương ứng (docs/brd/BRD_TXNG-F45_Cau_Hinh_Goi_Dich_Vu.md).

Phạm vi loại trừ (Out of scope): TXNG-F45 là module vận hành thương mại nội bộ, KHÔNG phát sinh Critical Tracking Event (CTE) / Key Data Element (KDE) truy xuất nguồn gốc. Việc kích hoạt, dùng thử, gia hạn, chấm dứt dịch vụ và chặn đăng nhập thuộc phạm vi TXNG-F46.

1.2. Thông tin chức năng ​

Thuộc tính Giá trị
Mã chức năng TXNG-F45
Tên chức năng Cấu hình Gói Dịch vụ
Menu TXNG > Quản trị Nền tảng > Gói Dịch vụ
Ưu tiên Bắt buộc
Giai đoạn v1.0
Pháp lý Chính sách thương mại nội bộ GoTrace (docs/sale/Sale_Pricing.md, 19/05/2026); Luật Giao dịch điện tử 2023 (căn cứ giá trị hợp đồng điện tử); Bộ luật Dân sự 2015 (hợp đồng dịch vụ); Nghị định 13/2023/NĐ-CP (bảo vệ dữ liệu cá nhân — chỉ áp dụng phần cấu hình, không lưu dữ liệu cá nhân trong F45). Module vận hành thương mại — KHÔNG áp dụng TCVN 12850:2019 / FDA / GS1 (không có CTE/KDE)
Liên thông → TXNG-F46 (Kích hoạt & Vòng đời Dịch vụ DN — chọn service_plans.status = ACTIVE để kích hoạt, chốt plan_snapshot)
→ TXNG-F16 (Cấp mã QR — ràng buộc qr_quota_year qua effective_qr_quota của DN)
→ TXNG-F03 (Sản phẩm — ràng buộc max_products qua effective_max_products của DN)
↔ TXNG-F01 (Quản lý Doanh nghiệp — gói được gán vào DN qua F46)

1.3. Tác nhân tham gia (Actors) ​

Actor Vai trò Quyền hạn
PLATFORM_ADMIN Quản trị viên Nền tảng GoTrace (impl ROLE.ADMIN = numeric 1, chặn qua AdminGuard, API prefix /api/v1/admin/...) Full CRUD gói dịch vụ: tạo gói mới, sửa cấu hình phí/quota/features, đổi trạng thái gói (DRAFT → ACTIVE → DEPRECATED), xem danh sách / chi tiết / lịch sử version. Là role duy nhất được thao tác ghi. Nhân viên Sale thao tác gián tiếp qua tài khoản PLATFORM_ADMIN (SALE_STAFF không phải actor ghi trực tiếp)
ENTERPRISE_ADMIN Quản trị viên Doanh nghiệp Chỉ xem danh sách gói công khai (is_public = true AND status = ACTIVE) qua endpoint public tùy chọn GET /api/v1/service-plans/public để tham khảo bảng giá. KHÔNG thấy gói DRAFT/DEPRECATED, KHÔNG thao tác cấu hình
SYSTEM Hệ thống tự động Cung cấp cấu hình gói cho F46 khi kích hoạt (đọc service_plans → chốt plan_snapshot); validate ràng buộc quota khi F16/F03 kiểm tra hạn mức DN; KHÔNG tự thay đổi cấu hình gói

Ghi chú phân quyền: Mọi endpoint ghi (POST/PUT/PATCH) đều đi qua prefix /api/v1/admin/... và AdminGuard chặn theo ROLE.ADMIN. Endpoint đọc công khai GET /api/v1/service-plans/public không yêu cầu quyền admin nhưng chỉ trả về gói công khai đang bán.

1.4. Tham chiếu UI/UX ​

Tài liệu Link
Mockup danh sách gói dịch vụ docs/html/goi_dich_vu/service_plan_list.html (sẽ tạo trong Phase implementation)
Mockup tạo/sửa gói dịch vụ docs/html/goi_dich_vu/service_plan_form.html
Mockup chi tiết gói + lịch sử version docs/html/goi_dich_vu/service_plan_detail.html
Design System apps/frontend/CLAUDE.md — Section Frontend UI/UX (HeroUI, DebouncedInput, CSelect, useFilter, TableWrapper, FormWrapper/FormRow, React Hook Form + Zod)

2. Luồng nghiệp vụ (Business Flow) ​

2.1. Luồng nghiệp vụ tổng quan ​

┌──────────────────────────────────────────────────────────────────────────────────────┐
│                    LUỒNG CẤU HÌNH GÓI DỊCH VỤ (F45)                                  │
├──────────────────────────────────────────────────────────────────────────────────────┤
│                                                                                      │
│  ╔══════════════════════════════════════════════════════════════════════════════╗  │
│  ║               SEED KHỞI TẠO: 4 GÓI CANONICAL (Migration)                     ║  │
│  ╚══════════════════════════════════════════════════════════════════════════════╝  │
│    ┌───────────────┐  ┌────────────────┐  ┌───────────────┐  ┌────────────────┐    │
│    │ PKG_BASIC     │  │ PKG_STANDARD   │  │ PKG_LINKED    │  │ PKG_ENTERPRISE │    │
│    │ Gói Cơ bản    │  │ Gói Chuẩn hóa  │  │ Gói Liên kết  │  │ Gói Quản trị   │    │
│    │ status=ACTIVE │  │ status=ACTIVE  │  │ status=ACTIVE │  │ status=ACTIVE  │    │
│    └───────────────┘  └────────────────┘  └───────────────┘  └────────────────┘    │
│                                    │                                                  │
│  ════════════════════════════════════════════════════════════════════════════════  │
│                                    ▼                                                  │
│    ┌────────────────────────────────────────────────────────────────────────┐      │
│    │ PLATFORM_ADMIN mở SCR-F45-01 (Danh sách gói)                            │      │
│    │ • Xem/tìm kiếm/lọc theo trạng thái, từ khóa                             │      │
│    └───────────────────────────────┬────────────────────────────────────────┘      │
│                                     │                                                │
│         ┌───────────────────────────┼───────────────────────────┐                   │
│         ▼                           ▼                           ▼                   │
│  ┌─────────────┐            ┌─────────────┐            ┌─────────────────┐          │
│  │ TẠO GÓI MỚI │            │ SỬA GÓI     │            │ ĐỔI TRẠNG THÁI  │          │
│  │ (UC-F45-01) │            │ (UC-F45-02) │            │ (UC-F45-03)     │          │
│  │ SCR-F45-02  │            │ SCR-F45-02  │            │ SCR-F45-01/03   │          │
│  │ status=DRAFT│            │ • tăng      │            │ DRAFT→ACTIVE    │          │
│  │             │            │   version   │            │ ACTIVE⇄DEPRECAT │          │
│  └──────┬──────┘            └──────┬──────┘            └────────┬────────┘          │
│         │                          │                           │                    │
│         └──────────────────────────┼───────────────────────────┘                    │
│                                     ▼                                                │
│    ┌────────────────────────────────────────────────────────────────────────┐      │
│    │ AUDIT: mỗi thao tác ghi service_plans.updated_by + version++ (khi sửa   │      │
│    │ phí/quota); soft delete deleted_at; validate plan_code immutable        │      │
│    └───────────────────────────────┬────────────────────────────────────────┘      │
│                                     ▼                                                │
│    ┌────────────────────────────────────────────────────────────────────────┐      │
│    │ TIÊU THỤ: TXNG-F46 đọc gói status=ACTIVE để kích hoạt DN                │      │
│    │ → chốt plan_snapshot (bất biến); gói DEPRECATED KHÔNG được chọn mới     │      │
│    │ nhưng DN đang dùng vẫn giữ snapshot cũ                                  │      │
│    └────────────────────────────────────────────────────────────────────────┘      │
│                                                                                      │
└──────────────────────────────────────────────────────────────────────────────────────┘

2.2. Diễn giải luồng ​

Bước Mô tả hành động Hệ thống thực hiện
B1 Migration seed 4 gói canonical (PKG_BASIC / PKG_STANDARD / PKG_LINKED / PKG_ENTERPRISE) theo Sale_Pricing.md INSERT 4 bản ghi service_plans với đầy đủ phí, quota, features JSONB, status = ACTIVE, version = 1, display_order 1–4
B2 PLATFORM_ADMIN mở SCR-F45-01, tìm kiếm / lọc / xem danh sách gói SELECT service_plans WHERE deleted_at IS NULL + filter (status, keyword) ORDER BY display_order; pagination
B3 (Tạo mới) ADMIN click "Tạo gói" → điền form SCR-F45-02 → Lưu 1. Validate server-side (plan_code UNIQUE + UPPER_SNAKE, phí ≥ 0, quota ≥ 0, features theo JSON schema)
2. INSERT service_plans {status: DRAFT, version: 1, created_by}
3. Return 201 Created
B4 (Sửa) ADMIN mở gói → sửa phí/quota/features/tính năng → Lưu 1. Validate; plan_code KHÔNG được đổi (immutable — E-F45-002)
2. UPDATE service_plans {updated_by}; nếu thay đổi phí/quota → version++
3. Return 200 OK
B5 (Đổi trạng thái) ADMIN đổi DRAFT→ACTIVE / ACTIVE⇄DEPRECATED 1. Validate transition hợp lệ (Section 12)
2. UPDATE service_plans.status, updated_by
3. Nếu → DEPRECATED: gói không hiện trong danh sách chọn khi kích hoạt mới ở F46 (nhưng DN đang dùng giữ plan_snapshot)
4. Return 200 OK
B6 (Xóa) ADMIN xóa gói (chỉ khi chưa DN nào dùng) 1. Kiểm tra không có company_subscriptions tham chiếu plan_id (E-F45-006)
2. Soft delete: SET deleted_at = NOW(), updated_by
3. Return 200 OK
B7 F46 kích hoạt DN SELECT service_plans WHERE status = ACTIVE AND deleted_at IS NULL; chốt plan_snapshot JSONB vào company_subscriptions

3. Phân loại / Trạng thái ​

3.1. Trạng thái gói dịch vụ (SERVICE_PLAN_STATUS — Canonical §2.45) ​

Trạng thái Mã Mô tả CSS class Background Color
Nháp DRAFT Đang soạn cấu hình gói, chưa cho phép gán cho DN .bdg.bdg-neutral #f0f2f5 #8c99ab
Đang áp dụng ACTIVE Đang bán, có thể gán cho DN khi kích hoạt (F46) .bdg.bdg-success #e8f5e9 #2e7d32
Ngừng bán DEPRECATED Ngừng bán gói mới; DN đang dùng vẫn giữ plan_snapshot .bdg.bdg-warning #fff8e1 #fb8c00

3.2. Mã gói canonical (SERVICE_PLAN_CODE — Canonical §2.44) ​

Mã Tên gói (hiển thị) Đối tượng sử dụng
PKG_BASIC Gói Cơ bản OCOP, HTX, hộ kinh doanh cần số hóa dữ liệu cơ bản, QR truy xuất, chứng nhận, vùng nguyên liệu, trang truy xuất công khai
PKG_STANDARD Gói Chuẩn hóa DN cần chuẩn hóa dữ liệu sản phẩm, quy trình sản xuất, lô, chứng nhận, kiểm nghiệm và QR truy xuất trên hệ thống thống nhất
PKG_LINKED Gói Liên kết DN có nhiều nhà cung cấp, đối tác phân phối, lô sản xuất cần liên kết dữ liệu để truy xuất toàn chuỗi cung ứng
PKG_ENTERPRISE Gói Quản trị DN xuất khẩu, DN quy mô lớn (BigCorp) cần dashboard, cảnh báo, phân quyền, mở rộng API và tích hợp nâng cao

plan_code là dữ liệu động; admin có thể thêm gói mới với mã UPPER_SNAKE_CASE bắt đầu bằng PKG_. 4 mã trên là seed canonical bắt buộc.

3.3. Hình thức đào tạo (Training Type) ​

Mã Tên Mô tả
ONLINE Đào tạo trực tuyến Đào tạo qua họp trực tuyến, tài liệu hướng dẫn
ONSITE Đào tạo tại chỗ Đào tạo trực tiếp tại cơ sở DN (kèm training_onsite_count số lần)

3.4. Chu kỳ thanh toán (Billing Cycle) ​

Mã Tên Mô tả
ANNUAL Theo năm Phí duy trì tính theo năm (mặc định)

4. Danh sách màn hình ​

Mã màn hình Tên màn hình Chức năng chính Vai trò sử dụng Ghi chú
SCR-F45-01 Danh sách Gói Dịch vụ Hiển thị, tìm kiếm, lọc, đổi trạng thái danh sách gói PLATFORM_ADMIN Màn hình chính; summary theo trạng thái
SCR-F45-02 Tạo / Sửa Gói Dịch vụ Form tạo mới hoặc sửa cấu hình gói (phí, quota, features, chiết khấu) PLATFORM_ADMIN React Hook Form + Zod; FormWrapper/FormRow
SCR-F45-03 Chi tiết Gói Dịch vụ Xem toàn bộ cấu hình gói + lịch sử version + nút đổi trạng thái PLATFORM_ADMIN Tabs: Thông tin / Ma trận tính năng / Lịch sử version

5. Mô tả chi tiết trường dữ liệu ​

5.1. SCR-F45-01: Danh sách Gói Dịch vụ ​

Summary cards (đếm theo trạng thái):

Card Nguồn dữ liệu
Tổng số gói COUNT(service_plans WHERE deleted_at IS NULL)
Đang áp dụng COUNT(status = ACTIVE)
Nháp COUNT(status = DRAFT)
Ngừng bán COUNT(status = DEPRECATED)

Thanh công cụ (Toolbar):

Phần tử Loại Mô tả
Tìm kiếm Text Input (DebouncedInput, 300ms) Tìm theo plan_code, plan_name (≥ 2 ký tự)
Lọc theo trạng thái Dropdown (CSelect) DRAFT / ACTIVE / DEPRECATED / Tất cả
Nút "Tạo gói" Button (Primary) Mở SCR-F45-02 (chế độ tạo mới) — PLATFORM_ADMIN

Bảng dữ liệu (TableWrapper):

Cột Kiểu Sortable Mô tả
STT Number Không Số thứ tự (theo display_order)
Mã gói Text (Link) Có plan_code — click mở SCR-F45-03
Tên gói Text Có plan_name
Phí khởi tạo Currency Có setup_fee (VND, format #,##0)
Phí chuẩn hóa Currency Có standardization_fee
Phí duy trì/năm Currency Có maintenance_fee_year
Tổng năm đầu Currency Không setup_fee + standardization_fee + maintenance_fee_year (computed)
Quota QR/năm Number Có qr_quota_year
SP tối đa Number Có max_products
Trạng thái Badge Có DRAFT / ACTIVE / DEPRECATED (color-coded §3.1)
Thao tác Actions Không Xem / Sửa / Đổi trạng thái / Xóa (theo trạng thái + BR)

Phân trang: 10 / 20 / 50 / 100 bản ghi/trang. Response envelope { page, page_size, total_items, total_pages }.

5.2. SCR-F45-02: Tạo / Sửa Gói Dịch vụ ​

Card 1: Thông tin định danh ​

Tên trường Field name Kiểu dữ liệu Bắt buộc Validation Mô tả
Mã gói plan_code Text (3–30) Có UNIQUE (WHERE deleted_at IS NULL); regex ^[A-Z][A-Z0-9_]{2,29}$; immutable sau khi tạo Mã định danh gói (UPPER_SNAKE_CASE). Khi Sửa: trường read-only
Tên gói plan_name Text (1–150) Có max 150 ký tự Tên hiển thị gói (tiếng Việt có dấu)
Đối tượng sử dụng target_audience Textarea Không max 2000 ký tự Mô tả đối tượng khách hàng phù hợp
Thứ tự hiển thị display_order Number Không ≥ 0; INT Thứ tự sắp xếp trong danh sách (mặc định 0)
Công khai bảng giá is_public Toggle Không boolean Cho phép hiển thị ở endpoint public (mặc định true)
Ngày hiệu lực effective_from Date Không ISO 8601 Ngày cấu hình gói bắt đầu có hiệu lực

Card 2: Cấu hình phí (Fees — VND, chưa gồm VAT) ​

Tên trường Field name Kiểu dữ liệu Bắt buộc Validation Mô tả
Phí khởi tạo setup_fee Decimal(14,2) Có ≥ 0 Phí khởi tạo hệ thống (khảo sát, thiết kế cấu trúc dữ liệu, cấu hình, mẫu QR)
Phí chuẩn hóa & liên kết standardization_fee Decimal(14,2) Có ≥ 0 Phí chuẩn hóa dữ liệu nền + liên kết chuỗi cung ứng + import dữ liệu ban đầu
Phí duy trì/năm maintenance_fee_year Decimal(14,2) Có ≥ 0 Phí duy trì hệ thống theo năm (hạ tầng, bảo mật, sao lưu, hỗ trợ)
Phí mã QR tăng thêm extra_qr_fee Decimal(10,2) Có ≥ 0 Phí mỗi mã QR vượt qr_quota_year
Phí mã SP tăng thêm extra_product_fee Decimal(12,2) Có ≥ 0 Phí mỗi mã sản phẩm vượt max_products (cộng vào phí duy trì kỳ tiếp)

Tổng phí năm đầu (hiển thị read-only, computed): setup_fee + standardization_fee + maintenance_fee_year. Từ năm 2: maintenance_fee_year.

Card 3: Cấu hình quota / giới hạn ​

Tên trường Field name Kiểu dữ liệu Bắt buộc Validation Mô tả
Sản lượng QR/năm qr_quota_year Number (INT) Có ≥ 0 Số mã QR được cấp/năm (ràng buộc F16)
Số SP tối đa max_products Number (INT) Có ≥ 0 Số mã sản phẩm tối đa (ràng buộc F03)
Số user tối đa max_users Number (INT) Có ≥ 0 Số tài khoản người dùng tối đa của DN
Dung lượng lưu trữ (GB) storage_gb Number (INT) Có ≥ 0 Dung lượng lưu trữ file (GB)

Card 4: Đào tạo & hỗ trợ ​

Tên trường Field name Kiểu dữ liệu Bắt buộc Validation Mô tả
Hình thức đào tạo training_type Dropdown Có Enum: ONLINE / ONSITE (§3.3) Hình thức đào tạo tiêu chuẩn
Số lần Onsite training_onsite_count Number (INT) Có ≥ 0; = 0 nếu ONLINE Số buổi đào tạo tại chỗ
Hỗ trợ go-live (ngày) go_live_support_days Number (INT) Có ≥ 0 Số ngày hỗ trợ đưa hệ thống vào vận hành
SLA hỗ trợ sla_support Text (1–100) Có max 100 ký tự Cam kết hỗ trợ (vd: "Trong giờ hành chính", "24/7", "Ưu tiên 24/7")

Card 5: Ma trận tính năng (features JSONB) ​

Tên trường Field name Kiểu dữ liệu Bắt buộc Validation Mô tả
Ma trận tính năng features JSONB Có Theo JSON schema ma trận (BR-F45-04) Object mô tả từng tính năng (bật/tắt/mức độ). Xem Section 18 và schema dưới

JSON schema features (giá trị mỗi khóa ∈ {true, false, "OPTIONAL", "CUSTOM"} hoặc số/chuỗi mô tả mức):

Khóa (feature key) Kiểu giá trị Ý nghĩa
company_profile boolean Hồ sơ doanh nghiệp/chủ thể
product_catalog_standardization boolean Chuẩn hóa danh mục sản phẩm (giới hạn theo max_products)
material_input_standardization boolean Chuẩn hóa thông tin nguyên liệu đầu vào
certification_standardization boolean Chuẩn hóa danh mục chứng nhận/chứng chỉ
growing_area_standardization boolean Chuẩn hóa danh mục vùng trồng/vùng nuôi
customer_agent_standardization boolean Chuẩn hóa dữ liệu khách hàng/đại lý
staff_standardization boolean Chuẩn hóa danh mục nhân viên
production_process_standardization boolean Chuẩn hóa danh mục quy trình sản xuất
product_lot_code_consulting boolean Tư vấn cấu trúc mã sản phẩm/lô
qr_traceability boolean QR truy xuất nguồn gốc
public_trace_page "STANDARD" / "CUSTOM" Trang truy xuất công khai (chuẩn / tùy chỉnh)
qr_scan_statistics true / "CUSTOM" Thống kê lượt quét QR
supply_chain_linking boolean Liên kết các chủ thể chuỗi cung ứng (chủ thể ≥ gói Chuẩn hóa)
product_recall_management boolean Quản lý thu hồi sản phẩm
operation_dashboard true / "CUSTOM" Dashboard vận hành
activity_log boolean Nhật ký thao tác
api_integration false / "BASIC" / "CUSTOM" API tích hợp (none / 1 API cơ bản / theo phạm vi)
blockchain false / "OPTIONAL" Blockchain (không / tùy chọn mua thêm)

Card 6: Chính sách chiết khấu & thanh toán ​

Tên trường Field name Kiểu dữ liệu Bắt buộc Validation Mô tả
Chiết khấu 2 năm (%) discount_2y_percent Decimal(5,2) Không 0–100; mặc định 10 Chiết khấu phí duy trì khi ký 2 năm (trước VAT)
Chiết khấu 3 năm (%) discount_3y_percent Decimal(5,2) Không 0–100; mặc định 20 Chiết khấu phí duy trì khi ký 3 năm (trước VAT)
Chu kỳ thanh toán billing_cycle Dropdown Không Enum: ANNUAL (§3.4); mặc định ANNUAL Chu kỳ tính phí duy trì
Đơn vị tiền tệ currency Text (auto) Không mặc định VND Đơn vị tiền tệ (read-only v1.0)

Trường hệ thống (read-only, hiển thị ở SCR-F45-03) ​

Tên trường Field name Kiểu dữ liệu Mô tả
Phiên bản version INT Số version cấu hình, tăng khi sửa phí/quota (mặc định 1)
Trạng thái status Enum DRAFT / ACTIVE / DEPRECATED (§3.1)
Người tạo created_by BIGINT FK User tạo gói
Người cập nhật updated_by BIGINT FK User cập nhật gần nhất
Ngày tạo created_at TIMESTAMPTZ Thời điểm tạo
Ngày cập nhật updated_at TIMESTAMPTZ Thời điểm cập nhật gần nhất

5.3. SCR-F45-03: Chi tiết Gói Dịch vụ ​

Header Info Box (read-only): plan_code lớn + badge trạng thái màu; plan_name; tổng phí năm đầu; version; người tạo/cập nhật + thời điểm.

Tabs:

Action buttons (theo status + role PLATFORM_ADMIN):

Status Hành động khả dụng
DRAFT Sửa / Kích hoạt (→ ACTIVE) / Xóa (nếu chưa DN dùng)
ACTIVE Sửa / Ngừng bán (→ DEPRECATED)
DEPRECATED Sửa / Mở lại (→ ACTIVE) / Xóa (nếu chưa DN dùng)

6. Danh sách Use Case ​

ID Tên Use Case Actor chính Mô tả ngắn
UC-F45-01 Tạo gói dịch vụ PLATFORM_ADMIN Tạo gói mới với cấu hình phí/quota/features, status = DRAFT
UC-F45-02 Sửa gói dịch vụ PLATFORM_ADMIN Chỉnh sửa cấu hình gói; tăng version khi sửa phí/quota; ghi audit
UC-F45-03 Đổi trạng thái gói PLATFORM_ADMIN DRAFT→ACTIVE, ACTIVE⇄DEPRECATED
UC-F45-04 Xem danh sách / chi tiết gói PLATFORM_ADMIN, ENTERPRISE_ADMIN (public) Tìm kiếm, lọc, xem chi tiết cấu hình + ma trận tính năng + lịch sử version

7. Mô tả chi tiết Use Case ​

UC-F45-01: Tạo gói dịch vụ ​

Mô tả: PLATFORM_ADMIN tạo một gói dịch vụ mới với cấu hình đầy đủ phí, quota, tính năng.

Tiền điều kiện:

Hậu điều kiện:

Bước Tác nhân Hành động / Mô tả Phản hồi / Xử lý hệ thống Ghi chú
1 ADMIN Click "Tạo gói" trên SCR-F45-01 Mở SCR-F45-02 (chế độ tạo mới, form trống)
2 ADMIN Điền Card 1–6 (định danh, phí, quota, đào tạo, features, chiết khấu) Validate realtime client-side (Zod)
3 ADMIN Click "Lưu" 1. Validate toàn bộ form (server-side)
2. Kiểm tra plan_code UNIQUE + regex UPPER_SNAKE (E-F45-001)
3. Kiểm tra phí ≥ 0, quota ≥ 0 (E-F45-003)
4. Kiểm tra features theo JSON schema (E-F45-004)
5. INSERT service_plans {status: DRAFT, version: 1, created_by: current_user, created_at: NOW}
6. Ghi log {action: CREATE, user, timestamp}
7. Return 201 Created + plan_id
Status code: 201

Alternative Flow:

Bước Điều kiện Xử lý
3a plan_code đã tồn tại (chưa xóa mềm) E-F45-001 "Mã gói đã tồn tại"
3b plan_code sai định dạng (không UPPER_SNAKE) E-F45-001 "Mã gói phải viết HOA, dùng chữ/số/gạch dưới"
3c Phí hoặc quota < 0 E-F45-003 "Phí và quota phải ≥ 0"
3d features không đúng JSON schema E-F45-004 "Cấu trúc ma trận tính năng không hợp lệ"

UC-F45-02: Sửa gói dịch vụ ​

Tiền điều kiện: User là PLATFORM_ADMIN; gói tồn tại, chưa xóa mềm.

Hậu điều kiện: Cấu hình cập nhật; nếu thay đổi phí/quota → version++; ghi audit; plan_code không đổi.

Bước Tác nhân Hành động / Mô tả Phản hồi / Xử lý hệ thống Ghi chú
1 ADMIN Click "Sửa" trên SCR-F45-01 hoặc SCR-F45-03 Mở SCR-F45-02 với data hiện tại; plan_code read-only
2 ADMIN Chỉnh sửa phí/quota/features/đào tạo/chiết khấu → "Lưu" 1. Validate server-side
2. Kiểm tra plan_code KHÔNG bị đổi (E-F45-002)
3. So sánh diff phí/quota so với bản ghi hiện tại
4. UPDATE service_plans {..., updated_by: current_user, updated_at: NOW}
5. Nếu có thay đổi bất kỳ trong {setup_fee, standardization_fee, maintenance_fee_year, extra_qr_fee, extra_product_fee, qr_quota_year, max_products, max_users, storage_gb} → version = version + 1
6. Ghi log {action: UPDATE, user, timestamp, changes_json}
7. Return 200 OK
Audit trail bắt buộc

Alternative Flow:

Bước Điều kiện Xử lý
2a Client gửi plan_code khác giá trị gốc E-F45-002 "Không được thay đổi mã gói sau khi tạo"
2b Phí/quota < 0 E-F45-003
2c features sai schema E-F45-004
2d Sửa gói DEPRECATED Cho phép sửa cấu hình; W-F45-001 "Gói đang ngừng bán — thay đổi không ảnh hưởng DN đang dùng (giữ plan_snapshot)"

UC-F45-03: Đổi trạng thái gói ​

Tiền điều kiện: User là PLATFORM_ADMIN; transition hợp lệ theo Section 12.

Hậu điều kiện: status cập nhật; ghi audit.

Bước Tác nhân Hành động / Mô tả Phản hồi / Xử lý hệ thống
1 ADMIN Trên SCR-F45-01/03 click "Kích hoạt" / "Ngừng bán" / "Mở lại" Mở confirm dialog
2 ADMIN Xác nhận 1. Validate transition hợp lệ (E-F45-005 nếu không): DRAFT→ACTIVE; ACTIVE→DEPRECATED; DEPRECATED→ACTIVE
2. UPDATE service_plans.status, updated_by, updated_at
3. Ghi log {action: STATUS_CHANGE, from, to, user}
4. Nếu → DEPRECATED: gói bị loại khỏi danh sách chọn khi kích hoạt mới ở F46
5. Return 200 OK

Alternative Flow:

Bước Điều kiện Xử lý
2a Transition không hợp lệ (vd DRAFT→DEPRECATED) E-F45-005 "Chuyển trạng thái không hợp lệ"

UC-F45-04: Xem danh sách / chi tiết gói ​

Bước Tác nhân Hành động / Mô tả Phản hồi / Xử lý hệ thống
1 ADMIN Mở SCR-F45-01, nhập search / chọn filter Debounce 300ms; SELECT service_plans WHERE deleted_at IS NULL + filter (status, keyword ILIKE plan_code/plan_name) ORDER BY display_order LIMIT N OFFSET M
2 ADMIN Click 1 dòng Navigate SCR-F45-03; SELECT chi tiết + render features thành ma trận
3 ENTERPRISE_ADMIN Gọi GET /api/v1/service-plans/public SELECT service_plans WHERE status = ACTIVE AND is_public = true AND deleted_at IS NULL ORDER BY display_order (không trả trường nội bộ như created_by)

8. Thiết kế Database Schema ​

8.1. ER Diagram ​

┌─────────────────────────────────┐         ┌─────────────────────────────────┐
│           users                  │         │          companies (F01)         │
│  id (BIGINT, PK)                │         │  id (BIGINT, PK)                │
│  role (INT)                     │         │  status (INT) ── mirror F46      │
└──────────┬──────────────────────┘         └──────────┬──────────────────────┘
           │ 1:N (created_by, updated_by)              │ 1:N (qua F46)
           ▼                                            │
┌──────────────────────────────────────────────────────┼───────────────────────┐
│                       service_plans (F45)             │                       │
│  id (BIGSERIAL, PK)                                   │                       │
│  plan_code (VARCHAR 30) ── UNIQUE WHERE deleted_at IS NULL                    │
│  plan_name (VARCHAR 150)                                                      │
│  target_audience (TEXT)                                                       │
│  setup_fee, standardization_fee, maintenance_fee_year (NUMERIC 14,2)          │
│  qr_quota_year, max_products, max_users, storage_gb (INT)                     │
│  extra_qr_fee (NUMERIC 10,2), extra_product_fee (NUMERIC 12,2)                │
│  training_type (VARCHAR 20), training_onsite_count (INT)                      │
│  go_live_support_days (INT), sla_support (VARCHAR 100)                        │
│  features (JSONB)                                                             │
│  discount_2y_percent, discount_3y_percent (NUMERIC 5,2)                       │
│  billing_cycle (VARCHAR 20), currency (VARCHAR 10)                            │
│  status (VARCHAR 20) ── DRAFT / ACTIVE / DEPRECATED                           │
│  display_order (INT), is_public (BOOLEAN), effective_from (DATE)              │
│  version (INT)                                                                │
│  created_by, updated_by (BIGINT FK → users)                                  │
│  created_at, updated_at (TIMESTAMPTZ), deleted_at (TIMESTAMPTZ NULL)          │
└──────────────────────────────────┬────────────────────────────────────────────┘
                                    │ 1:N (tham chiếu qua F46 khi kích hoạt)
                                    ▼
                    ┌──────────────────────────────────────────┐
                    │  company_subscriptions (F46)             │
                    │  plan_id (BIGINT FK → service_plans)     │
                    │  plan_code (VARCHAR 30) ── snapshot      │
                    │  plan_snapshot (JSONB) ── chốt cấu hình  │
                    └──────────────────────────────────────────┘

8.2. Mô tả bảng ​

Bảng service_plans ​

Cột Kiểu dữ liệu Nullable Default Mô tả Constraint
id BIGSERIAL NOT NULL Khóa chính PRIMARY KEY
plan_code VARCHAR(30) NOT NULL Mã gói UPPER_SNAKE (PKG_*) UNIQUE WHERE deleted_at IS NULL
plan_name VARCHAR(150) NOT NULL Tên hiển thị gói
target_audience TEXT NULL Đối tượng sử dụng
setup_fee NUMERIC(14,2) NOT NULL 0 Phí khởi tạo (VND) CHECK (setup_fee >= 0)
standardization_fee NUMERIC(14,2) NOT NULL 0 Phí chuẩn hóa & liên kết CHECK (standardization_fee >= 0)
maintenance_fee_year NUMERIC(14,2) NOT NULL 0 Phí duy trì/năm CHECK (maintenance_fee_year >= 0)
qr_quota_year INT NOT NULL 0 Sản lượng QR/năm CHECK (qr_quota_year >= 0)
max_products INT NOT NULL 0 Số SP tối đa CHECK (max_products >= 0)
max_users INT NOT NULL 0 Số user tối đa CHECK (max_users >= 0)
storage_gb INT NOT NULL 0 Dung lượng lưu trữ (GB) CHECK (storage_gb >= 0)
extra_qr_fee NUMERIC(10,2) NOT NULL 0 Phí mỗi QR tăng thêm CHECK (extra_qr_fee >= 0)
extra_product_fee NUMERIC(12,2) NOT NULL 0 Phí mỗi SP tăng thêm CHECK (extra_product_fee >= 0)
training_type VARCHAR(20) NOT NULL 'ONLINE' Hình thức đào tạo CHECK IN ('ONLINE','ONSITE')
training_onsite_count INT NOT NULL 0 Số lần Onsite CHECK (training_onsite_count >= 0)
go_live_support_days INT NOT NULL 0 Số ngày hỗ trợ go-live CHECK (go_live_support_days >= 0)
sla_support VARCHAR(100) NULL Cam kết SLA hỗ trợ
features JSONB NOT NULL '{}' Ma trận tính năng
discount_2y_percent NUMERIC(5,2) NOT NULL 10 Chiết khấu 2 năm (%) CHECK (discount_2y_percent BETWEEN 0 AND 100)
discount_3y_percent NUMERIC(5,2) NOT NULL 20 Chiết khấu 3 năm (%) CHECK (discount_3y_percent BETWEEN 0 AND 100)
billing_cycle VARCHAR(20) NOT NULL 'ANNUAL' Chu kỳ thanh toán CHECK IN ('ANNUAL')
currency VARCHAR(10) NOT NULL 'VND' Đơn vị tiền tệ
status VARCHAR(20) NOT NULL 'DRAFT' Trạng thái gói CHECK IN ('DRAFT','ACTIVE','DEPRECATED')
display_order INT NOT NULL 0 Thứ tự hiển thị
is_public BOOLEAN NOT NULL true Công khai bảng giá
effective_from DATE NULL Ngày hiệu lực
version INT NOT NULL 1 Số version cấu hình
created_by BIGINT NULL User tạo FOREIGN KEY → users(id)
updated_by BIGINT NULL User cập nhật FOREIGN KEY → users(id)
created_at TIMESTAMPTZ NOT NULL NOW() Thời điểm tạo
updated_at TIMESTAMPTZ NOT NULL NOW() Thời điểm cập nhật
deleted_at TIMESTAMPTZ NULL Soft delete

8.3. Indexes ​

Tên index Cột Loại Mục đích
pk_service_plans id PRIMARY KEY Khóa chính
uq_service_plans_code plan_code UNIQUE (partial) WHERE deleted_at IS NULL Đảm bảo mã gói duy nhất (BR-F45-01, BR-F45-08)
idx_service_plans_status status B-tree Lọc theo trạng thái
idx_service_plans_display_order display_order B-tree Sắp xếp danh sách

8.4. Migration SQL ​

sql
-- =====================================================================
-- Migration: TXNG-F45 — service_plans (Cấu hình Gói Dịch vụ)
-- =====================================================================

CREATE TABLE IF NOT EXISTS service_plans (
    id                     BIGSERIAL     PRIMARY KEY,
    plan_code              VARCHAR(30)   NOT NULL,
    plan_name              VARCHAR(150)  NOT NULL,
    target_audience        TEXT,
    setup_fee              NUMERIC(14,2) NOT NULL DEFAULT 0 CHECK (setup_fee >= 0),
    standardization_fee    NUMERIC(14,2) NOT NULL DEFAULT 0 CHECK (standardization_fee >= 0),
    maintenance_fee_year   NUMERIC(14,2) NOT NULL DEFAULT 0 CHECK (maintenance_fee_year >= 0),
    qr_quota_year          INT           NOT NULL DEFAULT 0 CHECK (qr_quota_year >= 0),
    max_products           INT           NOT NULL DEFAULT 0 CHECK (max_products >= 0),
    max_users              INT           NOT NULL DEFAULT 0 CHECK (max_users >= 0),
    storage_gb             INT           NOT NULL DEFAULT 0 CHECK (storage_gb >= 0),
    extra_qr_fee           NUMERIC(10,2) NOT NULL DEFAULT 0 CHECK (extra_qr_fee >= 0),
    extra_product_fee      NUMERIC(12,2) NOT NULL DEFAULT 0 CHECK (extra_product_fee >= 0),
    training_type          VARCHAR(20)   NOT NULL DEFAULT 'ONLINE'
                             CHECK (training_type IN ('ONLINE','ONSITE')),
    training_onsite_count  INT           NOT NULL DEFAULT 0 CHECK (training_onsite_count >= 0),
    go_live_support_days   INT           NOT NULL DEFAULT 0 CHECK (go_live_support_days >= 0),
    sla_support            VARCHAR(100),
    features               JSONB         NOT NULL DEFAULT '{}'::jsonb,
    discount_2y_percent    NUMERIC(5,2)  NOT NULL DEFAULT 10 CHECK (discount_2y_percent BETWEEN 0 AND 100),
    discount_3y_percent    NUMERIC(5,2)  NOT NULL DEFAULT 20 CHECK (discount_3y_percent BETWEEN 0 AND 100),
    billing_cycle          VARCHAR(20)   NOT NULL DEFAULT 'ANNUAL' CHECK (billing_cycle IN ('ANNUAL')),
    currency               VARCHAR(10)   NOT NULL DEFAULT 'VND',
    status                 VARCHAR(20)   NOT NULL DEFAULT 'DRAFT'
                             CHECK (status IN ('DRAFT','ACTIVE','DEPRECATED')),
    display_order          INT           NOT NULL DEFAULT 0,
    is_public              BOOLEAN       NOT NULL DEFAULT true,
    effective_from         DATE,
    version                INT           NOT NULL DEFAULT 1,
    created_by             BIGINT        REFERENCES users(id),
    updated_by             BIGINT        REFERENCES users(id),
    created_at             TIMESTAMPTZ   NOT NULL DEFAULT NOW(),
    updated_at             TIMESTAMPTZ   NOT NULL DEFAULT NOW(),
    deleted_at             TIMESTAMPTZ
);

CREATE UNIQUE INDEX IF NOT EXISTS uq_service_plans_code
    ON service_plans (plan_code) WHERE deleted_at IS NULL;
CREATE INDEX IF NOT EXISTS idx_service_plans_status
    ON service_plans (status);
CREATE INDEX IF NOT EXISTS idx_service_plans_display_order
    ON service_plans (display_order);

-- =====================================================================
-- SEED: 4 gói canonical theo docs/sale/Sale_Pricing.md (hiệu lực 01/06/2026)
-- =====================================================================

INSERT INTO service_plans (
    plan_code, plan_name, target_audience,
    setup_fee, standardization_fee, maintenance_fee_year,
    qr_quota_year, max_products, max_users, storage_gb,
    extra_qr_fee, extra_product_fee,
    training_type, training_onsite_count, go_live_support_days, sla_support,
    features, discount_2y_percent, discount_3y_percent,
    billing_cycle, currency, status, display_order, is_public,
    effective_from, version
) VALUES
-- ----- PKG_BASIC — Gói Cơ bản -----
(
    'PKG_BASIC', 'Gói Cơ bản',
    'Dành cho chủ thể OCOP, HTX, hộ kinh doanh cần số hóa dữ liệu cơ bản, QR truy xuất, chứng nhận, vùng nguyên liệu và trang truy xuất công khai.',
    2000000, 3000000, 4500000,
    5000, 10, 2, 1,
    150, 490000,
    'ONLINE', 0, 7, 'Trong giờ hành chính',
    '{
        "company_profile": true,
        "product_catalog_standardization": true,
        "material_input_standardization": true,
        "certification_standardization": true,
        "growing_area_standardization": true,
        "customer_agent_standardization": true,
        "staff_standardization": true,
        "production_process_standardization": false,
        "product_lot_code_consulting": false,
        "qr_traceability": true,
        "public_trace_page": "STANDARD",
        "qr_scan_statistics": true,
        "supply_chain_linking": false,
        "product_recall_management": false,
        "operation_dashboard": true,
        "activity_log": false,
        "api_integration": false,
        "blockchain": false
    }'::jsonb,
    10, 20, 'ANNUAL', 'VND', 'ACTIVE', 1, true, DATE '2026-06-01', 1
),
-- ----- PKG_STANDARD — Gói Chuẩn hóa -----
(
    'PKG_STANDARD', 'Gói Chuẩn hóa',
    'Dành cho Doanh nghiệp cần chuẩn hóa dữ liệu sản phẩm, quy trình sản xuất, lô, chứng nhận, kiểm nghiệm và QR truy xuất trên một hệ thống thống nhất.',
    3000000, 5000000, 9500000,
    20000, 50, 5, 3,
    120, 490000,
    'ONLINE', 0, 15, '24/7',
    '{
        "company_profile": true,
        "product_catalog_standardization": true,
        "material_input_standardization": true,
        "certification_standardization": true,
        "growing_area_standardization": true,
        "customer_agent_standardization": true,
        "staff_standardization": true,
        "production_process_standardization": true,
        "product_lot_code_consulting": true,
        "qr_traceability": true,
        "public_trace_page": "STANDARD",
        "qr_scan_statistics": true,
        "supply_chain_linking": false,
        "product_recall_management": false,
        "operation_dashboard": true,
        "activity_log": false,
        "api_integration": false,
        "blockchain": "OPTIONAL"
    }'::jsonb,
    10, 20, 'ANNUAL', 'VND', 'ACTIVE', 2, true, DATE '2026-06-01', 1
),
-- ----- PKG_LINKED — Gói Liên kết -----
(
    'PKG_LINKED', 'Gói Liên kết',
    'Dành cho Doanh nghiệp có nhiều nhà cung cấp, đối tác phân phối, lô sản xuất cần liên kết dữ liệu để truy xuất toàn bộ chuỗi cung ứng.',
    6000000, 8000000, 25500000,
    100000, 150, 20, 10,
    100, 490000,
    'ONSITE', 1, 30, 'Ưu tiên 24/7',
    '{
        "company_profile": true,
        "product_catalog_standardization": true,
        "material_input_standardization": true,
        "certification_standardization": true,
        "growing_area_standardization": true,
        "customer_agent_standardization": true,
        "staff_standardization": true,
        "production_process_standardization": true,
        "product_lot_code_consulting": true,
        "qr_traceability": true,
        "public_trace_page": "STANDARD",
        "qr_scan_statistics": "CUSTOM",
        "supply_chain_linking": true,
        "product_recall_management": true,
        "operation_dashboard": "CUSTOM",
        "activity_log": true,
        "api_integration": "BASIC",
        "blockchain": "OPTIONAL"
    }'::jsonb,
    10, 20, 'ANNUAL', 'VND', 'ACTIVE', 3, true, DATE '2026-06-01', 1
),
-- ----- PKG_ENTERPRISE — Gói Quản trị -----
(
    'PKG_ENTERPRISE', 'Gói Quản trị',
    'Dành cho Doanh nghiệp xuất khẩu, DN quy mô lớn (BigCorp) hoặc doanh nghiệp cần dashboard, cảnh báo, phân quyền, mở rộng API và tích hợp nâng cao.',
    9000000, 15000000, 85000000,
    500000, 500, 50, 50,
    80, 490000,
    'ONSITE', 3, 60, 'Ưu tiên 24/7',
    '{
        "company_profile": true,
        "product_catalog_standardization": true,
        "material_input_standardization": true,
        "certification_standardization": true,
        "growing_area_standardization": true,
        "customer_agent_standardization": true,
        "staff_standardization": true,
        "production_process_standardization": true,
        "product_lot_code_consulting": true,
        "qr_traceability": true,
        "public_trace_page": "CUSTOM",
        "qr_scan_statistics": "CUSTOM",
        "supply_chain_linking": true,
        "product_recall_management": true,
        "operation_dashboard": "CUSTOM",
        "activity_log": true,
        "api_integration": "CUSTOM",
        "blockchain": "OPTIONAL"
    }'::jsonb,
    10, 20, 'ANNUAL', 'VND', 'ACTIVE', 4, true, DATE '2026-06-01', 1
)
ON CONFLICT DO NOTHING;

Đối chiếu tổng phí năm đầu (setup + standardization + maintenance): PKG_BASIC = 9.500.000; PKG_STANDARD = 17.500.000; PKG_LINKED = 39.500.000; PKG_ENTERPRISE = 109.000.000 VND — khớp 100% Sale_Pricing.md. Từ năm 2 = maintenance_fee_year: 4.500.000 / 9.500.000 / 25.500.000 / 85.000.000 VND.


9. Thiết kế API ​

9.1. Danh sách endpoint ​

# Method Endpoint Mô tả Auth / Role
1 GET /api/v1/admin/service-plans Danh sách gói (filter status, keyword; pagination) Bearer; PLATFORM_ADMIN
2 POST /api/v1/admin/service-plans Tạo gói mới (status = DRAFT) Bearer; PLATFORM_ADMIN
3 GET /api/v1/admin/service-plans/:id Chi tiết gói Bearer; PLATFORM_ADMIN
4 PUT /api/v1/admin/service-plans/:id Sửa cấu hình gói (tăng version khi sửa phí/quota) Bearer; PLATFORM_ADMIN
5 PATCH /api/v1/admin/service-plans/:id/status Đổi trạng thái gói (DRAFT/ACTIVE/DEPRECATED) Bearer; PLATFORM_ADMIN
6 DELETE /api/v1/admin/service-plans/:id Xóa mềm gói (chỉ khi chưa DN dùng) Bearer; PLATFORM_ADMIN
7 GET /api/v1/service-plans/public Danh sách gói công khai (ACTIVE + is_public) Bearer; mọi role đăng nhập (tùy chọn)

9.2. Chi tiết API ​

9.2.1. GET /api/v1/admin/service-plans ​

Query params: status (DRAFT/ACTIVE/DEPRECATED), keyword (≥ 2 ký tự, match plan_code/plan_name), page (default 1), page_size (default 20, ∈ {10,20,50,100}).

Response 200:

json
{
  "success": true,
  "statusCode": 200,
  "data": {
    "items": [
      {
        "id": 1,
        "plan_code": "PKG_BASIC",
        "plan_name": "Gói Cơ bản",
        "setup_fee": 2000000,
        "standardization_fee": 3000000,
        "maintenance_fee_year": 4500000,
        "first_year_total": 9500000,
        "qr_quota_year": 5000,
        "max_products": 10,
        "status": "ACTIVE",
        "display_order": 1,
        "version": 1
      }
    ],
    "pagination": { "page": 1, "page_size": 20, "total_items": 4, "total_pages": 1 }
  }
}

9.2.2. POST /api/v1/admin/service-plans ​

Request:

json
{
  "plan_code": "PKG_PREMIUM",
  "plan_name": "Gói Cao cấp",
  "target_audience": "DN chuỗi đa quốc gia",
  "setup_fee": 12000000,
  "standardization_fee": 18000000,
  "maintenance_fee_year": 120000000,
  "qr_quota_year": 1000000,
  "max_products": 1000,
  "max_users": 100,
  "storage_gb": 100,
  "extra_qr_fee": 60,
  "extra_product_fee": 490000,
  "training_type": "ONSITE",
  "training_onsite_count": 5,
  "go_live_support_days": 90,
  "sla_support": "Ưu tiên 24/7",
  "features": { "company_profile": true, "qr_traceability": true, "api_integration": "CUSTOM" },
  "discount_2y_percent": 10,
  "discount_3y_percent": 20,
  "billing_cycle": "ANNUAL",
  "display_order": 5,
  "is_public": true,
  "effective_from": "2026-07-01"
}

Response 201:

json
{ "success": true, "statusCode": 201, "data": { "id": 5, "plan_code": "PKG_PREMIUM", "status": "DRAFT", "version": 1 } }

Response 409 (trùng mã):

json
{ "success": false, "statusCode": 409, "error": { "code": "E-F45-001", "message": "Mã gói đã tồn tại" } }

9.2.3. GET /api/v1/admin/service-plans/:id ​

Response 200: trả về toàn bộ field service_plans (kể cả features, discount_*, audit fields, first_year_total computed).

Response 404:

json
{ "success": false, "statusCode": 404, "error": { "code": "E-F45-007", "message": "Không tìm thấy gói dịch vụ" } }

9.2.4. PUT /api/v1/admin/service-plans/:id ​

Request: như POST nhưng KHÔNG chấp nhận đổi plan_code (nếu gửi khác giá trị gốc → 400 E-F45-002).

Response 200:

json
{ "success": true, "statusCode": 200, "data": { "id": 1, "plan_code": "PKG_BASIC", "version": 2 } }

Response 400 (đổi mã gói):

json
{ "success": false, "statusCode": 400, "error": { "code": "E-F45-002", "message": "Không được thay đổi mã gói sau khi tạo" } }

9.2.5. PATCH /api/v1/admin/service-plans/:id/status ​

Request:

json
{ "status": "ACTIVE" }

Response 200:

json
{ "success": true, "statusCode": 200, "data": { "id": 1, "status": "ACTIVE" } }

Response 400 (transition không hợp lệ):

json
{ "success": false, "statusCode": 400, "error": { "code": "E-F45-005", "message": "Chuyển trạng thái không hợp lệ" } }

9.2.6. DELETE /api/v1/admin/service-plans/:id ​

Response 200:

json
{ "success": true, "statusCode": 200, "data": { "id": 5, "deleted_at": "2026-07-01T10:30:00Z" } }

Response 409 (đang có DN dùng):

json
{ "success": false, "statusCode": 409, "error": { "code": "E-F45-006", "message": "Không thể xóa gói đang có doanh nghiệp sử dụng" } }

9.2.7. GET /api/v1/service-plans/public ​

Response 200: danh sách gói status = ACTIVE AND is_public = true, chỉ trả trường công khai (plan_code, plan_name, target_audience, phí, quota, features, discount_, sla_support, training_), KHÔNG trả created_by/updated_by/deleted_at.


10. Test Cases cho QA ​

Mã Mô tả Tiền điều kiện Bước thực hiện Kết quả mong đợi
TC-01-01 Tạo gói mới hợp lệ Login PLATFORM_ADMIN POST service-plans với plan_code mới, phí/quota hợp lệ, features hợp schema 201; status = DRAFT, version = 1
TC-01-02 Tạo gói trùng mã Đã có PKG_BASIC POST với plan_code = "PKG_BASIC" 409 E-F45-001
TC-01-03 Tạo gói sai định dạng mã Login ADMIN POST plan_code = "pkg-basic" 400 E-F45-001 (regex fail)
TC-01-04 Tạo gói phí âm Login ADMIN POST setup_fee = -1 400 E-F45-003
TC-01-05 Tạo gói quota âm Login ADMIN POST qr_quota_year = -100 400 E-F45-003
TC-01-06 Tạo gói features sai schema Login ADMIN POST features = "not-an-object" 400 E-F45-004
TC-02-01 Sửa phí gói → tăng version Gói version = 1 PUT maintenance_fee_year mới 200; version = 2
TC-02-02 Sửa chỉ tên gói (không đụng phí/quota) Gói version = 1 PUT plan_name mới 200; version giữ = 1
TC-02-03 Cố đổi plan_code khi sửa Gói tồn tại PUT plan_code khác gốc 400 E-F45-002
TC-02-04 Sửa gói DEPRECATED Gói DEPRECATED PUT cấu hình 200 + W-F45-001; không ảnh hưởng DN đang dùng
TC-03-01 Kích hoạt gói DRAFT→ACTIVE Gói DRAFT PATCH status = ACTIVE 200; status = ACTIVE
TC-03-02 Ngừng bán ACTIVE→DEPRECATED Gói ACTIVE PATCH status = DEPRECATED 200; gói bị loại khỏi chọn F46
TC-03-03 Mở lại DEPRECATED→ACTIVE Gói DEPRECATED PATCH status = ACTIVE 200
TC-03-04 Transition không hợp lệ DRAFT→DEPRECATED Gói DRAFT PATCH status = DEPRECATED 400 E-F45-005
TC-04-01 Xóa gói chưa DN dùng Gói không có subscription DELETE service-plans/:id 200; deleted_at set
TC-04-02 Xóa gói đang có DN dùng Gói có company_subscriptions DELETE service-plans/:id 409 E-F45-006
TC-05-01 Danh sách + filter status Có 4 gói GET ?status=ACTIVE Trả đúng gói ACTIVE
TC-05-02 Tìm kiếm keyword Có PKG_BASIC GET ?keyword=Cơ bản Trả PKG_BASIC
TC-05-03 Pagination > 20 gói GET ?page=2&page_size=20 Đúng trang 2
TC-05-04 Chi tiết gói không tồn tại id không có GET service-plans/9999 404 E-F45-007
TC-06-01 Public trả gói ACTIVE public Có gói ACTIVE is_public GET public Không có DRAFT/DEPRECATED; không có created_by
TC-07-01 Multi-tenant / quyền: DN gọi admin API Login ENTERPRISE_ADMIN GET /admin/service-plans 403 (AdminGuard)
TC-08-01 Seed 4 gói đúng số DB sau migration SELECT * FROM service_plans 4 gói khớp Sale_Pricing (đối chiếu Section 8.4)
TC-09-01 Performance list 100 gói GET service-plans Response ≤ 500ms
TC-09-02 Performance chi tiết 1 gói GET service-plans/:id Response ≤ 500ms; page load ≤ 2s

11. Acceptance Criteria ​

Mã Tiêu chí chấp nhận
AC-F45-01 PLATFORM_ADMIN tạo được gói mới với đầy đủ phí/quota/features; gói mới ở status = DRAFT, version = 1
AC-F45-02 Hệ thống chặn plan_code trùng và sai định dạng UPPER_SNAKE (E-F45-001)
AC-F45-03 plan_code không thể thay đổi sau khi tạo (E-F45-002)
AC-F45-04 Sửa bất kỳ trường phí/quota nào làm tăng version; sửa trường phi-phí/quota không tăng version
AC-F45-05 Đổi trạng thái chỉ theo transition hợp lệ (DRAFT→ACTIVE, ACTIVE⇄DEPRECATED); transition khác bị chặn (E-F45-005)
AC-F45-06 Gói DEPRECATED không xuất hiện trong danh sách chọn khi kích hoạt mới ở F46; DN đang dùng giữ plan_snapshot
AC-F45-07 Không thể xóa gói đang có DN sử dụng (E-F45-006); xóa là soft delete (deleted_at)
AC-F45-08 Mọi thao tác ghi (create/update/status/delete) ghi updated_by + timestamp (audit)
AC-F45-09 Endpoint public chỉ trả gói ACTIVE + is_public, ẩn trường nội bộ
AC-F45-10 4 gói canonical được seed đúng 100% số liệu Sale_Pricing.md (đối chiếu tổng phí năm đầu)
AC-F45-11 Chỉ role ROLE.ADMIN (PLATFORM_ADMIN) truy cập được endpoint /api/v1/admin/service-plans/* (AdminGuard)
AC-F45-12 Tất cả tiền hiển thị VND, chưa gồm VAT; chiết khấu 2y=10%/3y=20% mặc định, cấu hình được
AC-F45-13 Danh sách gói có pagination + filter status + search keyword (debounce 300ms)
AC-F45-14 Page load ≤ 2s; API read ≤ 500ms

12. Chuyển đổi trạng thái (State Transitions) ​

SERVICE_PLAN_STATUS state machine:

                    ┌─────────┐
        (tạo mới)   │  DRAFT  │
        ──────────► │         │
                    └────┬────┘
                         │ Kích hoạt (UC-F45-03)
                         ▼
                    ┌─────────┐   Ngừng bán    ┌──────────────┐
                    │ ACTIVE  │ ─────────────► │ DEPRECATED   │
                    │         │ ◄───────────── │              │
                    └─────────┘    Mở lại      └──────────────┘
Từ trạng thái Sự kiện Sang trạng thái Điều kiện Actor
(none) Tạo gói DRAFT Validate hợp lệ PLATFORM_ADMIN
DRAFT Kích hoạt ACTIVE Cấu hình đầy đủ PLATFORM_ADMIN
ACTIVE Ngừng bán DEPRECATED — PLATFORM_ADMIN
DEPRECATED Mở lại ACTIVE — PLATFORM_ADMIN

Transition KHÔNG hợp lệ (chặn, E-F45-005): DRAFT→DEPRECATED, ACTIVE→DRAFT, DEPRECATED→DRAFT, và mọi self-transition.

Xóa mềm (deleted_at) áp dụng cho DRAFT và DEPRECATED khi chưa DN nào dùng; không phải một status mà là cờ soft delete độc lập.


13. Notification List ​

Mã Sự kiện kích hoạt Người nhận Kênh Template message
N-F45-01 Tạo gói mới PLATFORM_ADMIN (người tạo) In-app "Đã tạo gói dịch vụ [plan_name] ([plan_code]) ở trạng thái Nháp."
N-F45-02 Kích hoạt gói (→ ACTIVE) PLATFORM_ADMIN In-app "Gói [plan_name] đã được kích hoạt và sẵn sàng gán cho doanh nghiệp."
N-F45-03 Ngừng bán gói (→ DEPRECATED) PLATFORM_ADMIN In-app "Gói [plan_name] đã ngừng bán. Doanh nghiệp đang dùng vẫn giữ cấu hình đã chốt."
N-F45-04 Sửa phí/quota (version++) PLATFORM_ADMIN In-app "Cấu hình gói [plan_name] đã cập nhật sang phiên bản v[version]."

Module vận hành thương mại nội bộ: notification chỉ gửi in-app cho nhóm PLATFORM_ADMIN. Không gửi cho DN (DN chỉ xem bảng giá public).


14. Error / Warning Message Handling ​

14.1. Error (chặn hành động) ​

Mã Điều kiện kích hoạt Message (tiếng Việt) HTTP Xử lý hệ thống
E-F45-001 plan_code trùng hoặc sai định dạng UPPER_SNAKE "Mã gói đã tồn tại hoặc không hợp lệ (viết HOA, dùng chữ/số/gạch dưới)." 409 / 400 Chặn tạo, giữ form
E-F45-002 Cố đổi plan_code khi sửa "Không được thay đổi mã gói sau khi tạo." 400 Chặn update
E-F45-003 Phí hoặc quota < 0 "Phí và quota phải lớn hơn hoặc bằng 0." 400 Chặn lưu
E-F45-004 features không đúng JSON schema "Cấu trúc ma trận tính năng không hợp lệ." 400 Chặn lưu
E-F45-005 Transition trạng thái không hợp lệ "Chuyển trạng thái không hợp lệ." 400 Chặn đổi trạng thái
E-F45-006 Xóa gói đang có DN dùng "Không thể xóa gói đang có doanh nghiệp sử dụng." 409 Chặn xóa
E-F45-007 Không tìm thấy gói theo id "Không tìm thấy gói dịch vụ." 404 Trả lỗi
E-F45-008 Role không phải PLATFORM_ADMIN gọi endpoint admin "Bạn không có quyền thực hiện thao tác này." 403 AdminGuard chặn

14.2. Warning (cho phép tiếp tục) ​

Mã Điều kiện Message Xử lý
W-F45-001 Sửa cấu hình gói DEPRECATED "Gói đang ngừng bán — thay đổi không ảnh hưởng doanh nghiệp đang dùng (giữ plan_snapshot)." Cho phép lưu, hiển thị cảnh báo
W-F45-002 Ngừng bán gói còn DN đang dùng "Gói còn [N] doanh nghiệp đang sử dụng — họ vẫn giữ cấu hình đã chốt." Cho phép, ghi log

15. Ma trận truy xuất (Traceability Matrix) ​

Use Case Screen Acceptance Criteria Test Case Error / Warning Notification
UC-F45-01 SCR-F45-02 AC-F45-01, AC-F45-02, AC-F45-10, AC-F45-12 TC-01-01..06, TC-08-01 E-F45-001/003/004 N-F45-01
UC-F45-02 SCR-F45-02 AC-F45-03, AC-F45-04, AC-F45-08 TC-02-01..04 E-F45-002/003/004; W-F45-001 N-F45-04
UC-F45-03 SCR-F45-01, SCR-F45-03 AC-F45-05, AC-F45-06, AC-F45-08 TC-03-01..04 E-F45-005; W-F45-002 N-F45-02, N-F45-03
UC-F45-04 SCR-F45-01, SCR-F45-03 AC-F45-07, AC-F45-09, AC-F45-11, AC-F45-13, AC-F45-14 TC-04-01..02, TC-05-01..04, TC-06-01, TC-07-01, TC-09-01..02 E-F45-006/007/008 —

16. Quy tắc nghiệp vụ (Business Rules) ​

Mã Quy tắc Giải thích / Cơ sở
BR-F45-01 plan_code UNIQUE (partial WHERE deleted_at IS NULL), regex ^[A-Z][A-Z0-9_]{2,29}$, immutable sau khi tạo Đảm bảo tính ổn định định danh gói cho F46 snapshot; tránh phá vỡ tham chiếu
BR-F45-02 Chỉ PLATFORM_ADMIN (ROLE.ADMIN) được CRUD gói qua /api/v1/admin/... + AdminGuard Phân quyền vận hành nền tảng; Sale thao tác qua tài khoản admin
BR-F45-03 Mọi phí ≥ 0 và mọi quota ≥ 0 (CHECK constraint) Đảm bảo toàn vẹn dữ liệu tài chính
BR-F45-04 features phải tuân JSON schema ma trận tính năng (Section 5.2 Card 5) Chuẩn hóa hiển thị ma trận tính năng, tránh dữ liệu rác
BR-F45-05 Gói DEPRECATED không được chọn khi kích hoạt mới ở F46; DN đang dùng giữ plan_snapshot (bất biến) Bảo vệ hợp đồng đã ký; căn cứ Bộ luật Dân sự 2015
BR-F45-06 Chiết khấu mặc định 2y = 10%, 3y = 20% (áp dụng trước VAT); có thể cấu hình theo gói Theo chính sách Sale_Pricing.md mục "Chính sách khác"
BR-F45-07 Sửa bất kỳ trường phí/quota (setup_fee, standardization_fee, maintenance_fee_year, extra_qr_fee, extra_product_fee, qr_quota_year, max_products, max_users, storage_gb) → version++ và ghi audit updated_by Truy vết thay đổi giá/quota; đảm bảo minh bạch
BR-F45-08 Soft delete bằng deleted_at; unique index partial để tái dùng mã sau xóa Bảo toàn lịch sử; chuẩn repo
BR-F45-09 Seed đúng 4 gói canonical (PKG_BASIC/STANDARD/LINKED/ENTERPRISE) khớp 100% Sale_Pricing.md Nguồn canonical cho billing/kích hoạt
BR-F45-10 Mọi giá là VND (NUMERIC), chưa gồm VAT; hiển thị dd/MM/yyyy cho ngày, API/DB ISO 8601 Chuẩn hiển thị & convention repo

17. Ràng buộc & Giả định (Constraints / Assumptions) ​

17.1. Ràng buộc ​

  1. Module vận hành thương mại: TXNG-F45 KHÔNG phát sinh Critical Tracking Event (CTE) / Key Data Element (KDE) truy xuất nguồn gốc. Không áp dụng TCVN 12850:2019 / FDA FSMA 204 / GS1 EPCIS cho chức năng này.
  2. Phân quyền cứng: Toàn bộ thao tác ghi chỉ dành cho PLATFORM_ADMIN (impl ROLE.ADMIN = numeric 1), chặn qua AdminGuard, prefix /api/v1/admin/.... ENTERPRISE_ADMIN chỉ xem bảng giá public.
  3. Tính bất biến hợp đồng: Thay đổi cấu hình gói (kể cả giá) KHÔNG tác động ngược tới DN đang dùng — F46 đã chốt plan_snapshot tại thời điểm kích hoạt.
  4. Không multi-tenant scope trong F45: service_plans là bảng cấu hình nền tảng dùng chung, KHÔNG có company_id. Ràng buộc multi-tenant chỉ áp dụng ở F46 (bảng company_subscriptions lọc theo company_id).
  5. Tiền tệ: v1.0 chỉ hỗ trợ VND; currency để mở rộng tương lai. Giá chưa gồm VAT.
  6. Chu kỳ thanh toán: v1.0 chỉ ANNUAL.
  7. SLA hiệu năng: Page load ≤ 2 giây; API read (list/detail) ≤ 500ms; API write ≤ 1 giây.

17.2. Giả định ​

  1. Bảng users và companies (F01) đã tồn tại; FK created_by/updated_by → users(id).
  2. Migration seed 4 gói chạy 1 lần khi khởi tạo hệ thống (idempotent qua ON CONFLICT DO NOTHING + unique index).
  3. F46 sẽ đọc service_plans khi kích hoạt; ràng buộc "không xóa gói đang dùng" dựa trên tồn tại bản ghi company_subscriptions.plan_id.
  4. Số liệu Sale_Pricing.md (19/05/2026, hiệu lực 01/06/2026) là nguồn chuẩn; khi bảng giá đổi, admin cập nhật qua UI (không cần migration mới).
  5. Việc tăng version chỉ là số nguyên đơn giản (không tạo bản ghi lịch sử version riêng trong v1.0); lịch sử chi tiết trước/sau thuộc phạm vi audit log chung.

18. Phụ lục (Appendix) ​

18.1. Bảng phí 4 gói (đối chiếu Sale_Pricing.md) ​

Hạng mục PKG_BASIC PKG_STANDARD PKG_LINKED PKG_ENTERPRISE
Tên gói Gói Cơ bản Gói Chuẩn hóa Gói Liên kết Gói Quản trị
Phí khởi tạo (VND) 2.000.000 3.000.000 6.000.000 9.000.000
Phí chuẩn hóa & liên kết (VND) 3.000.000 5.000.000 8.000.000 15.000.000
Phí duy trì/năm (VND) 4.500.000 9.500.000 25.500.000 85.000.000
Tổng phí năm đầu (VND) 9.500.000 17.500.000 39.500.000 109.000.000
Từ năm 2 (VND) 4.500.000 9.500.000 25.500.000 85.000.000
Sản lượng QR/năm 5.000 20.000 100.000 500.000
Số SP tối đa 10 50 150 500
Số user tối đa 2 5 20 50
Dung lượng lưu trữ (GB) 1 3 10 50
Phí QR tăng thêm (VND) 150 120 100 80
Phí SP tăng thêm (VND) 490.000 490.000 490.000 490.000
Đào tạo Online Online 01 lần Onsite 03 lần Onsite
Hỗ trợ go-live (ngày) 7 15 30 60
SLA hỗ trợ Trong giờ hành chính 24/7 Ưu tiên 24/7 Ưu tiên 24/7

Chiết khấu duy trì: 2 năm giảm 10%, 3 năm giảm 20% (trước VAT). Báo giá chưa gồm VAT, chưa gồm chi phí in tem vật lý và phí bên thứ ba.

18.2. Ma trận so sánh tính năng 4 gói (Sale_Pricing Phụ lục) ​

Quy ước: ✔ = có · — = không · Tùy chọn = mua thêm · Tùy chỉnh = mức cao hơn / cấu hình riêng.

Nhóm tính năng feature key Cơ bản Chuẩn hóa Liên kết Quản trị
Hồ sơ DN/chủ thể company_profile ✔ ✔ ✔ ✔
Chuẩn hóa danh mục sản phẩm product_catalog_standardization Tối đa 10 SKU Tối đa 50 SKU Tối đa 150 SKU Tối đa 500 SKU
Chuẩn hóa nguyên liệu đầu vào material_input_standardization ✔ ✔ ✔ ✔
Chuẩn hóa chứng nhận/chứng chỉ certification_standardization ✔ ✔ ✔ ✔
Chuẩn hóa vùng trồng/vùng nuôi growing_area_standardization ✔ ✔ ✔ ✔
Chuẩn hóa khách hàng/đại lý customer_agent_standardization ✔ ✔ ✔ ✔
Chuẩn hóa danh mục nhân viên staff_standardization ✔ ✔ ✔ ✔
Chuẩn hóa quy trình sản xuất production_process_standardization — ✔ ✔ ✔
Tư vấn cấu trúc mã SP/lô product_lot_code_consulting — ✔ ✔ ✔
QR truy xuất nguồn gốc qr_traceability ✔ ✔ ✔ ✔
Trang truy xuất công khai public_trace_page ✔ (STANDARD) ✔ (STANDARD) ✔ (STANDARD) Tùy chỉnh (CUSTOM)
Thống kê lượt quét QR qr_scan_statistics ✔ ✔ Tùy chỉnh Tùy chỉnh
Liên kết chủ thể chuỗi cung ứng supply_chain_linking — — ✔ ✔
Quản lý thu hồi sản phẩm product_recall_management — — ✔ ✔
Dashboard vận hành operation_dashboard ✔ ✔ Tùy chỉnh Tùy chỉnh
Phân quyền người dùng (map max_users) 2 user 5 user 20 user 50 user
Dung lượng lưu trữ (map storage_gb) 1GB 3GB 10GB 50GB
Nhật ký thao tác activity_log — — ✔ ✔
API tích hợp api_integration — — 1 API cơ bản (BASIC) Tùy phạm vi (CUSTOM)
Blockchain blockchain — Tùy chọn Tùy chọn Tùy chọn
Đào tạo tiêu chuẩn (map training_type) Online Online 1 buổi Onsite 3 buổi Onsite
Hỗ trợ go-live (map go_live_support_days) 7 ngày 15 ngày 30 ngày 60 ngày
SLA hỗ trợ (map sla_support) Trong giờ hành chính 24/7 Ưu tiên 24/7 Ưu tiên 24/7

18.3. Công thức tính phí liên quan ​


Hết tài liệu SRS TXNG-F45.