# SPEC — PE "Ý kiến cấp duyệt" save-advance + creator-as-approver + reject-return — 12-07-2026

> Engine-đắt `/fable-clone vai invest/review` (H21 LỆNH-B) — anh-directed S112 (đề-bài từ 2 screenshot chat FDC anh Kiệt). Opus 4.8 MAX, RUN-TRACE. Lead ghi sau verify. Worker nhận spec qua prompt.

## ① Đề-bài (ĐIỀU TRA + THIẾT KẾ, KHÔNG code lượt này)

Yêu cầu từ **anh Kiệt / PRO** (chat FDC 11-12/7): đổi luồng **"Ý kiến cấp duyệt (SIGN-OFF THEO WORKFLOW)"** của module **PurchaseEvaluation V2**.

**Hiện tại:** save = **đẩy thẳng sang cấp trên** ngay ("Hiện tại save là đẩy sang cấp trên"). Tức save của thao-tác duyệt = comment + advance `CurrentApprovalLevelOrder` cùng lúc.

**Mong muốn (3 điểm):**
1. **Người tạo phiếu (PRO) CŨNG là người duyệt Cấp-1 (Bước-1)** — "người duyệt cũng là người tạo luôn". Tạo phiếu xong → comment vào **ô duyệt của CHÍNH họ** (LevelOpinion cấp mình) → rồi save.
2. **Save mới đẩy sang cấp trên** — "tạo phiếu xong cho comment vào chỗ ô duyệt rồi save chuyển qua bên cấp trên" / "Rồi họ save thì đẩy sang". Giữ semantics comment-in-own-box → save → advance.
3. **Reject / Trả lại** → "Chỗ Trả phải rejected trả về để bạn comment. Rồi đi tiếp" = khi trả lại, phiếu về đúng người đó để **comment lại**, rồi **đi tiếp forward** lại.

**Bằng chứng (screenshot):** BƯỚC 1 Phòng Cung ứng (Cấp 1 Bình Le Thanh comment ngân-sách chi-tiết + Cấp 2 Bùi Lê Thúy Trà "duyệt — không ý kiến") → BƯỚC 2 Phòng KSCP (Cấp 1 Lê Văn Bình chi-tiết + Cấp 2 Phan Văn Chương "ok") → BƯỚC 3 BGĐ (chưa duyệt). Mỗi cấp có ô ý-kiến + trạng-thái "Đã duyệt".

## ② Cách điều tra (3 lăng-kính investigator-codebase same-role + reviewer verify)

- **Lane A — LUỒNG HIỆN TẠI:** `PurchaseEvaluationWorkflowService.cs` (Infrastructure/Services) `ApproveV2Async` — đọc kỹ: comment UPSERT `PurchaseEvaluationLevelOpinion` + advance `CurrentApprovalLevelOrder` xảy ra thế nào, ai là approver cấp-1 (`ApprovalWorkflow.cs` V2 Steps>Levels>ApproverUserId), creator=approver hiện chạy được chưa. PE create luồng (`PeWorkspaceCreateView.tsx`) → sau tạo phiếu đi đâu (Phase nào, cấp nào). `PurchaseEvaluation.cs` (CurrentApprovalLevelOrder, Phase, CreatedBy), `PurchaseEvaluationPhase.cs` (states: Nháp/Đã gửi/TraLai=98/Từ chối/Đã duyệt). → sơ đồ luồng hiện tại (file:line) + ĐIỂM CHẶN cho yêu cầu.
- **Lane B — THIẾT KẾ THAY ĐỔI:** cần đổi gì ở BE `ApproveV2Async` + FE `PeWorkflowPanel.tsx` (ô duyệt + nút save) để creator comment ô duyệt cấp mình → save → advance. Câu hỏi cốt: "save = advance" giữ nguyên hay cần TÁCH "lưu comment (nháp, chưa advance)" vs "save-advance (submit lên cấp trên)"? SignedByUserId/actor==ApproverUserId gate ảnh hưởng gì? Governance flag: PRO tự duyệt cấp-1 rồi lên CCM/CEO — self-approval control-issue hay bước-đề-xuất hợp-lệ (creator=Cấp-1 = proposal, cấp trên mới là approval thật)? → bảng thay đổi BE+FE (file:line).
- **Lane C — REJECT / TRẢ-LẠI:** TraLai (Phase 98) hiện xử thế nào — trả về ai (creator? cấp trước?), comment cũ giữ/xóa, re-comment được không, "đi tiếp" lại từ cấp nào. Yêu cầu: reject → về commenter → re-comment → forward lại. Cần đổi gì (BE reject handler + FE). Edge: multi-cấp OR-of-N cùng cấp, reject giữa chừng nhiều cấp, comment đã tồn tại khi bị trả lại. → luồng reject hiện tại vs mong muốn + ≥3 edge.

## ③ Checklist (deliverable ĐO ĐƯỢC)

- [ ] **Lane A** — sơ đồ create→sign-off→advance hiện tại (file:line) + creator-vs-approver identity + điểm chặn yêu cầu.
- [ ] **Lane B** — bảng thay đổi BE + FE (file:line) + quyết "save=advance vs tách save-nháp/submit" + flag self-approval governance.
- [ ] **Lane C** — luồng reject hiện tại vs mong muốn (file:line) + thay đổi + ≥3 edge multi-cấp/reject.
- [ ] **Reviewer** — verify 3 lane khớp code thật + state-machine đúng + risk (self-approve, reject-loop vô-hạn, multi-cấp OR-of-N, comment-mất-khi-reject) + PASS/FAIL + gap file:line.
- [ ] **em-main** — design synthesis + option cho anh quyết + agent-memory (invest + reviewer stamp `[engine: fable-clone-ensemble · Opus-S112]`).
