# Session 2026-05-07 (S15) — Tooltip diagnose "Lưu & Gửi Duyệt" + drastic refactor DEFER **Dev:** Claude **Duration:** ~1h **Base commit:** `7c83ac8` (sau Session 14 wrap-up docs) **Final commit:** `835cc7f` (+ working tree refactor revert) **Total commits:** 1 ## Bối cảnh User UAT live screenshot phiếu PE Bản nháp "huy test 1" (PE/2026/A/004) trên `eoffice.solutions.com.vn/purchase-evaluations/workspace?type=1&id=...`. Báo button "Lưu & Gửi Duyệt" KHÔNG hoạt động + suy đoán "Hình nó đang bị trùng ID với thằng khác? Mỗi lần tạo mới Phiếu Duyệt NCC là 1 phiếu độc lập nhé". ## Phần 1 — Diagnose tooltip (commit `835cc7f`) ### Root cause analysis `PeDetailTabs.tsx` line 220: `disabled={!canSubmitForApproval || submitForApproval.isPending}`. Khi `canSubmitForApproval=false` button visually clickable nhưng không fire onClick → user không biết tại sao silent. `canSubmitForApproval = mode === 'workspace' && canEditPhase && !readOnly && evaluation.workflow.nextPhases.some(p => p !== TuChoi && p !== TraLai)`. Hypothesis: phiếu pin `WorkflowDefinitionId` của 1 version cấu hình thiếu adjacent step → `policy.NextPhasesFrom(DangSoanThao)` empty (legacy data hoặc admin chưa setup workflow). ### Fix UX (PeDetailTabs.tsx, fe-admin + fe-user mirror) ```javascript const forwardPhase = evaluation.workflow.nextPhases.find(p => p !== TuChoi && p !== TraLai) const canSubmitForApproval = mode === 'workspace' && canEditPhase && !readOnly && forwardPhase != null const submitDisabledReason = !canEditPhase ? `Phiếu đã ở phase X — chỉ Bản nháp / Trả lại mới sửa + gửi được.` : readOnly ? 'Chế độ chỉ đọc.' : !forwardPhase ? `Workflow không có phase tiếp theo từ X. Liên hệ admin kiểm tra cấu hình quy trình.` : null ``` Button: - `title={submitDisabledReason ?? "Gửi phiếu sang \"\""}` — hover tooltip show reason hoặc forward phase - Dialog confirm thay text generic "Gửi phiếu vào quy trình duyệt?" → explicit "Sẽ chuyển sang \"Chờ Purchasing\". Sau khi gửi sẽ KHÔNG sửa được nữa (trừ khi approver Trả lại)." Mirror fe-admin + fe-user. KHÔNG đụng BE. Build pass cả 2 app. ### "Trùng ID" KHÔNG phải bug FE `PurchaseEvaluationWorkspacePage.tsx` URL state đúng: - `+ Thêm mới` → `setParams({ mode: 'new', id: null })` - POST tạo phiếu → BE gen new GUID + MaPhieu mới (atomic SERIALIZABLE qua `PurchaseEvaluationCodeGenerator`) - Save → URL set `id=` thay thế Mỗi PE row unique GUID + MaPhieu (UNIQUE filtered). Không có path share ID. User suy đoán có thể do button silent disabled → tưởng load lại record cũ. ## Phần 2 — Plan drastic refactor flat workflow → DEFER User chốt drastic refactor: "bỏ phase enum hoàn toàn, dùng ChoDuyet=10 đơn nhất + currentStepIndex tracking". ### Spec mới (theo user) Workflow = list flat (Phòng × Cấp × Users[]). Ví dụ Quy trình A: - Phòng A (2 cấp): cấp 1 NV A, cấp 2 NV B - Phòng B (3 cấp): cấp 1 NV C, cấp 2 [NV D 1, D 2, D 3], cấp 3 NV E - Phòng C (1 cấp): NV C Rules: - Phòng 1 cấp → 1 user duyệt = pass phòng đó - Phòng N cấp → tuần tự cấp 1 → 2 → ... → N - Cùng cấp + cùng phòng = OR (1 user duyệt = pass cấp) - Hết tất cả phòng → DaDuyet - PE pin Quy trình ID, không thay đổi giữa chừng ### Plan refactor 6 chunk (theo `feedback_per_chunk_commit.md`) | Chunk | Scope | Estimate | |---|---|---| | A | Domain enum + entity changes + Migration 21 | 50min | | B | App CQRS + Policy registry rewrite | 45min | | C | PE Service rewrite TransitionAsync flat | 2-3h | | D | Contract Service rewrite + Tests update (drop 12 N-stage tests) | 1.5h | | E | FE Designer flat UI (PeWorkflowsPage + WorkflowsPage) | 1.5h | | F | FE PeWorkflowPanel + Docs | 1h | **Tổng realistic:** ~8-10h focused. Vượt session boundary + risk session deep ~30 commits. ### Attempt → REVERT Edit working tree 12 file (Chunk A partial): - `PurchaseEvaluationPhase.cs`: + ChoDuyet=10 enum value - `ContractPhase.cs`: + ChoDuyet=10 - `PurchaseEvaluation.cs`: + CurrentWorkflowStepIndex, RejectedAtStepIndex - `Contract.cs`: + same 2 fields - `WorkflowDefinition.cs` (Contract): drop class WorkflowStepInnerStep + nav, WorkflowStep + DepartmentId, PositionLevel - `PurchaseEvaluationWorkflowDefinition.cs`: same pattern - `ContractDepartmentApproval.cs`: drop InnerStepId - `PurchaseEvaluationDepartmentApproval.cs`: drop InnerStepId - `WorkflowDefinitionConfiguration.cs`: drop InnerStep config + add DeptId/PositionLevel cấu hình + FK Restrict - `PurchaseEvaluationConfiguration.cs`: same - `DepartmentApprovalsConfiguration.cs`: drop InnerStepId FK + filtered indexes, restore simple unique non-filtered - `ApplicationDbContext.cs`: drop DbSet<*WorkflowStepInnerStep> × 2 Realize codebase chưa compile (Service + App + Tests dùng `WorkflowStepInnerStep` đã drop). Need rewrite ALL dependent code trong cùng Chunk A → blow scope vượt session. **Decision REVERT** working tree về `835cc7f` clean. Tests 96 pass intact. ### Memory entry mới `feedback_drastic_refactor_scope.md` — decision rule: drastic refactor cần dedicated session với context fresh, scope estimation conservative (2x buffer), tránh mid-session big refactor (risk session context deep + breaking states giữa chunk + tests rewrite biggest risk). ## Verify - ✅ `git restore` 12 files reverted - ✅ `dotnet test` 96 pass (54 + 42) — state intact - ✅ working tree clean (chỉ 2 untracked .zip backup files) - ✅ commit `835cc7f` (tooltip) pushed gitea OK ## Stats cumulative (sau Session 15) | | Trước S15 | Sau S15 | Diff | |---|---:|---:|---:| | BE LOC | ~15800 | ~15800 | 0 (FE-only commit + revert) | | Migrations | 20 | 20 | 0 | | DB tables | 57 | 57 | 0 | | FE pages | 32 | 32 | 0 | | Tests | 96 | 96 | 0 | | Docs | ~58 | ~59 | +1 (session log này) | | Memory entries | 11 | **12** | +1 (drastic refactor scope) | | Commits S15 | — | **+1** | `835cc7f` | ## Plan organization sau Session 15 ``` Plan cha: Phase 9 active — UAT ├── Plan con A-S14: Tất cả ✅ DONE (xem session logs trước) ├── Plan con S15: Tooltip diagnose + drastic refactor DEFER │ ├── ✅ FE PE button "Lưu & Gửi Duyệt" tooltip + dialog reason rõ │ └── ⏸ Drastic refactor flat workflow — DEFER session sau (~8-10h) └── Plan con Defer S16+: ├── Drastic refactor flat workflow (Mig 21, ~8-10h, dedicated session) │ OR fallback Approach Y (FE flat UI giới hạn 5 phòng, 1-2h) ├── Task 2 sample data seed N-stage (block DesignTime vs Runtime DB) ├── Budget N-stage (cần migration `AddBudgetVersionedWorkflow`) ├── schema-diagram §17-19 Mig 18-20 (defer cron audit 2026-06-01) └── Hard blockers Ops (UAT, SMTP, Rotate creds, SQL backup, Remove huypham.vn, win-acme) ``` ## Handoff UAT iteration mode. Phiếu PE Bản nháp giờ có tooltip rõ reason khi không gửi được. User test hover → biết admin cần kiểm tra cấu hình workflow definition. Drastic refactor pending dedicated session. Khi resume: 1. Fresh session start với context clean 2. Plan kỹ 6 chunk per-commit 3. Buffer 2x estimate (~16h thực tế nếu phát sinh) 4. Test rewrite biggest risk — pattern reusable nhưng nội dung phải mới hoàn toàn **Cron audit kế:** 2026-06-01 (~25 ngày).