Skip to content

QA Test Plan: Quản lý vật tư theo Subtask

Feature slug: subtask-materialVersion: 1.0 Ngày: 2026-03-26


D1) Coverage

LoạiSố lượngChi tiết
FR8FR-001 → FR-008
NFR2NFR-001, NFR-002
Edge Cases7EC-01 → EC-07
Test Cases22TC-FR-001 → TC-FR-008, TC-NFR-001 → TC-NFR-002, TC-EC-001 → TC-EC-007, TC-CP-001 → TC-CP-005

D2) Requirements Matrix

FR/NFRTest CasesPriority
FR-001 Thêm vật tưTC-FR-001a (tạo mới), TC-FR-001b (search thêm)Critical
FR-002 Auto-fillTC-FR-002a (có config), TC-FR-002b (không config)Critical
FR-003 Sửa vật tưTC-FR-003a (sửa SL), TC-FR-003b (xóa dòng), TC-FR-003c (thêm khi sửa)Critical
FR-004 ReadonlyTC-FR-004High
FR-005 AggregateTC-FR-005a (aggregate đúng), TC-FR-005b (cập nhật khi sửa subtask)Critical
FR-006 Backward compatTC-FR-006a (data cũ), TC-FR-006b (cả 2 data)High
FR-007 Fix button + confirmTC-FR-007a (label), TC-FR-007b (confirm message)Medium
FR-008 Product disabledTC-FR-008a (auto-fill filter), TC-FR-008b (badge)High
NFR-001 Insert perfTC-NFR-001High
NFR-002 Aggregate perfTC-NFR-002High

D3) Seed Data

Dataset SD-01: Task cha + 2 Subtask + Materials

Cách tạo:

Option A: SQL Script (khuyến nghị)

sql
-- 1. Tạo project (nếu chưa có test project)
-- Giả sử project_id = 'proj-test-001' đã tồn tại

-- 2. Tạo task cha
INSERT INTO project_task (id, project_id, name, status_id, created_by)
VALUES ('task-parent-001', 'proj-test-001', 'Laser Q-switch tàn nhang - Buổi 1 - KH Test', 'new_branch', 'test-user');

-- 3. Tạo 2 subtask
INSERT INTO project_task (id, project_id, parent_id, name, status_id, created_by)
VALUES
  ('task-sub-001', 'proj-test-001', 'task-parent-001', 'BS - Laser sắc tố nám', 'new_branch', 'test-user'),
  ('task-sub-002', 'proj-test-001', 'task-parent-001', 'Chăm sóc da cơ bản', 'new_branch', 'test-user');

-- 4. Tạo materials cho subtask 1
INSERT INTO project_task_material (task_id, product_id, product_name, product_sku, product_unit, quantity, created_by)
VALUES
  ('task-sub-001', 'prod-serum-001', 'Serum Laser X', 'SP001', 'ml', 2, 'test-user'),
  ('task-sub-001', 'prod-gel-042', 'Gel làm mát', 'SP042', 'tube', 1, 'test-user');

-- 5. Tạo materials cho subtask 2
INSERT INTO project_task_material (task_id, product_id, product_name, product_sku, product_unit, quantity, created_by)
VALUES
  ('task-sub-002', 'prod-serum-001', 'Serum Laser X', 'SP001', 'ml', 2, 'test-user'),
  ('task-sub-002', 'prod-mask-018', 'Mask dưỡng da', 'SP018', 'miếng', 1, 'test-user');

Option B: UI Manual

  1. Login role Manager → Tab Công Việc → Chọn 1 task cha
  2. Click "Thêm công việc" → tạo subtask "BS - Laser sắc tố nám" → thêm 2 vật tư → Lưu
  3. Click "Thêm công việc" → tạo subtask "Chăm sóc da cơ bản" → thêm 2 vật tư → Lưu

Dataset SD-02: Task cha có order_materials cũ (backward compat)

sql
-- Task cha có order liên kết (data trước deploy)
-- Giả sử đã có order với order_kind = 'internal_material' và task_id = task cha
-- Dùng data sẵn có trên test env — không cần tạo mới
-- Verify: query project_task WHERE id = X → order_materials phải có data

Dataset SD-03: Subtask done + product disabled

sql
-- 1. Subtask done
UPDATE project_task SET status_id = 'done_branch', done_at = now()
WHERE id = 'task-sub-001';

-- 2. Product disabled (giả sử product 'prod-serum-001' bị disable)
-- Chạy trên ecommerce DB:
UPDATE product SET disabled = true WHERE id = 'prod-serum-001';

D4) Test Cases

Critical Path Tests

TC-IDTênPreconditionStepsExpectedDatasetPriority
TC-CP-001Tạo subtask với auto-fill vật tư + saveSD-01 (project + task cha)1. Mở task cha → Click "Thêm công việc" 2. Chọn subtask definition có product_relation 3. Verify auto-fill 4. Bấm LưuSubtask tạo thành công + 2 records trong project_task_materialSD-01Critical
TC-CP-002Sửa subtask: thay đổi SL + thêm vật tư mớiSD-01 (subtask có materials)1. Click sửa subtask 2. Đổi SL Serum 2→3 3. Search "Mask" → thêm 4. LưuSL updated = 3, thêm 1 record mớiSD-01Critical
TC-CP-003Aggregate view cập nhật đúngSD-01 (2 subtask có materials)1. Mở task cha 2. Verify bảng VẬT TƯ DỰ KIẾNSerum = 4ml (2+2), Gel = 1, Mask = 1SD-01Critical
TC-CP-004Xóa subtask → materials cascade + aggregate giảmSD-011. Xóa subtask 1 (confirm dialog mention 2 vật tư) 2. Verify aggregateAggregate: Serum = 2 (chỉ từ subtask 2), Gel = 0 (không hiện)SD-01Critical
TC-CP-005End-to-end: tạo + sửa + xóa + verify aggregateSD-01Full flowAggregate luôn chính xác sau mỗi thao tácSD-01Critical

