wal: flush 20260712T1711

This commit is contained in:
pqhuy1987
2026-07-12 17:11:07 +07:00
parent 8fa2fcccb2
commit 2b54293679
2 changed files with 2 additions and 0 deletions

View File

@ -75,6 +75,7 @@ UI `disabled={!canX}` + BE helper `EnsureCanXAsync(id, userId)` throw 403 (NOT i
## 📅 Recent activity (FIFO — older → archive/git)
- **2026-07-12 (S112 PE sign-off close-review implement — `wf_7df56b89-7bb` GO-WITH-ADJ, owner anh Kiệt PROD-UAT-live, 4-region/1 file `PurchaseEvaluationWorkflowService.cs`, NO mig/FE/test → ACCEPT Case 1):** **C1 A1 (`ApplyDrafterBypassOnSubmitAsync`):** người soạn KÝ TAY ô cấp mình — `k=Max``minOwn=Min(drafterSlots.Order)`, bypass `<=k``<minOwn` (chỉ NV cấp dưới THUẦN), XÓA canned-opinion UPSERT (không tự ký hộ ai) + dead `maxLevelOrder`/`ownSlot`; advance 3-nhánh→1 `if(minOwn>1){pointer=minOwn}` STOP tại minOwn (người soạn tự ApproveV2Async); minOwn=1→bypass rỗng pointer giữ (0,1); non-contiguous Cấp1+Cấp3 ký tay đúng thứ tự. **C2 EDGE-5 hard-lock:** top `TransitionAsync` sau isAdmin/isSystem TRƯỚC mọi branch → `if(fromPhase is DaDuyet or TuChoi && !isAdmin && !isSystem) throw Conflict` (chặn non-admin lật phiếu terminal; Admin+System MIỄN); KHÔNG đụng EnsureCanRejectV2Async. **C3** no self-finalize (owner oQ1 permission-driven). **C4 opinion-history (`ApproveV2Async` UPSERT else):** old.Comment→Changelog (Workflow/Update/EntityId=level.Id) TRƯỚC overwrite chỉ khi non-empty — NO mig/bảng/endpoint. Build slnx **0/0**. Infra **4 FAIL/401 pass**: fail=C1 (PeSubmitGuardAndBypassTests 9/10/12/14 assert OLD auto-sign/terminal-on-submit); C2/C4 break 0 test → test-specialist ADD. LESSON: behavior-change write-site → run consumer tests, hand VERIFIED fail-list (KHÔNG sửa test). Tag `[s112, pe-signoff, a1-sign-own-level, edge5-hardlock, opinion-history-changelog, consumer-test-verified-handoff]`.
- **2026-07-12 (S112 Supplier Excel-import Phase B BE — `wf_e2f23a84` invest-ensemble → `wf_75d6149b` close-review GO-WITH-ADJ, owner anh Kiệt, commit `e100ef0`, Mig 63 → ACCEPT Case 1/2):** **Service** `SupplierExcelImportService` (preview + confirm 2-stage) + 2 endpoint (`/suppliers/import/preview` [415 non-xlsx] · `/suppliers/import/confirm` [401 auth]) + **Mig 63 `AddSupplierImportSourceFields`** (Supplier +`SourceUpdatedAt datetime2?`+`SourceUpdatedBy nvarchar(200)?` provenance cột 29/30; 2 AddColumn nullable no-new-table 89). FE 2-app byte-identical (`SupplierImportDialog`). **LESSONS:** (1) dedup Code = **OrdinalIgnoreCase ở SERVICE** — DB unique-index CI-collation lật (SQLite fixture BINARY KHÔNG bắt, prod SQL Server CI bắt) → dedup KHÔNG dựa DB; (2) parser **absolute cell-index** (row-4 header-fingerprint), KHÔNG `CellsUsed()` (column-drift); (3) **all-or-nothing** gate TRƯỚC mutate (1 hard-error→Committed=false DB unchanged); (4) fill-nulls **SKIP Code/Name/Type** (chỉ điền field trống); (5) `.Normalize(FormC)` cho real-file NFD (close-review adjust bake 30 `RealFileHeaderTokens` byte-verify). **SURPRISE:** Approach-B (extend seed) ĐÃ ship 4 NCC prod (Mig 62) → spec "partial" SAI; import ghi entity **TRỰC TIẾP** (fill-where-non-empty) KHÔNG qua `UpdateSupplierCommand` (tránh NULL-clobber #73). Shipped Run #496 PASS, reviewer GO-WITH-ADJ (BE 6/6 axes). Tag `[s112, supplier-excel-import, mig63, ordinalignorecase-dedup-at-service, abs-cell-index-parser, all-or-nothing, fill-nulls-safe, normalize-formc-nfd, direct-entity-write]`.
- **2026-06-25 (S88 PE stability cleanup — FROZEN-module mechanical relocation + 1 stale-comment fix BE — NO migration, 3 edit/2 file, em-main spec deterministic 100% file/line/boundary given → ACCEPT Case 1):** Pure mechanical move ZERO behavior-change. **T1 misplaced-command relocate:** `SetPeCcmBudgetPeriodCommand`+Validator+Handler (BUDGET setter writes `pe.CcmBudgetPeriodAmount` Section B "NS kỳ này" CCM, Mig 59) sống NHẦM trong suggested-PRICE file → move về budget file. (a) REMOVE `PeSuggestedPriceFeatures.cs` lines 144-199 (blank + `===== CCM NS kỳ này =====` header → `SetPeCcmBudgetPeriodCommandHandler` closing `}`); file end clean tại line 143 (`UpdatePeSuggestedPriceCcmCommandHandler` `}`), suggested-price PRO/CCM trên giữ nguyên. (b) APPEND `PeWorkItemBudgetFeatures.cs` END sau line 200 (`UpdatePeBudgetCcmCommandHandler` `}`) → cạnh budget setters `UpdatePeBudgetPro/Ccm`. Code BYTE-IDENTICAL (logic/authz Admin\|CostControl fail-closed/validation GTE(0).When(HasValue)/absolute-set null=clear/changelog UNCHANGED); CHỈ prepend 1 comment line `// [S88] Chuyển từ PeSuggestedPriceFeatures.cs về đây (đúng nhóm budget setter).`. Usings 8-header IDENTICAL 2 file → KHÔNG add duplicate. Namespace `SolutionErp.Application.PurchaseEvaluations` SAME → MediatR type-resolution transparent, controller `:125` KHÔNG đụng. **T2 stale-comment fix:** `Dtos/PurchaseEvaluationDtos.cs` ~250-254 comment trên `decimal WinnerQuoteTotal` ghi SUM "của SelectedSupplierId" (OLD single-winner) → verified actual compute `PurchaseEvaluationFeatures.cs:1093-1099` = `Suppliers.Where(s=>s.IsWinner)` (multi, Mig 58) → sửa CHỈ comment-text "MỌI đơn vị IsWinner (multi-winner Mig 58)". Field/code UNCHANGED. **LESSON:** frozen-relocation = read BOTH file FIRST exact boundary + verify usings-superset target + verify stale-comment claim vs real compute-site TRƯỚC sửa (KHÔNG trust comment blind, mirror S35 EF-source-of-truth). Build `SolutionErp.Application.csproj -v q` **0 warn 0 err** (4s). KHÔNG test (test-specialist)/FE/mig/commit. Tag `[s88, frozen-relocation, byte-identical-move, mediatr-type-resolve-transparent, stale-comment-verify-compute-site, no-behavior-change]`.
- **2026-06-24 (PE Section B 3-cột THỰC HIỆN — CCM "NS kỳ này" cột riêng BE — Mig 59 `AddCcmBudgetPeriodToPurchaseEvaluation` 3-file, 5 edit/0 new file, em-main CHỐT spec+API-contract 100% → ACCEPT Case 1):** Section B tách 3 cột Dự án|PRO|CCM giống Section A. PRO=`BudgetPeriodAmount` sẵn có; CCM nhập cột riêng độc-lập. COOKIE-CUTTER mirror S77 suggested-price-Ccm + S65b HoSoLink one-column. (1) `PurchaseEvaluation.cs` +`decimal? CcmBudgetPeriodAmount` sau ExpectedRemainingAmount. (2) `PurchaseEvaluationConfiguration.cs` +`HasColumnType("decimal(18,2)")` sau ExpectedRemaining (spec asked literal decimal(18,2); siblings dùng HasPrecision(18,2) NHƯNG emit identical column — tôi follow spec verbatim HasColumnType). (3) Mig: Up=1 AddColumn decimal(18,2) nullable NO table/index, Down=1 DropColumn, snapshot verified — clean mirror Mig 52. (4) DTO `PurchaseEvaluationDetailBundleDto` +`decimal? CcmBudgetPeriodAmount` sau ExpectedRemainingAmount TRƯỚC BudgetSummary + projection PEFeatures.cs:1110 positional-insert đúng order (positional record → projection order BẮT BUỘC khít). (5) Setter `SetPeCcmBudgetPeriodCommand(Guid Id, decimal? CcmBudgetPeriodAmount):IRequest` append `PeSuggestedPriceFeatures.cs` (same concern PE role-gate setters) + Validator `GreaterThanOrEqualTo(0).When(HasValue)` (budget=0 hợp lệ) + Handler fail-closed Forbidden Admin|CostControl TRƯỚC side-effect (S56 #5) + absolute-set null=clear (gotcha #73) + changelog Header (sibling-consistency; spec min=SaveChanges). (6) Controller `[HttpPatch("{id:guid}/budget/ccm-period")]` + `CcmBudgetPeriodBody` (spec EXPLICIT PATCH dù siblings PUT — follow spec). **Call-site verify:** `new PurchaseEvaluationDetailBundleDto(` repo-wide → EXACTLY 1 (PEFeatures:1100, đã sửa) → positional-safe. Submit-guard `BudgetPeriodAmount` PRO KHÔNG đụng. Build SolutionErp.slnx (6 proj incl 2 test, gotcha #65) **0 warn 0 err**. KHÔNG apply DB/FE/test/commit. Route FE/test mirror: DTO field `ccmBudgetPeriodAmount` camelCase + PATCH `/api/purchase-evaluations/{id}/budget/ccm-period` body `{ ccmBudgetPeriodAmount }`. Tag `[pe-section-b-3col, ccm-budget-period, mig59, one-column-no-table, absolute-set-fail-closed, positional-dto-order, patch-per-spec]`.
- **2026-06-19 (PE chuông báo approver BE — NO migration, 1 edit/1 file, em-main spec deterministic 100% → ACCEPT Case 1):** Tra Sol (Zalo) "không thấy chuông — việc có hồ sơ cần duyệt": approver Cấp hiện tại KHÔNG nhận notify khi phiếu ENTER/ADVANCE tới ChoDuyet (chỉ drafter báo terminal). FIX = +1 block trong `LogTransitionAsync` (PurchaseEvaluationWorkflowService.cs ~line 1058) NGAY SAU drafter-notify, KHÔNG endpoint mới (Pattern 4 — notify = side-effect của Submit/Approve-advance, cả 2 đã gọi LogTransitionAsync + end ChoDuyet). **Resolution mirror EXACT canonical** `EnsureActorInLevel` (line ~301): `db.ApprovalWorkflows.AsNoTracking().Include(Steps).ThenInclude(Levels)``stepsOrdered[CurrentWorkflowStepIndex]` (bounds-check) → `.Levels.Where(Order==CurrentApprovalLevelOrder && ApproverUserId!=Guid.Empty && !=actorUserId).Select(ApproverUserId).Distinct()``NotifyManyAsync(ids, Generic, "Phiếu cần bạn duyệt: {MaPhieu??TenGoiThau}", "Có phiếu Duyệt NCC đang chờ bạn duyệt.", /purchase-evaluations/{Id}, refId:Id, ct)`. **Recon:** (1) `ApprovalWorkflowLevel.ApproverUserId` = non-null `Guid` (NOT Guid?) → "exclude null" = `!=Guid.Empty` defensive (OR-of-N rows mỗi 1 NV). (2) `NotifyManyAsync` sig verified = `(IEnumerable<Guid>, NotificationType, title, string? desc, string? href, Guid? refId, CT)` khớp urgent-feature ref. (3) actor-exclude qua `!= actorUserId` (Guid != Guid? lift OK). **Guard V2-only:** `ApprovalWorkflowId is Guid && CurrentWorkflowStepIndex is int && CurrentApprovalLevelOrder is int` → V1/no-workflow skip. **Best-effort try/catch** nuốt lỗi (phiếu đã SaveChanges; notify fail KHÔNG rollback). Block đọc pointer SAU set → Submit (idx0/lvl1) + Approve-advance (pointer advance rồi LogTransition) đều fire đúng. Build SolutionErp.slnx (2 test project, gotcha #65) **0 warn 0 err**. KHÔNG test/FE/mig/commit. Tag `[pe-approver-bell, notify-many, log-transition-hook, no-mig, v2-only, best-effort-trycatch, pattern4-side-effect]`.