36 KiB
SRS — US-002, US-003 — Xem giỏ hàng nhóm theo seller · Sửa số lượng / xoá sản phẩm trong giỏ
| Version | 1.0 |
| Date | 2026-09-08 |
| Author | BA (qua skill ba-3-specification) |
| Status | 🟡 Draft |
| Approved by | PO: — · Tech Lead: — · QA: — · Designer: — (dự án không có Designer riêng — PO ký thay, DEC-01) |
| Source | 02-analysis/BACKLOG_CartCheckout_v1.0.md (US-002, US-003) · 02-analysis/BR_CartCheckout_v1.0.md (§3 vòng đời CART, §4 ERD) · 02-analysis/RBAC_CartCheckout_v1.0.md (§1, §2, §6) · 02-analysis/IMPACT_CartCheckout_v1.0.md · 00-index/UICONV_e-commerce_v1.0.md v1.1 · e-commerce/docs/sections/07-giao-dien.md §7.1.1 SCR-04 (prototype tham chiếu dạng văn bản, chế độ 🎨, DEC-01) · e-commerce/docs/sections/04-api-design.md §4.1.1, §4.1.5, §4.1.13, §4.2 · e-commerce/docs/sections/05-thiet-ke-du-lieu.md §5.2.3 (tham chiếu entity cart/cart_item, không chép schema vật lý) |
| Scope | US-002 (Xem giỏ hàng nhóm theo seller), US-003 (Sửa số lượng / xoá sản phẩm trong giỏ) — cùng màn hình SCR-04 Giỏ hàng |
| Profile | screen · greenfield · standard (00-index/PROFILE_e-commerce.md) |
| Biến thể PART 2 | srs-part2/screen.md (2.A Màn hình) + 2.B API (US-002/US-003 gọi trực tiếp API giỏ hàng để hiển thị/cập nhật) |
| Ngôn ngữ hiển thị | VI + EN (đầy đủ ở §4.2) · ZH/KO/JA để trống — chờ OQ-023 (cần biên dịch chuyên nghiệp, không dịch máy — ghi chú người duyệt) |
Change Log
| Version | Date | Người sửa | Thay đổi | CR |
|---|---|---|---|---|
| 1.0 | 2026-09-08 | BA (qua skill ba-3-specification) | Bản đầu — SRS chung US-002 + US-003, PART 2.A (màn hình SCR-04) + 2.B (API), 6 mã lỗi mới (E-CART-0002…0006), 6 OQ mới (OQ-028…OQ-033) |
— |
⚠️ Ngoại lệ gate — tài liệu này được viết trong khi Gate G2 (Solution sign-off) của module Giỏ hàng & Checkout chưa được PO + Tech Lech ký chính thức (xem
DEC-02,DEC-05,00-index/DECISION_e-commerce.md). Ngoại lệ đã được người điều phối xác nhận rõ ràng — xemDEC-06. NếuBACKLOG_CartCheckout/BR_CartCheckoutđổi khi G2 ký thật, phần bị ảnh hưởng nhiều nhất ở đây là §1.3 (BR áp dụng — hiện chưa cóBR-CART-nnnào áp dụng trực tiếp) và §1.4 (vòng đờiCART).
Quy ước đọc tài liệu này (quy tắc W11). Bảng thành phần ở §2.A.3.1 quyết định một phần tử có tồn tại hay không và nó hành xử thế nào.
WF_US002-003_v1.0.md+.html(và prototype SAD §7.1.1 nó tham chiếu) quyết định nó nằm ở đâu, to bằng nào, trông ra sao ở từng trạng thái.UICONV_e-commerce_v1.0.mdquyết định quy ước chung — tài liệu này chỉ ghi phần khác. Khi mâu thuẫn: bảng thắng về sự tồn tại và hành vi, WF thắng về bố cục — mâu thuẫn đó có dòng ởWF§5.
PHẦN 1 — NGHIỆP VỤ
1.1 Tóm tắt cho người quyết định
| US này giải quyết | RQ-001: Giỏ hàng đa seller, một lần checkout. US-002 cho khách nhìn thấy rõ mình đang mua sản phẩm gì của những seller nào và tổng tiền từng seller/toàn giỏ trước khi quyết định đặt hàng; US-003 cho khách tự sửa số lượng hoặc bỏ bớt sản phẩm ngay tại màn hình này mà không phải rời trang |
| Người dùng được gì | Nhìn một màn hình duy nhất biết chính xác đang mua của ai, số tiền mỗi seller và tổng cộng; tự điều chỉnh giỏ hàng (số lượng, xoá dòng) và thấy tổng tiền cập nhật ngay, không cần tải lại trang |
| Khác hiện tại chỗ nào | N/A — dự án greenfield (xem PROFILE_e-commerce.md §Hệ quả đã áp dụng), không có màn hình giỏ hàng cũ để so sánh; đây là màn hình mới hoàn toàn của module Giỏ hàng & Checkout |
1.2 Vai trò và quyền
| Vai trò | Được làm gì trong US này | Không được làm gì | RBAC |
|---|---|---|---|
| ROLE-01 Guest | Xem giỏ hàng của chính mình (theo X-Guest-Session-Id); sửa số lượng / xoá CartItem của chính mình |
Xem/sửa giỏ hàng của người khác; không có sổ địa chỉ (không liên quan ở US này) | RBAC_CartCheckout_v1.0.md §2 |
| ROLE-02 Customer | Xem giỏ hàng của chính mình (theo customer_id); sửa số lượng / xoá CartItem của chính mình |
Xem/sửa giỏ hàng của người khác | RBAC_CartCheckout_v1.0.md §2 |
1.3 Business rule áp dụng
| BR | Phát biểu ngắn | Áp dụng ở màn hình/field nào | Khi vi phạm |
|---|---|---|---|
| (không có) | Không có BR-CART-nn nào áp dụng trực tiếp cho US-002/US-003 — đúng theo BACKLOG_CartCheckout_v1.0.md (US-002: "BR áp dụng: chưa có — mục hiển thị thuần tuý"; US-003: "BR áp dụng: chưa có — ràng buộc số lượng tối thiểu/tối đa mỗi dòng chưa được chốt") |
— | — |
BR §3 CART |
Chỉ được sửa số lượng / xoá CartItem khi Cart.status = active (nguồn: BR_CartCheckout_v1.0.md §3, bảng "Chuyển trạng thái KHÔNG được phép") |
Toàn màn SCR-04 |
Chặn thao tác — xem §1.4 |
🔴 Không có BR nào định nghĩa giới hạn trên của số lượng (kiểm tra tồn kho) hay hành vi khi số lượng về 0 — đây là khoảng trống thật của BACKLOG/BR ở GĐ2, không phải BA quên chép. Xem OQ-028, OQ-030.
1.4 Vòng đời trạng thái (tham chiếu — không định nghĩa lại)
Vòng đời đầy đủ của CART ở BR_CartCheckout_v1.0.md §3. Ở đây chỉ nêu phần US-002/US-003 chạm tới.
| Nguồn | Sự kiện | Điều kiện | Đích | Ai được làm | BR |
|---|---|---|---|---|---|
| Active | Sửa số lượng CartItem |
Cart.status = active |
Active (không đổi) | Guest/Customer chủ giỏ | BR §3 CART |
| Active | Xoá CartItem (kể cả dòng cuối cùng của giỏ) |
Cart.status = active |
Active (không đổi — 🔴 giỏ hàng không tự chuyển abandoned khi rỗng, chỉ chuyển khi hết hạn phiên theo BR §3) |
Guest/Customer chủ giỏ | BR §3 CART |
Chuyển trạng thái KHÔNG được phép ở US-002/US-003: không có thao tác nào trong hai US này làm Cart.status chuyển sang converted/abandoned — hai chuyển đổi đó thuộc US-004 (checkout) và cơ chế hết hạn phiên tự động (ngoài phạm vi SRS này).
1.5 Ngoài phạm vi (quy tắc W12)
| # | Không làm gì | Vì sao | Xử lý ở đâu/khi nào |
|---|---|---|---|
| 1 | Kiểm tra tồn kho thời điểm thực khi xem/sửa giỏ hàng | BACKLOG_CartCheckout_v1.0.md US-003 khoanh phạm vi: "cảnh báo hết hàng theo thời gian thực thuộc US-004 tại thời điểm checkout, không phải tại thời điểm xem/sửa giỏ — trừ khi PO quyết khác" |
US-004 (checkout); sẽ được cập nhật nếu PO trả lời OQ-030 theo hướng khác |
| 2 | Áp dụng khuyến mãi/mã giảm giá/điểm thưởng | Điểm tích hợp với module Khuyến mãi & Loyalty (DEC-03) |
US-004/US-005 (module khác gọi vào) |
| 3 | Chọn/bỏ chọn từng dòng hoặc từng nhóm seller để checkout một phần giỏ hàng (checkbox) | SAD §7.1.1 SCR-04 mô tả thành phần này nhưng không có US nào trong BACKLOG xác nhận tính năng "checkout một phần giỏ hàng" |
Chưa xác định — xem OQ-031, WF_US002-003 §5 dòng #1. Không tự thêm vào SRS này |
| 4 | Hiển thị badge "Sản phẩm đã hết hàng" / "Giá đã thay đổi" tại màn Giỏ hàng | SAD §7.1.1 SCR-04 mô tả nhưng phụ thuộc mục 1, 2 ở trên (nguồn dữ liệu tồn kho/giá real-time chưa xác nhận thuộc scope US-002/US-003) |
Xem OQ-029, OQ-030, WF_US002-003 §5 dòng #2, #3 |
| 5 | Tính phí vận chuyển | Chỉ có ở bước checkout, cần địa chỉ trước | US-005 |
| 6 | Thanh toán | Ngoài phạm vi màn hình Giỏ hàng | US-007/US-008 |
| 7 | Điều hướng từ dòng CartItem (ảnh/tên) về trang chi tiết sản phẩm |
Không được BACKLOG/BR xác nhận là hành vi bắt buộc của US-002/US-003 |
Bổ sung qua CR sau nếu PO yêu cầu |
PHẦN 2 — ĐẶC TẢ SẢN PHẨM
2.A — Màn hình (srs-part2/screen.md)
2.A.1 Danh sách màn hình
| ID | Tên màn hình | Loại | Đường dẫn | Vai trò truy cập | Vào từ đâu | Ra đi đâu |
|---|---|---|---|---|---|---|
| SCR-04 | Giỏ hàng | Chi tiết (không phân trang) | /cart (quy ước kỹ thuật, Dev FE xác nhận — không chặn G3) |
ROLE-01 Guest, ROLE-02 Customer | Icon giỏ hàng ở header toàn site (SCR-01, theo SAD §7.1.1) |
SCR-05 Checkout (US-004, ngoài phạm vi SRS này) · SCR-01 (khi giỏ rỗng, nút "Tiếp tục mua sắm") |
2.A.2 Sơ đồ điều hướng
flowchart LR
SCR01(["SCR-01 Trang chủ — icon giỏ hàng"]) -->|"bấm icon giỏ hàng"| SCR04["SCR-04 Giỏ hàng"]
SCR04 -->|"'Tiếp tục mua sắm' (giỏ rỗng, C10)"| SCR01
SCR04 -->|"'Tiến hành Checkout' (C11) — US-004, ngoài phạm vi SRS này"| SCR05[["SCR-05 Checkout — ngoài phạm vi"]]
SCR01 -.->|"vào URL /cart trực tiếp"| SCR04
ID node khớp cột ID của bảng §2.A.1.
| Câu hỏi | SCR-04 |
|---|---|
| Hai đường quay lại (nút back + breadcrumb)? Quay lại có giữ trạng thái danh sách? | Không áp dụng breadcrumb (SCR-04 không lồng trong danh mục); quay lại dùng logo/menu header (theo UICONV §2). Không có "trạng thái danh sách" (giỏ hàng không phân trang/lọc) để giữ |
Vào bằng URL trực tiếp /cart khi chưa có CartItem nào / chưa có session Guest |
Hệ thống tự tạo Cart mới rỗng (Guest) hoặc lấy Cart rỗng của customer_id (Customer) — hiển thị trạng thái rỗng §2.A.3.6, không phải lỗi 404 |
| Rời màn hình khi đang sửa dở | N/A — không có "form đang dở": mỗi lần sửa số lượng/xoá gửi ngay lập tức (không có nút Lưu tổng, xem §2.A.3.5 và ngoại lệ UICONV §12) |
2.A.3 SCR-04 — Giỏ hàng
Bố cục: WF_US002-003_v1.0.md §3.1 · WF_US002-003_v1.0.html#SCR-04 · prototype: 🎨 e-commerce/docs/sections/07-giao-dien.md §7.1.1 SCR-04. Hành vi xem bảng bên dưới, không suy từ hình (W11).
2.A.3.1 Bảng thành phần
| ID | Tên thành phần | Loại | Nhãn (nguyên văn) | Placeholder / Hint | Hành vi & sự kiện | Điều kiện ẩn/khoá | BR |
|---|---|---|---|---|---|---|---|
| C01 | lbl_header_title | Nhãn | "Giỏ hàng của bạn ({n} sản phẩm)" | — | {n} = tổng số lượng (Σ quantity mọi CartItem), tĩnh, cập nhật khi Σ đổi |
— | — |
| C02 | grp_seller_header | Nhãn nhóm | "{Tên gian hàng}" | — | Lặp lại theo từng seller_id có ≥1 CartItem; nguồn tên: seller_id → tên gian hàng (module Seller Management, tham chiếu) |
❌ ẩn nếu nhóm không còn CartItem nào (xem AC-US003-04) |
— |
| C03 | img_cart_item | Ảnh | — | Ảnh dạng ô xám khi lỗi tải | Ảnh đại diện product_variant_id |
— | — |
| C04 | lbl_product_name | Nhãn | Tên sản phẩm | — | Hiển thị tĩnh, không điều hướng (xem §1.5 dòng 7) | — | — |
| C05 | lbl_variant | Nhãn | "{Thuộc tính biến thể}" (VD "Size M, Đỏ") | — | Hiển thị tĩnh, nguồn product_variant_id (module Catalog, tham chiếu, không sở hữu) |
— | — |
| C06 | lbl_unit_price | Nhãn | "{đơn giá} ₫" | — | Hiển thị tĩnh — nguồn giá trị: 🔴 chưa chốt snapshot hay real-time, xem OQ-029 |
— | — |
| F01 | input_quantity | Ô nhập số (bộ đếm +/-) | — | — | Xem bảng field §2.A.3.3 | 🔒 disable trong lúc đang gửi cập nhật (spinner cục bộ, xem §2.A.3.6) | — |
| C07 | btn_delete_item | Nút nguy hiểm | "Xoá" | — | Bấm → mở modal xác nhận (theo UICONV §5 — mặc định áp dụng, không có ngoại lệ ghi ở UICONV §12 cho dòng này) → xác nhận thì gọi DELETE |
🔒 disable trong lúc đang gửi | — |
| C08 | lbl_seller_subtotal | Nhãn | "Tạm tính: {tổng nhóm} ₫" | — | = Σ (quantity × giá hiển thị C06) của các CartItem cùng seller_id; cập nhật ngay khi F01/C07 thành công |
— | — |
| C09 | lbl_cart_total | Nhãn | "Tổng cộng: {tổng giỏ} ₫" | — | = Σ tất cả C08 | — | — |
| C10 | btn_continue_shopping | Nút phụ | "Tiếp tục mua sắm" | — | Chỉ hiện ở trạng thái rỗng → điều hướng SCR-01 |
❌ ẩn khi giỏ có ≥1 CartItem |
— |
| C11 | btn_checkout | Nút chính | "Tiến hành Checkout" | — | Bấm → điều hướng SCR-05 (US-004, ngoài phạm vi SRS này) |
🔒 disable khi giỏ có 0 CartItem. Điều kiện khác (yêu cầu chọn dòng) chưa xác nhận thuộc scope — xem OQ-031, không áp dụng ở SRS này |
— |
| C12 | banner_error_load | Banner | — | — | Hiện khi GET /v1/cart lỗi/timeout, thay chỗ danh sách CartItem |
❌ ẩn khi tải thành công | — |
| C13 | btn_retry_load | Nút phụ | "Thử lại" | — | Bấm → gọi lại GET /v1/cart |
Nằm trong C12 | — |
Cột Điều kiện ẩn/khoá — quy tắc W7 áp dụng đúng ba giá trị ❌ ẩn / 🔒 disable / 👁 read-only, không ghi "tuỳ trường hợp".
2.A.3.2 Bảng thuộc tính hiển thị mỗi dòng CartItem
Không phải danh sách phân trang/sắp xếp (giỏ hàng hiển thị toàn bộ, không giới hạn số dòng mỗi trang) — bảng dưới đây thay cho §2.3.2 chuẩn để ghi nguồn dữ liệu từng thuộc tính hiển thị.
| # | Thuộc tính | Nguồn dữ liệu | Định dạng | Xử lý khi rỗng/thiếu | Ghi chú |
|---|---|---|---|---|---|
| 1 | Ảnh | product_variant_id → ảnh đại diện (module Catalog) |
Ảnh vuông | Ô xám mặc định (theo UICONV) |
— |
| 2 | Tên sản phẩm | product_variant_id → tên (module Catalog) |
Text, không giới hạn dòng xác nhận — 🔴 chưa chốt số dòng tối đa (không blocking G3, mức độ thấp, Dev FE tự quyết theo CSS) | — | — |
| 3 | Biến thể | product_variant_id → thuộc tính |
Text | — nếu sản phẩm không có biến thể |
— |
| 4 | Đơn giá | cart_item.unit_price_snapshot hoặc giá hiện tại real-time — 🔴 OQ-029 |
#,##0 ₫ (UICONV §8) |
N/A | — |
| 5 | Số lượng | cart_item.quantity |
Số nguyên | N/A (luôn ≥1 khi dòng còn tồn tại) | Xem F01 |
Thứ tự hiển thị nhóm seller và CartItem trong mỗi nhóm: 🔴 chưa được BACKLOG/SAD xác nhận. BA đề xuất: theo thời điểm thêm vào giỏ tăng dần (added_at/tương đương cart_item không có cột này trong ERD GĐ2 hiện tại — cần Tech Lech xác nhận có cột thời gian thêm hay dùng thứ tự trả về mặc định của API). Mức độ thấp, không chặn G3 — ghi nhận ở WF_US002-003 §5 dòng #6.
2.A.3.3 Bảng field
| ID | Tên field | Kiểu | Bắt buộc | Độ dài / Khoảng | Default | Nguồn giá trị | Validation | Message khi sai | BR |
|---|---|---|---|---|---|---|---|---|---|
| F01 | Số lượng (quantity) |
Số nguyên (bộ đếm +/- và nhập trực tiếp) | ✅ | Tối thiểu 1 · tối đa 🔴 chưa chốt — xem OQ-030 (BA không tự đặt số, vì đây là ràng buộc tồn kho, quyết định nghiệp vụ) |
Giá trị quantity hiện có của CartItem khi mở màn hình |
cart_item.quantity (giá trị hiện tại) |
Số nguyên ≥ 1 | "Số lượng phải là số nguyên từ 1 trở lên." (E-CART-0002) |
(không có BR — xem §1.3) |
🔴 Hành vi khi giảm số lượng xuống 0 chưa chốt (bấm nút - khi quantity = 1, hoặc nhập trực tiếp 0): tự động mở modal xoá dòng, hay chặn không cho giảm dưới 1? — xem OQ-028. AC liên quan viết ở trạng thái mở, xem AC_US-003_v1.0.md §3 Nhóm 2.
2.A.3.4 Sơ đồ luồng — Sửa số lượng / Xoá CartItem
sequenceDiagram
autonumber
actor U as Customer/Guest
participant FE as Giao diện SCR-04
participant BE as Cart & Order Service
U->>FE: Đổi số lượng (F01) hoặc bấm Xoá (C07, đã xác nhận modal)
FE->>FE: Validate phía giao diện (số nguyên ≥ 1)
FE->>BE: PATCH /v1/cart/items/{cartItemId} hoặc DELETE /v1/cart/items/{cartItemId} (khoá F01/C07 đang xử lý)
alt Thành công
BE-->>FE: 200 OK (dữ liệu giỏ hàng cập nhật)
FE-->>U: Cập nhật C08/C09, mở lại F01/C07
else CartItem không còn tồn tại trong giỏ (404/409 — bị xoá ở nơi khác)
BE--xFE: 404 ERR_NOT_FOUND / 409 ERR_CONFLICT
FE-->>U: Banner "Sản phẩm này đã được xoá khỏi giỏ hàng trước đó." (E-CART-0003), ẩn dòng, cập nhật C08/C09
else Không đủ quyền sở hữu (403)
BE--xFE: 403 ERR_FORBIDDEN_OWNERSHIP
FE-->>U: Chuyển trang "Không có quyền" (E-CART-0005)
else Timeout / lỗi hệ thống (5xx)
BE--xFE: timeout / 5xx
FE-->>U: Banner lỗi tại dòng CartItem "Không thể cập nhật giỏ hàng, thử lại." (E-CART-0006), GIỮ NGUYÊN giá trị trước đó, mở lại F01/C07
end
🔴 Bắt buộc vẽ cả nhánh lỗi (alt/else) — đã vẽ đủ 4 nhánh (thành công, xung đột, phân quyền, hệ thống).
Bảng đi kèm:
| Bước | Mô tả | Timeout | Thất bại thì sao | Mã lỗi | AC |
|---|---|---|---|---|---|
| 3 | Gửi PATCH/DELETE cart item |
🔴 chưa chốt — xem NFR_US002-003_v1.0.md §1, OQ-033 |
Giữ giá trị/dòng cũ, mở lại nút | E-CART-0002/0003/0005/0006 (tuỳ nhánh) |
AC-US003-07…13 |
2.A.3.5 Hành động trên màn hình
| Hành động | Điều kiện được phép | Xác nhận trước khi làm | Kết quả thành công | Kết quả thất bại | Vai trò | AC |
|---|---|---|---|---|---|---|
| Xem giỏ hàng (tải trang) | — | Không | Hiển thị đủ nhóm/dòng/C08/C09 | Banner lỗi (C12) + Thử lại (C13) | Guest, Customer | AC-US002-01…04 |
| Sửa số lượng (F01) | Cart.status = active; giá trị mới là số nguyên ≥ 1 |
Không | Cập nhật F01, C08, C09 ngay | Hiện lỗi theo bảng mã lỗi §4.1, giữ giá trị trước đó | Guest, Customer | AC-US003-01, 02, 07…13 |
Xoá CartItem (C07) |
Cart.status = active |
✅ Modal xác nhận (UICONV §5) |
Xoá dòng, cập nhật C08/C09; nhóm rỗng thì ẩn nhóm (C02); giỏ rỗng thì chuyển trạng thái rỗng | Hiện lỗi theo bảng mã lỗi §4.1, giữ nguyên dòng | Guest, Customer | AC-US003-03…06, 10, 11 |
| Bấm "Tiến hành Checkout" (C11) | Giỏ có ≥1 CartItem |
Không | Điều hướng SCR-05 (ngoài phạm vi SRS này) |
N/A | Guest, Customer | Ngoài phạm vi |
🔴 Thất bại phải giữ nguyên dữ liệu người dùng đã nhập — áp dụng cho F01 (giữ số lượng cũ khi lỗi) và cho C07 (dòng không biến mất khi DELETE lỗi).
2.A.3.6 Trạng thái rỗng, đang tải, lỗi
| Trạng thái | Hiển thị gì | Text nguyên văn | Nút hành động |
|---|---|---|---|
| Đang tải lần đầu | Skeleton theo UICONV §6 |
— | — |
| Chưa có dữ liệu nào (giỏ trống) | Minh hoạ + câu dẫn + nút chính. 🔴 Ngoại lệ so với mẫu chung UICONV §6 ("Chưa có <đối tượng> nào.") — dùng nguyên văn theo SAD, đã ghi ở UICONV §12 |
"Giỏ hàng trống" (khoá cart.empty.title) |
C10 "Tiếp tục mua sắm" |
| Bộ lọc không khớp | N/A — giỏ hàng không có bộ lọc | — | — |
Lỗi tải dữ liệu (GET /v1/cart) |
Banner C12 thay chỗ danh sách | "Không tải được dữ liệu, thử lại." (error.load, theo UICONV §6 chung) |
C13 "Thử lại" |
| Đang xử lý sửa/xoá 1 dòng | Spinner cục bộ tại dòng đó (F01/C07 disable), theo UICONV §6 "đang tải cục bộ" |
— | — |
2.B — API tiêu thụ (vì SCR-04 gọi trực tiếp API giỏ hàng)
Chi tiết đầy đủ ở API_US002-003_v1.0.md. Ở đây chỉ mô tả góc nhìn người tiêu thụ (FE), không lặp lại hợp đồng.
| Endpoint | Dùng để | Gọi khi nào | Nguồn contract |
|---|---|---|---|
GET /v1/cart |
Tải dữ liệu SCR-04 khi vào màn hình | Mở màn hình; bấm "Thử lại" (C13) | ✅ method/path — SAD §4.1.5 · ⚠️ schema response — BA đề xuất |
PATCH /v1/cart/items/{cartItemId} |
Cập nhật F01 | Mỗi lần đổi số lượng (bấm +/-, hoặc rời field sau khi gõ tay) | ✅ method/path — SAD §4.1.5 · ⚠️ schema request/response — BA đề xuất |
DELETE /v1/cart/items/{cartItemId} |
Xoá C07 (sau khi xác nhận modal) | Người dùng xác nhận trong modal | ✅ method/path — SAD §4.1.5 · ⚠️ schema response — BA đề xuất |
Guest dùng header X-Guest-Session-Id thay JWT cho cả ba endpoint (theo SAD §4.1.1, §4.1.5).
PHẦN 3 — TIÊU CHÍ NGHIỆM THU
Chi tiết đầy đủ, tách riêng theo US: AC_US-002_v1.0.md và AC_US-003_v1.0.md.
| AC | Nhóm | Tóm tắt | Field/Thành phần | BR | Test case (QA điền) |
|---|---|---|---|---|---|
| AC-US002-01 | Thành công | Xem giỏ hàng nhiều seller | C01–C11 | — | |
| AC-US002-02 | Thành công | Xem giỏ hàng rỗng | C10 | BR §3 | |
| AC-US002-03 | Lỗi hệ thống | Lỗi tải giỏ hàng | C12, C13 | — | |
| AC-US002-04 | Lỗi hệ thống | Phân biệt "chưa có dữ liệu" ↔ "không tải được" | — | — | |
| AC-US002-05 | Phân quyền | Guest chỉ thấy giỏ của session mình | — | RBAC §2 | |
| AC-US002-06 | Phân quyền | Customer hết phiên bị chuyển đăng nhập | — | — | |
| AC-US003-01…06 | Thành công | Tăng/giảm/xoá CartItem, xoá nhóm/giỏ, huỷ modal |
F01, C07, C08, C09 | BR §3 | |
| AC-US003-07, 08 | Validation | Số lượng không hợp lệ; giảm về 0 (🔴 mở, OQ-028) |
F01 | — | |
| AC-US003-09…11 | Lỗi hệ thống | 5xx cập nhật, timeout xoá, item bị xoá đồng thời | F01, C07 | — | |
| AC-US003-12, 13 | Phân quyền | Gọi API không đúng chủ sở hữu (Customer/Guest — Guest 🔴 mở, OQ-032) |
— | RBAC §6 |
Mỗi US phải có đủ bốn nhóm — đã đủ cho cả hai US, trừ hai AC còn mở chờ trả lời OQ (ghi rõ thay vì bịa).
PHẦN 4 — MÃ LỖI VÀ TEXT HIỂN THỊ
4.1 Mã lỗi
| Mã | Khi nào xảy ra | Thông điệp hiển thị (nguyên văn) | Hiển thị ở đâu | Người dùng làm gì tiếp | BR/AC |
|---|---|---|---|---|---|
E-CART-0002 |
Nhập số lượng không phải số nguyên ≥ 1 | "Số lượng phải là số nguyên từ 1 trở lên." | Inline dưới F01 | Sửa lại giá trị | AC-US003-07 |
E-CART-0003 |
CartItem không còn thuộc giỏ hàng (đã bị xoá ở nơi khác) khi cố sửa/xoá |
"Sản phẩm này đã được xoá khỏi giỏ hàng trước đó." | Banner tại dòng (trước khi dòng biến mất) | Không cần làm gì — hệ thống tự cập nhật lại danh sách | AC-US003-11 |
E-CART-0004 |
GET /v1/cart lỗi/timeout |
"Không tải được dữ liệu, thử lại." | Banner C12 thay chỗ danh sách | Bấm "Thử lại" (C13) | AC-US002-03 |
E-CART-0005 |
Gọi PATCH/DELETE cart item không thuộc quyền sở hữu (403) |
"Bạn không có quyền truy cập nội dung này." | Trang trạng thái | Về trang chủ | AC-US003-12, 13 |
E-CART-0006 |
Lỗi hệ thống (5xx) hoặc timeout khi PATCH/DELETE |
"Không thể cập nhật giỏ hàng, thử lại." | Banner tại dòng | Bấm lại nút +/- hoặc Xoá | AC-US003-09, 10 |
Dãy mã theo UICONV §10: E-CART- tiếp từ 0002 (0001 đã dùng cho BR-CART-02 ở US-004). Sau SRS này, số kế tiếp chưa dùng là 0007 — đã cập nhật UICONV §10.
4.2 Text màn hình
| Khoá | Ngữ cảnh | VI | EN | KO/ZH/JA | Giới hạn ký tự |
|---|---|---|---|---|---|
cart.title |
Header C01 | "Giỏ hàng của bạn" | "Your Cart" | 🔴 để trống — OQ-023 |
🔴 chưa chốt — OQ-023 |
cart.itemCount |
Header C01 phụ | "({n} sản phẩm)" | "({n} items)" | 🔴 OQ-023 |
— |
cart.empty.title |
Trạng thái rỗng | "Giỏ hàng trống" | "Your cart is empty" | 🔴 OQ-023 |
— |
cart.empty.cta |
Nút C10 | "Tiếp tục mua sắm" | "Continue shopping" | 🔴 OQ-023 |
— |
cart.error.load |
Banner C12 | "Không tải được dữ liệu, thử lại." | "Failed to load data. Please try again." | 🔴 OQ-023 |
— |
btn.retry |
Nút C13 | "Thử lại" | "Try again" | 🔴 OQ-023 |
— |
btn.delete |
Nút C07 | "Xoá" | "Remove" | 🔴 OQ-023 |
— |
cart.delete.confirm.title |
Modal xác nhận xoá | "Xoá sản phẩm khỏi giỏ hàng?" | "Remove item from cart?" | 🔴 OQ-023 |
— |
cart.delete.confirm.body |
Modal xác nhận xoá | "Sản phẩm sẽ được xoá khỏi giỏ hàng của bạn." | "This item will be removed from your cart." | 🔴 OQ-023 |
— |
btn.checkout |
Nút C11 | "Tiến hành Checkout" | "Proceed to Checkout" | 🔴 OQ-023 |
— |
E-CART-0002.msg |
Inline F01 | "Số lượng phải là số nguyên từ 1 trở lên." | "Quantity must be a whole number of 1 or more." | 🔴 OQ-023 |
— |
E-CART-0003.msg |
Banner dòng | "Sản phẩm này đã được xoá khỏi giỏ hàng trước đó." | "This item has already been removed from your cart." | 🔴 OQ-023 |
— |
E-CART-0005.msg |
Trang trạng thái | "Bạn không có quyền truy cập nội dung này." | "You don't have permission to access this content." | 🔴 OQ-023 |
— |
E-CART-0006.msg |
Banner dòng | "Không thể cập nhật giỏ hàng, thử lại." | "Couldn't update your cart. Please try again." | 🔴 OQ-023 |
— |
Bản dịch KO/ZH/JA để trống, ghi OQ-023 (đã mở từ UICONV) — không dịch máy, theo ghi chú người duyệt.
4.3 Định dạng hiển thị
Tham chiếu UICONV §8, không có ngoại lệ cho SCR-04.
| Loại dữ liệu | Định dạng | Ví dụ | Ghi chú |
|---|---|---|---|
| Số tiền | #,##0 ₫ |
1.234.567 ₫ | Theo UICONV §8, không làm tròn thêm (VND) |
| Số lượng | Số nguyên, không định dạng | 3 | — |
| Rỗng/null | — |
Theo UICONV §8 |
PHẦN 5 — PHI CHỨC NĂNG
Chi tiết ở NFR_US002-003_v1.0.md. Ở đây chỉ nêu áp dụng cho US-002/US-003.
| ID | Nhóm | Yêu cầu (có số đo) | Điều kiện đo | Cách verify |
|---|---|---|---|---|
| NFR-PERF-01 | Hiệu năng | 🔴 Ngưỡng thời gian tải/cập nhật giỏ hàng chưa chốt riêng — xem NFR §1, OQ-033 |
— | — |
| NFR-SEC-01 | Bảo mật | Mọi PATCH/DELETE cart item kiểm tra quyền sở hữu (chống IDOR), trả 403 ERR_FORBIDDEN_OWNERSHIP khi không khớp |
Theo SAD §4.1.1 | Test gọi thẳng API với token/session khác chủ |
PHẦN 6 — DỮ LIỆU VÀ TÍCH HỢP
6.1 API sử dụng
Chi tiết ở API_US002-003_v1.0.md.
| # | Mục đích | Method | Endpoint | Nguồn contract |
|---|---|---|---|---|
| 1 | Tải giỏ hàng | GET | /v1/cart |
✅ method/path BE cung cấp · ⚠️ schema BA đề xuất |
| 2 | Sửa số lượng | PATCH | /v1/cart/items/{cartItemId} |
✅ method/path BE cung cấp · ⚠️ schema BA đề xuất |
| 3 | Xoá sản phẩm | DELETE | /v1/cart/items/{cartItemId} |
✅ method/path BE cung cấp · ⚠️ schema BA đề xuất |
6.2 Tác động dữ liệu
Tham chiếu IMPACT_CartCheckout_v1.0.md. Không có tác động dữ liệu cũ (greenfield). Điểm chạm nội bộ duy nhất liên quan trực tiếp US-002/US-003: module Catalog & Inventory cung cấp tên/ảnh/biến thể sản phẩm (product_variant_id) hiển thị ở C03–C06 — độ trễ đồng bộ giá/tồn kho vẫn là ASM-06/OQ-009 (chưa xác minh), ảnh hưởng trực tiếp OQ-029/OQ-030 ở SRS này.
PHẦN 7 — BÀN GIAO CHO DEV
| Trạng thái | 🟡 Chưa sẵn sàng — còn 5 OQ chặn G3 (OQ-028…OQ-030, OQ-032, OQ-033) |
| Tài liệu cần đọc kèm | UICONV_e-commerce_v1.0.md v1.1 → WF_US002-003_v1.0.md+.html → AC_US-002_v1.0.md → AC_US-003_v1.0.md → NFR_US002-003_v1.0.md → API_US002-003_v1.0.md |
| Quyết định đã chốt, không phải mặc định | Modal xác nhận bắt buộc khi xoá CartItem (áp UICONV §5 mặc định, không có ngoại lệ) · Cart không tự chuyển abandoned khi rỗng · Checkout (C11) chỉ disable khi 0 CartItem, KHÔNG có điều kiện "phải chọn dòng" trong SRS này |
| Điểm còn mở | OQ-028 (quantity=0) · OQ-029 (nguồn giá) · OQ-030 (giới hạn trên số lượng/tồn kho) · OQ-031 (checkout một phần giỏ — chủ yếu ảnh hưởng US-004) · OQ-032 (ownership Guest) · OQ-033 (ngưỡng hiệu năng) |
| Không được sao chép từ đâu | Không có màn hình cũ (greenfield) — không áp dụng |
PHẦN 8 — OPEN QUESTIONS
| ID | Câu hỏi | Hỏi ai | Từ ngày | Chặn gì | 🔴 chặn G3? | Phương án BA đề xuất |
|---|---|---|---|---|---|---|
| OQ-028 | Sửa số lượng CartItem về 0 (nhập tay hoặc bấm - khi đang ở 1) → hệ thống tự mở modal xoá dòng, hay chặn không cho giảm dưới 1? |
PO | 2026-09-08 | F01, AC-US003-08 |
🔴 | Chặn không cho - giảm dưới 1 (nút - disable ở quantity=1); xoá dòng chỉ qua nút "Xoá" (C07) riêng — tách rõ hai hành động, tránh xoá nhầm khi bấm liên tiếp nút - |
| OQ-029 | Giá hiển thị ở SCR-04 (C06) là giá snapshot lúc thêm giỏ (cart_item.unit_price_snapshot) hay giá hiện tại real-time từ Catalog? |
PO, Tech Lead | 2026-09-08 | C06, WF_US002-003 §5 #2/#3 |
🔴 | Dùng unit_price_snapshot để hiển thị nhất quán với số tiền đã "chốt" khi thêm giỏ, đối chiếu lại giá thật ở bước checkout (US-004) — tránh giỏ hàng nhảy số liên tục do giá đổi ngoài ý muốn khách |
| OQ-030 | Sửa số lượng ở SCR-04 (US-003) có kiểm tra giới hạn trên theo tồn kho hiện tại ngay lúc sửa không, hay chỉ kiểm tra ở checkout (US-004) như BACKLOG đã khoanh phạm vi? |
PO, Tech Lead | 2026-09-08 | F01, C06 (badge hết hàng/giá đổi), WF_US002-003 §5 #2/#4 |
🔴 | Giữ đúng khoanh vùng của BACKLOG: không kiểm tra tồn kho ở màn Giỏ hàng, chỉ kiểm tra ở US-004 — đơn giản hoá US-002/US-003, tránh phụ thuộc real-time vào Catalog ở màn hình tần suất truy cập cao |
| OQ-031 | Nút "Tiến hành Checkout" áp dụng cho toàn bộ giỏ hàng, hay chỉ cho các dòng được tick chọn (theo checkbox mà SAD SCR-04 mô tả nhưng không US nào trong BACKLOG định nghĩa)? |
PO | 2026-09-08 | C11, WF_US002-003 §5 #1, ảnh hưởng US-004 |
🔴 | Áp dụng cho toàn bộ giỏ hàng ở MVP (không có tính năng chọn từng phần) — khớp đúng phạm vi BACKLOG hiện tại, tính năng chọn từng phần đưa vào backlog cho phiên bản sau nếu PO thấy cần |
| OQ-032 | Với Guest, cơ chế kiểm tra quyền sở hữu CartItem khi gọi PATCH/DELETE /v1/cart/items/{cartItemId} có tương đương cơ chế ownership theo JWT (403 ERR_FORBIDDEN_OWNERSHIP) không? SAD §4.1.1/§4.2 chỉ mô tả rõ cho customerId/sellerId trong JWT |
Tech Lead | 2026-09-08 | AC-US003-13, E-CART-0005 |
🔴 | Áp dụng cùng cơ chế: đối chiếu cart_id của cartItemId với session_id trong X-Guest-Session-Id, không khớp → 403 ERR_FORBIDDEN_OWNERSHIP |
| OQ-033 | Ngưỡng thời gian phản hồi (p95) cho GET /v1/cart và cho PATCH/DELETE /v1/cart/items là bao nhiêu giây? SAD NFR-01 chỉ có ngưỡng cho danh mục/tìm kiếm (<2s) và checkout (<3s) |
PO, Tech Lead | 2026-09-08 | NFR_US002-003 §1 |
🔴 | Áp dụng cùng ngưỡng với danh mục/tìm kiếm (≤2s p95) cho GET /v1/cart vì cùng là thao tác đọc; ≤1s p95 cho PATCH/DELETE (thao tác ghi đơn giản, 1 dòng) |
Tự chấm
Gate G3
| # | Tiêu chí | ☐/✅ | Ghi chú |
|---|---|---|---|
| 1 | Đủ mọi mục bắt buộc (mục N/A có ghi lý do) | ✅ | Mọi mục N/A (§2.A.3.2 phân trang, §4.2/4.3 phần không áp dụng) đều ghi lý do |
| 2 | Tiêu chí riêng của biến thể PART 2 đã nạp | ☐ | Bảng field F01 còn 1 ô để trống có lý do rõ (giới hạn trên — OQ-030); bảng thành phần đủ 8 cột; hai trạng thái rỗng khác nhau (đã có) |
| 3 | Mỗi US có AC đủ 4 nhóm, có luồng lỗi | ✅ (có 2 AC còn mở chờ OQ) | Xem AC_US-002, AC_US-003 |
| 4 | Bảng mã lỗi đầy đủ, mỗi mã có thông điệp | ✅ | 5 mã, đủ thông điệp VI+EN |
| 5 | NFR có số đo + cách verify | ☐ | Ngưỡng hiệu năng chưa chốt — OQ-033 |
| 6 | API contract có, ghi rõ nguồn | ✅ | Đánh dấu rõ ✅/⚠️ theo từng phần |
| 7 | QA xác nhận mọi AC test được | ☐ | Chờ QA — chưa có QA thật tham gia lần chạy này |
| 8 | Không còn OQ mở ảnh hưởng hành vi | ☐ | 5/6 OQ mới đánh dấu 🔴 chặn G3 |
| 9 | Không còn TBD trong bảng đặc tả PART 2 và bảng mã lỗi |
✅ | Không dùng chữ "TBD" — mọi chỗ thiếu dùng 🔴 + OQ-nnn cụ thể |
| 10 | Đã áp đúng bảng "Bớt ở light"/"Thêm ở strict" theo RIGOR | ✅ | RIGOR = standard — áp đúng checklist §2, không bớt/thêm |
| 11 | (screen) WF có cho mọi SCR, tập C-id khớp hai chiều, §5 không còn lệch Tồn tại/Hành vi chưa quyết |
☐ | Xem WF_US002-003_v1.0.md §5 — còn 4 dòng chưa quyết |
| 12 | (screen) Quy ước chung tham chiếu UICONV; chỗ khác có dòng ở UICONV §12 |
✅ | 2 dòng ngoại lệ đã thêm vào UICONV §12 (text rỗng SCR-04, auto-save không có nút Lưu) |
Quét bắt buộc trước khi nộp:
grep -niE "nhanh|mượt|thân thiện|v\.v|phù hợp|tương ứng|nên |có thể " SRS_US002-003_v1.0.md
→ chạy thật: 5 dòng khớp ban đầu (§1.5 dòng 1 và 7, §2.A.3.5 hai dòng "tương ứng", §8 OQ-028
"bấm nhanh") — đã sửa lại câu chữ cho cả 5 dòng, chạy lại lần hai: 0 dòng khớp
grep -n "TBD\|TODO\|???" SRS_US002-003_v1.0.md
→ 0 dòng khớp (dùng 🔴 + OQ-nnn thay vì TBD)
Quy tắc viết W1–W13
| W1 | W2 | W3 | W4 | W5 | W6 | W7 | W8 | W9 | W10 | W11 | W12 | W13 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| ✅ | ✅ | ✅ (con số thiếu ⇒ OQ, không bịa) |
✅ (sequence đủ 4 nhánh) | ✅ (§4.2) | ✅ (§1.4 tham chiếu BR §3) | ✅ (§2.A.3.1 cột riêng) | ✅ (mọi OQ ghi rõ đề xuất, không quyết thay) |
✅ (mọi AC/field ghi US/BR) | ✅ (§1.1 cho PO) | ✅ (có câu quy ước đầu tài liệu) | ✅ (§1.5) | ✅ (2 sơ đồ mermaid, đủ bảng đi kèm) |