[CLAUDE] Docs: S89 closeout — PE visibility/finalize + user-cleanup + Word guide

- STATUS/HANDOFF: prepend S89 (demote S88); state Mig 59 · 422 test · prod 17 user
- session log 2026-06-25-S89 (D1 draft-privacy IDOR · D2 CCM suggested-price !readOnly · D3 finalize-note 3-surface + flow badge + matrix flag · prod user-delete · Word guide)
- docs/Huong-dan-Duyet-NCC.docx (skill docx — logo Solution, 8 sections, khoanh-do 1->2->3; real screenshots pending anh)
- harvest residual workflow-sub agent-memory (cicd / investigator-codebase / reviewer + 2 plan-reviews)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
pqhuy1987
2026-06-25 17:05:49 +07:00
parent 681b1edc7e
commit 946c5b21ae
9 changed files with 107 additions and 5 deletions

View File

@ -70,6 +70,7 @@ Bearer từ `POST api.solutions.com.vn/api/auth/login` → status matrix expecte
## 📅 Recent activity (compressed — full verbatim → `archive/2026-06.md` via `archive/_INDEX.md`)
- **2026-06-25 PE S89 AllowApproverFinalize NOTE feasibility (anh Kiệt FDC — "phiếu kết thúc cấp X không CEO" 3 surface):** ⭐ **GAP CONFIRMED — BE field MỚI cần.** `AllowApproverFinalize` (Mig 58 per-Level bool `ApprovalWorkflow.cs:142`) HIỆN CHỈ expose qua `currentLevelOptions` (CURRENT level only) `PurchaseEvaluationFeatures.cs:981` + DTO `PurchaseEvaluationDtos.cs:123` + FE type `purchaseEvaluation.ts:412`. **`approvalFlow` DTO KHÔNG carry flag**: flow-level record `PurchaseEvaluationApprovalFlowLevelDto` (`Dtos.cs:154-158`) chỉ {Order,Name,Approvers,Status}; build loop `PurchaseEvaluationFeatures.cs:1050-1065` KHÔNG project `l.AllowApproverFinalize`. **LIST DTO KHÔNG có** (`PurchaseEvaluationListItemDto:6-45`, 0 finalize field). **DASHBOARD = reuse list** → cũng thiếu. ⇒ FE detail CHỈ biết finalize khi actor LÀ approver cấp finalize (current). Để render "phiếu này kết thúc ở Bước Y/Cấp X — không CEO" cho MỌI người (kể cả non-approver, list, dashboard) → BE PHẢI expose field mới: bundle `bool EndsBeforeCeo`+`string? FinalizeStepName/FinalizeLevelName` (compute = first Step/Level có AllowApproverFinalize=true trong aw.Steps loop đã load `:985`) + mirror lên list-item + dashboard. BE finalize logic `PurchaseEvaluationWorkflowService.cs:861` (AUTO khi matchingLevel.AllowApproverFinalize → DaDuyet bỏ CEO, NO threshold/role-gate). **FE detail placement (cả 2 app SHA-IDENTICAL** sha `e477c939` PeWorkflowPanel + `49b39404` PeDetailTabs): note nên đặt đầu `PeWorkflowPanel.tsx` flow `<ol>` (sau h3 "Quy trình duyệt" :245, trước :256) — banner emerald mirror style :599 existing approverFinalize callout (chỉ trong dialog hiện). Detail đã có precedent: TraLai banner `PeDetailTabs.tsx:370-378`, V2 current-level banner `PeWorkflowPanel.tsx:346-365`. Edge: phiếu **DaDuyet** → đổi copy "đã kết thúc tại <X>, không qua CEO" (past-tense); copy đề xuất "⚑ Quy trình này duyệt tới <StepName/Cấp X> là KẾT THÚC — không trình CEO". MIRROR 2-app bắt buộc. → _INDEX.
- **2026-06-25 PE FROZEN patchwork audit AREA-6 (FE consumers BOTH apps — multi-winner + mirror-drift + dead-code + #70):** ⭐ Post-Mig58 multi-winner (`isWinner` bool) NOW implemented (supersedes 06-24 "SINGLE" recon). **All 6 PE components + 3 PE pages SHA-IDENTICAL fe-admin↔fe-user** (PeDetailTabs/PeHeaderForm/PeUrgentChips/PeListPanel/PeWorkspaceCreateView/PeWorkflowPanel). type files DIFFER but ONLY badge-tint (-700→-800 ink) + comments (intentional per-app UX, struct fields identical incl ccmBudgetPeriodAmount/isWinner/8 Allow flags). **RESIDUAL single-winner BUG (low):** `PeListPanel.tsx:168` renders `p.selectedSupplierName` (list-card winner label) → BLANK when ≥2 joint winners (BE list-DTO `PurchaseEvaluationFeatures.cs:615,706` projects from `SelectedSupplierId` which BE sets null when count≠1 `PurchaseEvaluationDetailFeatures.cs:431`). List DTO has NO isWinner/winner-list field; detail-view multi-winner correct everywhere (8 detail consumers all converted: computeGiaChaoThau:78, missingForApproval:195, submitChecklist:229, ThongTinChonThau:1949/1970, Section-B row4:1578, CreateContractDialog:2171 fallback `||selectedSupplierName`, HangMucCard toggle:2560). **#70 stale-echo ASYMMETRY:** budget Section-B CCM-period editor gates `ccmPeriodMut.isPending||peFetching` (`PeDetailTabs.tsx:1563`) but PRO sibling row3/row8 `adjustMut` gates `.isPending` ONLY (`:1548,:1649`, NO peFetching) — rapid PRO row3→row8 absolute-set pair can echo stale ev.* pre-refetch (same #70 class CCM guards). 3 peFetching sites exist (945/1268/1736). **Stale comment** PeDetailTabs:2507 references `AddItemDialog` "giữ(dead)" but function DELETED (grep 0 def) — misleading comment only, not dead code. PeWorkflowsPage admin-only = by-design (workflow config not mirrored). → _INDEX.
- **2026-06-25 PE FROZEN patchwork audit AREA-2 (budget matrix + Section B 3-col, Mig 56/59):** ⭐ **BAND-AID CONFIRMED** `SetPeCcmBudgetPeriodCommand+Validator+Handler` (`PeSuggestedPriceFeatures.cs:153-199`) is a BUDGET setter (writes `pe.CcmBudgetPeriodAmount`) MISPLACED in a price-feature file; SHOULD live in `PeWorkItemBudgetFeatures.cs` (where UpdatePeBudgetPro/Ccm sit). Test+comments already self-flag (PeCcmBudgetPeriodSetterAuthzTests.cs:14). ⚠️ **Role-gate ASYMMETRY (intentional, NOT a bug — but inconsistent surface):** CCM budget setters = flat role-gate (CostControl|Admin fail-closed): UpdatePeBudgetCcm `PeWorkItemBudgetFeatures.cs:160-165` + SetPeCcmBudgetPeriod `PeSuggestedPriceFeatures.cs:176-181`. BUT the PRO sibling `/budget-adjust``AdjustPurchaseEvaluationBudgetCommandHandler` `PurchaseEvaluationFeatures.cs:349-394` = **phase/drafter/workflow-slot gate** (Drafter when Nháp/TraLai; Approver+AllowApproverEditBudget F4 when ChoDuyet) NOT role-PRO. So "PRO col" & "CCM col" of same Section-B row 3 have DIFFERENT authz models (PRO=drafter-flow on `pe.BudgetPeriodAmount`; CCM=role-flow on `pe.CcmBudgetPeriodAmount`). Both per-PE fields (NOT PeWorkItemBudget per-pair — that's the matrix rows Initial/Adjustment). Section-B **"Dự án" col = intentional FE-only "—"** hardcoded (`PeDetailTabs.tsx:1520,1542,1596,1606`; Domain `PeWorkItemBudget.cs:19` "chưa wire BE — sau mới có người dự án nhập") — documented, NOT a stub-bug. Controller class-level `[Authorize]` only (`PurchaseEvaluationsController.cs:15`) → handler gates ARE the boundary. Tests solid (PeWorkItemBudgetTests 30+ cases, PeCcmBudgetPeriodSetterAuthzTests 7). → _INDEX.