5.4 KiB
5.4 KiB
SPEC WAVE-2 — CRUD phiếu nháp + căn cứ b.8-9 + FE 3 page ×2 app (GĐ2 KHKK)
Nguồn RATIFIED: spec cũ §2.3 (CQRS) + §2.4 (FE 4-place) + §③ WAVE-2 (
:310-339). Chạy SAU W1.
① TÍNH NĂNG / ĐỀ BÀI
BCH tạo được phiếu nháp "Kế hoạch ký kết HĐ" từ PE DaDuyet: dòng giá đề xuất per-winner (snapshot PeReferenceAmount), danh mục căn cứ b.8-9 (mẫu/shopdrawing TVGS per-item status), đính kèm — chưa trình được (trình = W3). Người dùng thấy: 6 leaf Khkk_* hết ComingSoon, thành trang thật.
② CÁCH IMPLEMENT
- Gate 5-anchor (§③-A).
- BE
Application/ContractSigningPlans/ContractSigningPlanFeatures.cs(mega-file khuônProposalFeatures.cs— CHỈ CRUD/DTO, 🔴 CẤM copy phần duyệt của Proposal, bẫy §2.1 spec cũ):CreateContractSigningPlanCommand(PeId, ApprovalWorkflowId, GhiChu?): validator PE tồn tại +Phase==DaDuyet(mirrorPurchaseEvaluations/CreateContractFromEvaluationFeatures.cs:53-54— đường dẫn ĐẦY ĐỦ, file KHÔNG nằm ở Contracts/) + chưa-có-plan-sống (AnyAsync(p.PeId==x && p.Phase != TuChoi)→ 409; cho tạo lại sau TuChoi) + workflowApplicableType==10(mirror Proposal:258-267— copy CẢ guard:266-267, đừng chỉ copy phần nạp). Handler auto-sinh Lines từ winners:pe.Suppliers.Where(IsWinner),PeReferenceAmount = SUM(Quotes WHERE IsSelected)per-winner (copy phép tính:56-62,:88-90) — snapshot 1 lần. SinhMaKeHoach=KHKK/{YYYY}/{Seq:D3}(W1 CodeGen).UpdateContractSigningPlanDraftCommand: guardPhase ∈ {DangSoanThao, TraLai}+ cho re-pinApprovalWorkflowId.Get/List/pendingMe: detail Include Lines+DossierItems+Attachments+LevelOpinions+workflow-tree (khuôn Proposal:161-228); inbox V2 precompute TỪ NGÀY 1 (mirror PEResolveV2InboxIdsAsync) — wire sẵn cho W3.UpsertDossierItemCommand/DeleteDossierItemCommand: chỉ mởDangSoanThao|TraLai. Attachment mở mọi phase (triết lý PE S147).DeleteContractSigningPlanCommand: allow-listDangSoanThao|TuChoi— mở NGAY W2 (spec cũ M6: thiếu nó, tạo nhầm 1 phiếu là PE bị khoá 409 vĩnh viễn).- Màn nguồn "PE đã duyệt — chờ lập KH": query mirror picker
ListApprovedPurchaseEvaluationsQuery:173đổi vị ngữ&& chưa-có-plan-sống.
- Controller
ContractSigningPlansController.cs— 🔴 authz 2 TẦNG: class[Authorize]+ per-action[Authorize(Policy="KeHoachKyKet.{Read|Create|Update|Delete}")](Δ W1: prefixKeHoachKyKet, KHÔNG phảiContractSigningPlansnhư spec cũ:202). Endpoints: GET list/inbox · GET {id} · POST · PUT {id} · dossier-items CRUD · attachments · DELETE {id}. (transitions = W3.) - FE ×2 app (duplicate CÓ CHỦ ĐÍCH, 4-place mirror Pattern 16-bis):
pages/khkk/3 page (List ~252 + Create ~245 + Detail ~386 + types ~95 — khuôn bộ Proposal) ·App.tsxroutes ·Layout.tsxstaticMap re-point 6 leafKhkk_*khỏi/coming-soon(List→list · Create→create · Pending→list?filter=ChoDuyet · Approved→list?filter=DaDuyet · Deleted→deleted-view · WfView→matrix type=10) ·lib/menuKeys.ts. Detail: bảng Lines (đề xuất per-NCC) + bảng DossierItems (chip status + TVGS + scan) + attachments; panel workflow + banner = W3. - Build ×2 + test + commit.
③ CHECKLIST
A. Gate 5-anchor: ProposalFeatures.cs:427-439 (bẫy flatten — để BIẾT mà né) · CreateContractFromEvaluationFeatures.cs:53-62 (guard+phép tính snapshot) · ProposalFeatures.cs:258-267 (guard workflow-type trọn) · DbInitializer.cs:2207 khu staticMap comment Khkk_* (leaf đang ComingSoon) · MenuKeys.cs All (KeHoachKyKet ĐÃ vào từ W1).
B. Acceptance (spec cũ W2 :322-337 + Δ):
POST /api/contract-signing-planstừ PE DaDuyet → 201; DB N Lines = số winner (đếm đối chiếuPurchaseEvaluationSuppliers WHERE IsWinner=1).PeReferenceAmountmỗi Line == SUM Quotes IsSelected đúng NCC (2 câu đếm so khớp).- Tạo lần 2 cùng PE → 409. · [ ] PE chưa DaDuyet → 400/409, KHÔNG 500.
- 🔴 Authz 2 tầng: user thiếu quyền POST → 403 (không 200/401/500); có quyền → 201. Gate rẻ:
grep -c "Authorize(Policy" ContractSigningPlansController.cs≥ 4. - DELETE ở DangSoanThao → 204; sau đó tạo lại plan cho CÙNG PE → 201 (chứng thoát khoá 409).
dotnet test≥ baseline + 2, PASS 0 fail, tên khuôn repo (English-predicate):ContractSigningPlansController_EveryWriteEndpoint_HasAuthorizePolicy+CreateContractSigningPlan_FromNonApprovedPe_Throws.- 🔴 Grant ăn (review F-04): sau seed, Permission
KeHoachKyKetcó CanCreate=1 cho role đích (đo CỜ — row đã tồn tại nên đếm-row 0-bit); user role đóPOST→ 201, user không-cấp → 403 (KHÔNG 500 — chứng policyKeHoachKyKet.*sinh đúng, khớp key ∈ All). - FE build ×2 PASS · SHA256 file mirror IDENTICAL · 6 leaf
Khkk_*hết trỏ coming-soon:grep "Khkk_" fe-user/src/components/Layout.tsx | grep -c "coming-soon"= 0 (cùng lệnh fe-admin). - cicd 3-chân-kiềng + byte-marker bundle (control ÂM độc quyền — chọn chuỗi trước khi đo). C. Rủi ro: [ ] HasQueryFilter hoạt động (phiếu xóa mềm không hiện list) · [ ] tạo-lại-sau-xóa không bị chặn oan (gotcha #57).