FR Test Cases

TC-IDFRTênStepsExpectedPriority
TC-FR-001aFR-001Thêm vật tư khi tạo subtaskMở form tạo subtask → search vật tư → chọn → nhập SL → LưuRecord inserted, denormalized fields đúngCritical
TC-FR-001bFR-001Search vật tư theo SKUSearch "SP042"Hiện "Gel làm mát" (SP042)High
TC-FR-002aFR-002Auto-fill có configChọn subtask def có 3 product_relationBảng fill 3 dòng với đúng product + quantityCritical
TC-FR-002bFR-002Auto-fill không có configChọn subtask def không có product_relationBảng trống + text "Chưa có vật tư"High
TC-FR-003aFR-003Sửa SLMở sửa subtask → đổi SL Serum 2→5 → Lưuproject_task_material.quantity = 5Critical
TC-FR-003bFR-003Xóa dòng vật tưMở sửa subtask → click 🗑 Gel → LưuRecord Gel bị xóa, chỉ còn SerumHigh
TC-FR-003cFR-003Thêm khi sửaMở sửa → search "Mask" → thêm → LưuThêm 1 record mới, records cũ giữ nguyênHigh
TC-FR-004FR-004Readonly khi doneSD-03 (subtask done) → mở sửaẨn 🗑, disable SL input, ẩn searchHigh
TC-FR-005aFR-005Aggregate cộng dồnSD-01 (2 subtask cùng dùng Serum)Serum aggregate = 4 (2+2)Critical
TC-FR-005bFR-005Aggregate cập nhật realtimeSửa subtask 1 SL Serum 2→3 → save → xem task chaAggregate Serum = 5 (3+2)High
TC-FR-006aFR-006Backward data cũSD-02 (task có order_materials)Hiện bảng "VẬT TƯ (ĐÃ XUẤT KHO)" với data cũHigh
TC-FR-006bFR-006Cả 2 dataTask có order_materials + project_task_materialHiện 2 sections riêng biệtHigh
TC-FR-007aFR-007Button label fixMở task detail (automate=true)Button hiện "Thêm công việc" (không phải "Thêm vật tư")Medium
TC-FR-007bFR-007Delete confirm messageXóa subtask có 3 materialsDialog hiện "Sẽ xóa 3 vật tư đi kèm"Medium
TC-FR-008aFR-008Auto-fill filter disabledProduct "Serum cũ" disabled → chọn subtask defSerum cũ KHÔNG xuất hiện trong auto-fillHigh
TC-FR-008bFR-008Badge "Ngưng KD"SD-03 (product disabled sau khi lưu) → mở sửaBadge "Ngưng KD" cạnh tên SerumHigh

NFR Test Cases

TC-IDNFRStepsExpectedPriority
TC-NFR-001NFR-001Insert 10 materials cho 1 subtask → measure timeResponse < 500msHigh
TC-NFR-002NFR-002Task cha có 20 subtask × 5 materials = 100 records → load aggregateResponse < 200ms (dùng denormalized, zero remote join)High

Edge Case Test Cases

TC-IDECStepsExpectedPriority
TC-EC-001EC-01Chọn subtask def không có product_relationBảng trống + "Chưa có vật tư. Tìm kiếm để thêm."Medium
TC-EC-002EC-02Search "xyz123" (không tồn tại)Dropdown trống + "Không tìm thấy"Medium
TC-EC-003EC-03Nhập SL = 0 → LưuValidate error inline "SL phải > 0", không cho lưuHigh
TC-EC-004EC-04Thêm product đã có trong bảngToast warning "Vật tư đã có trong danh sách"Medium
TC-EC-005EC-05Task cha không có subtask nào có materialsSection "VẬT TƯ DỰ KIẾN" ẩn hoàn toànMedium
TC-EC-006EC-06Task cha chỉ có order_materials cũ, không có data mớiChỉ hiện "VẬT TƯ (ĐÃ XUẤT KHO)", không hiện "VẬT TƯ DỰ KIẾN"Medium
TC-EC-007EC-07Manual board (automate=false): tạo subtaskMaterialForm hiện, auto-fill không chạy, search hoạt độngMedium

D5) Entry / Exit Criteria

Entry Criteria

  • [ ] Migration create_project_task_material đã apply thành công trên test env
  • [ ] Hasura metadata reload thành công (schema + permissions)
  • [ ] pnpm codegen pass (TypeScript types generated)
  • [ ] MaterialForm component render được (không error)
  • [ ] Test accounts sẵn sàng: Staff, Manager, Admin, Call Center

Exit Criteria

  • [ ] 5/5 Critical Path tests pass (TC-CP-001 → TC-CP-005)
  • [ ] 16/16 FR tests pass
  • [ ] 2/2 NFR tests pass
  • [ ] 7/7 Edge case tests pass
  • [ ] Backward compat verified (data cũ hiển thị đúng)
  • [ ] Tiền tour KHÔNG bị ảnh hưởng (verify trên 1 subtask có materials + tour_money)
  • [ ] No P1/P2 bugs open