From 7c83ac8f17d17cc193d2f20fa2ae57f95ec19e51 Mon Sep 17 00:00:00 2001 From: pqhuy1987 Date: Thu, 7 May 2026 19:40:00 +0700 Subject: [PATCH] =?UTF-8?q?[CLAUDE]=20Docs+Skill:=20ch=E1=BB=91t=20Session?= =?UTF-8?q?=2014=20wrap-up=20=E2=80=94=20PE=203-button=20workflow=20+=20Ta?= =?UTF-8?q?sk=202=20defer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Session 14 (2026-05-07) docs/skill update: STATUS.md: - Last updated + Phase summary count (95→96 test, 20 mig, 57 bảng, 41 gotcha) - Recently Done row Session 14 chi tiết (3-button + Task 2 in-progress + DesignTime/Runtime DB gotcha) HANDOFF.md: - TL;DR Session 14 prepend với 1 commit + Task 2 defer - 5 cảnh báo Session 15+: TraLai phase orphan / Task 2 sample seed / DesignTime vs Runtime DB / Budget N-stage defer / schema-diagram §17-19 defer migration-todos.md: Phase 9 + Session 14 block 4 sub-task done + 2 defer task Session log NEW `2026-05-07-2500-3-button-workflow.md`: - Bối cảnh + spec 3-button (Duyệt/Trả lại/Từ chối) + implementation chi tiết (Domain policy expand + Service tách reject + FE button + dialog warning) - Tests update (95→96 với +1 NEW Reject_To_TuChoi_Locks_Permanently) - Task 2 in-progress: DesignTime vs Runtime DB gotcha + API exit 255 sớm - Plan organization sau S14 Skill ef-core-migration: - description + heading: 17→20 migration - Bảng migration history thêm Mig 18-19 (PE) + Mig 20 (Contract) - Section MỚI "N-stage workflow pattern (Mig 18-20)" — architecture decision với filtered unique trick + per-module migration packaging guideline - Phase 8 update: 83→96 test breakdown Skill contract-workflow: - Section MỚI "Phase 9+ done (Mig 18-20 — Session 12/13/14)": * N-stage workflow PE + Contract (entity + filtered unique split + service refactor + tests + FE Designer + UsersPage cấp + API) * PE 3-button approval (Duyệt/Trả lại/Từ chối) Session 14 * Defer: Budget N-stage / Phase TraLai=98 orphan CLAUDE.md root: 16→20 migration + 55→57 bảng + 83→96 test docs/rules.md §7: Phase 9 active 83→96 test Verify: dotnet test 96 pass + npm build (skip — pure docs/skill update). 🎉 Session 14 wrap-up complete. Pushed 1 task (3-button) + Task 2 defer. Cumulative since session start (13h17): 13 commit (1 button removal + 6 PE N-stage Chunk A-F + 5 Contract N-stage Chunk A,B,C,D,F + 1 3-button). Co-Authored-By: Claude Opus 4.7 (1M context) --- .claude/skills/contract-workflow/SKILL.md | 48 ++++++ .claude/skills/ef-core-migration/SKILL.md | 29 +++- CLAUDE.md | 4 +- docs/HANDOFF.md | 59 ++++++- docs/STATUS.md | 5 +- docs/changelog/migration-todos.md | 16 ++ .../2026-05-07-2500-3-button-workflow.md | 150 ++++++++++++++++++ docs/rules.md | 2 +- 8 files changed, 303 insertions(+), 10 deletions(-) create mode 100644 docs/changelog/sessions/2026-05-07-2500-3-button-workflow.md diff --git a/.claude/skills/contract-workflow/SKILL.md b/.claude/skills/contract-workflow/SKILL.md index 0e2661a..b276212 100644 --- a/.claude/skills/contract-workflow/SKILL.md +++ b/.claude/skills/contract-workflow/SKILL.md @@ -314,6 +314,54 @@ POST /api/contracts/{id}/transitions { targetPhase: 3, decision: 1, comment: ".. - [x] **Lock edit guards** 17 handler khi Phase != DangSoanThao - [x] **CanBypassReview toggle** per User — admin UI + audit IsBypassed=true +## Phase 9+ done (Mig 18-20 — Session 12/13/14) + +### N-stage workflow (PE Mig 18-19 + Contract Mig 20) + +- [x] **N-stage approval** Phòng × PositionLevel (NV/PP/TP) cấu hình động per + WorkflowStep cha. Mỗi inner step = 1 cấp duyệt (Order asc, sequential). +- [x] **PositionLevel enum** Domain/Identity (NV=1, PP=2, TP=3) + `User.PositionLevel int?` +- [x] **InnerStep entity** + ALTER `*DepartmentApproval.InnerStepId Guid?` per module +- [x] **Filtered unique split** (Mig 19 cho PE / gộp Mig 20 cho Contract): + legacy `WHERE InnerStepId IS NULL` (Stage Review/Confirm) + N-stage + `WHERE InnerStepId IS NOT NULL` (per inner step) +- [x] **Service refactor** TransitionAsync — load InnerSteps eager + reject + branch clear N-stage rows + dept approval block split: hasInnerSteps→N-stage + logic / else→legacy 2-stage. Match firstPending Order asc + (exact level + OR canBypass + level≥). Bypass batch upsert NV+PP+TP cùng dept ≤ actor. +- [x] **6 test PE N-stage** + **6 test Contract N-stage** (`PeNStageApprovalTests`, + `ContractNStageApprovalTests`). Pattern reusable. +- [x] **FE Designer** (PeWorkflowsPage + WorkflowsPage) sub-section "Cấp duyệt nhỏ + trong phòng" drag-list { Phòng × Cấp + required } +- [x] **UsersPage cột Cấp** + cycle button null→1→2→3→null +- [x] **API** `PATCH /users/{id}/position-level` + +**Backward compat 100%:** workflow no InnerSteps → service fallback legacy 2-stage +Mig 16. Data legacy InnerStepId=null vẫn enforce unique cũ qua filtered index. + +### PE 3-button approval (Session 14 — `0d77698`) + +UI distinguishment 3 hành động cho approver: +- **Duyệt** = forward (decision=Approve) +- **Trả lại** = về DangSoanThao + Drafter sửa (decision=Reject + target=DangSoanThao + → smart reject pattern Mig 16 + clear N-stage rows + jump-back) +- **Từ chối** = Phase=TuChoi (decision=Reject + target=TuChoi → phiếu khoá + vĩnh viễn 17 handler Mig 16 lock edit, Drafter phải tạo phiếu mới) + +**Domain policy expand:** NccOnly + NccWithPlan + FromDefinition thêm `(X → TuChoi)` +transition cho mọi phase trung gian (trước chỉ DangSoanThao→TuChoi). + +**Service Reject branch tách 2 case:** +- target=TuChoi → giữ nguyên (KHÔNG override + KHÔNG set RejectedFromPhase + KHÔNG clear N-stage) +- target khác (DangSoanThao) → smart reject (force DangSoanThao + RejectedFromPhase + clear N-stage) + +### Defer + +- Budget N-stage — cần migration `AddBudgetVersionedWorkflow` trước (Budget + hardcoded `BudgetPolicy.Default`, chưa có versioned WorkflowDefinition). +- Phase TraLai = 98 (Domain enum từ S11+++++++) — orphan, KHÔNG wire (user + Session 14 chốt không cần phase trung gian). + ## Tier 4+ (còn thiếu / future) - [ ] Warning notification 20% SLA (`SlaWarningSent` flag đã có) diff --git a/.claude/skills/ef-core-migration/SKILL.md b/.claude/skills/ef-core-migration/SKILL.md index 9a2c7bd..b2847d0 100644 --- a/.claude/skills/ef-core-migration/SKILL.md +++ b/.claude/skills/ef-core-migration/SKILL.md @@ -1,6 +1,6 @@ --- name: ef-core-migration -description: Tạo/sửa/revert EF Core 10 migration cho SOLUTION_ERP. Dùng khi thêm entity mới, thay đổi schema, rollback migration, debug DesignTimeDbContextFactory fail. Đã có 16 migration sẵn (Init → AddTwoStageDeptApprovalAndSmartReject). Snapshot + Designer + Migration 3-file rule bắt buộc commit đủ. +description: Tạo/sửa/revert EF Core 10 migration cho SOLUTION_ERP. Dùng khi thêm entity mới, thay đổi schema, rollback migration, debug DesignTimeDbContextFactory fail. Đã có 20 migration sẵn (Init → AddContractWorkflowInnerStepsAndAlterDeptApprovalUnique). Snapshot + Designer + Migration 3-file rule bắt buộc commit đủ. when-to-use: - "thêm migration" - "EF Core migration" @@ -16,7 +16,7 @@ when-to-use: > **Context:** .NET 10 + EF Core 10 + SQL Server. DbContext: `ApplicationDbContext` ở `Infrastructure/Persistence/`. Startup: `SolutionErp.Api`. -## Migration history (17 migration hiện có) +## Migration history (20 migration hiện có) | # | Name | Tables added / changed | |---|---|---| @@ -37,8 +37,29 @@ when-to-use: | **15** | **`AddPurchaseEvaluationDepartmentOpinions`** | **1 bảng `PurchaseEvaluationDepartmentOpinions` (UNIQUE PEId+Kind, max 1 row mỗi PeDepartmentKind per phiếu — Phê duyệt/Ccm/MuaHang/SmPm)** | | **16** | **`AddTwoStageDeptApprovalAndSmartReject`** | **3 bảng `Contract/PurchaseEvaluation/Budget DepartmentApprovals` (UNIQUE TargetId+Phase+Dept+Stage cho 2-stage NV.Review → TPB.Confirm per phòng × phase) + 4 ALTER (`Users.CanBypassReview` bit cho NV bypass + 3 `RejectedFromPhase` int cho smart reject jump-back). Phase 9 — đóng bug "NV duyệt được hết phase" anh Kiệt (FDC) báo. Logic 2-stage trong PurchaseEvaluationWorkflowService chỉ áp PE; HĐ + Budget defer.** | | **17** | **`AddManualBudgetFieldsToPeAndContract`** | **4 ALTER (PE + HĐ × `BudgetManualName` nvarchar(200) + `BudgetManualAmount` decimal(18,2)) — manual budget fallback khi user không link Budget entity approved. KHÔNG XOR với BudgetId, cả 2 cùng null OK. Carry-forward `pe.BudgetManualName/Amount → contract` ở `CreateContractFromEvaluation`. Phase 9 — Session 11 (2026-05-07).** | +| **18** | **`AddPeWorkflowInnerStepsAndPositionLevel`** | **N-stage workflow PE — 1 CREATE TABLE `PurchaseEvaluationWorkflowStepInnerSteps` (Order, DepartmentId, PositionLevel, Name, SlaDays, IsRequired) + 2 ALTER (`Users.PositionLevel int?` 1=NV/2=PP/3=TP + `PEDeptApproval.InnerStepId Guid?`) + 3 IX + FK Cascade Step / Restrict Dept+InnerStep. Phase 9+ — Session 12 (2026-05-07).** | +| **19** | **`AlterPeDeptApprovalsUniqueFilteredForInnerSteps`** | **Filtered unique split: drop UNIQUE cũ Mig 16 → 2 filtered: legacy `WHERE InnerStepId IS NULL` (Stage Review/Confirm) + N-stage `WHERE InnerStepId IS NOT NULL` (per inner step). Tránh conflict 2 inner step cùng dept Stage=Confirm. Session 12.** | +| **20** | **`AddContractWorkflowInnerStepsAndAlterDeptApprovalUnique`** | **N-stage workflow Contract mirror PE Mig 18+19 — GỘP 1 migration: CREATE TABLE `WorkflowStepInnerSteps` + ALTER `ContractDeptApproval.InnerStepId` + DropIndex old + Recreate filtered legacy/N-stage + 3 IX + FK. Session 13 (2026-05-07).** | -Total: **55 bảng** dbo + `__EFMigrationsHistory` (Mig 17 không thêm bảng, chỉ +4 cột). Xem `docs/database/schema-diagram.md` ERD đầy đủ. +Total: **57 bảng** dbo + `__EFMigrationsHistory` (Mig 17 alter cột; Mig 18 + Mig 20 thêm 1 bảng/mỗi; Mig 19 chỉ alter index). Xem `docs/database/schema-diagram.md` ERD đầy đủ. + +## N-stage workflow pattern (Mig 18-20 — Session 12-13) + +Architecture decision đáng ghi cho session sau: + +**Pattern:** Mỗi WorkflowStep cha (= 1 phase) cấu hình động chuỗi InnerSteps con theo Department × PositionLevel với Order sequential. Approver cần khớp DeptId + PositionLevel + Order tiếp theo chưa duyệt. CanBypassReview cho user level cao hơn skip cấp dưới cùng dept. + +**Per-module migration** (PE first, mirror sang Contract sau): +- PE: 2 migration tách (Mig 18 schema + Mig 19 alter index) — vì index issue phát hiện sau khi schema đã commit +- Contract: 1 migration GỘP (Mig 20) — gộp CREATE TABLE + ALTER + DropIndex + Recreate filtered. Cleaner cho mirror. + +**Filtered unique trick:** legacy 2-stage rows (Mig 16, Stage=Review/Confirm) + N-stage rows (per InnerStep) cùng table `*DepartmentApprovals` → cần unique split: +- `UX_*_Phase_Dept_Stage WHERE InnerStepId IS NULL` (legacy, prevent duplicate Review/Confirm cùng phase × dept) +- `UX_*_Phase_InnerStep WHERE InnerStepId IS NOT NULL` (N-stage, prevent duplicate per inner step) + +**Backward compat 100%:** workflow no InnerSteps configured → service fallback legacy 2-stage logic Mig 16. Data legacy InnerStepId=null vẫn enforce unique cũ qua filtered index. + +**Budget defer:** Budget chưa có versioned WorkflowDefinition entity (hardcoded `BudgetPolicy.Default`). Để mirror N-stage Budget cần migration `AddBudgetVersionedWorkflow` trước (4 bảng + ALTER `Budget.WorkflowDefinitionId?`). Defer cho user quyết riêng. **Phase 7 pending:** - `AddPePaymentTermFields` — tách `PurchaseEvaluations.PaymentTerms` JSON thành 6 column riêng (Session 11+++++ đã thay UI Workspace `