# 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 \"<forward phase label>\""}` — 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=<new>` 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).
