[CLAUDE] PurchaseEvaluation: nut XOA phieu o man duyet + menu 3 muc + man "Da xoa" (dot 2)
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 5m47s

Dong ca UAT goc (Tra Sol): "lo bam sai cai goi thau, quay lai khong duoc, phai
xoa thi no moi ko co luy ke len". Dot 1 (b1bae77) da dung NEN (co F6 + lenh sua
quy trinh tai cho); dot 2 la phan nguoi dung cham vao.

- App: PeSoftDeleteFeatures.cs MOI
  - DELETE /api/purchase-evaluations/{id}/by-approver — 3 rao: phase ChoDuyet
    ^ dung luot actor ^ matchingLevel.AllowApproverDelete (PER-NGUOI, khong g.Any)
  - GET /api/purchase-evaluations/deleted — IgnoreQueryFilters() KEM .Where(IsDeleted)
    (IgnoreQueryFilters GO filter chu khong DAO — thieu Where la liet ke ca phieu song)
    + tai lap khoi IDOR cua list hien hanh
  - ghi vet ChangelogAction.Delete + ly do
- Api: 2 endpoint moi. 🔴 by-approver CO Y KHONG co [Authorize(Policy)] — do tren DB:
  moi vai dang lam nguoi duyet (Procurement/CostControl...) deu CanDelete=0 va khong
  seeder nao nang co do => gan policy la 403 voi dung nguoi can dung, ma FE gate cung
  quyen nen khong thay nut khong thay loi (gotcha #44). Quyen = 3 rao handler; co F6
  la admin tick dich danh tung nguoi => chat hon quyen theo vai.
- Dto: ApprovalWorkflowOptionsDto + AllowApproverDelete. Thieu field nay thi FE gate
  luon sai => nut KHONG BAO GIO hien, ma BE lan FE deu build sach (reviewer bat).
- Domain/Infra: 2 menu key Pe_{code}_Approved / _Deleted (KHONG vao MenuKeys.All =>
  giu 54/216) + seed tree + labelBackfill "Duyet" -> "Dang duyet" + 2 danh sach permission
- FE x2 app: nut Xoa trong PeWorkflowPanel (md5 mirror khop) + dialog ly do + Layout
  regex/route cho 2 muc moi + man "Da xoa" chi-xem
- Tests: +11 (562 tong, 0 fail). T27 khoa CA LOP loi entity-co-co-ma-DTO-khong-co
  (fault-inject xac nhan: go field ra => T27 do ngay)

Review: 12 FLAG, chot chan H1 (DTO thieu field) da va. Deploy KHONG co migration
nhung CO seed menu => phai restart API.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
pqhuy1987
2026-07-27 14:06:14 +07:00
parent 0aa8557650
commit 4464a464c5
15 changed files with 1633 additions and 33 deletions

View File

@ -1874,7 +1874,13 @@ public static class DbInitializer
tree.Add((MenuKeys.PurchaseEvaluationWorkflowView(code), "Luồng duyệt", MenuKeys.PurchaseEvaluationGroup(code), peOrder++, "Network"));
tree.Add((MenuKeys.PurchaseEvaluationList(code), "Danh sách", MenuKeys.PurchaseEvaluationGroup(code), peOrder++, "List"));
tree.Add((MenuKeys.PurchaseEvaluationCreate(code), "Thao tác", MenuKeys.PurchaseEvaluationGroup(code), peOrder++, "Plus"));
tree.Add((MenuKeys.PurchaseEvaluationPending(code), "Duyệt", MenuKeys.PurchaseEvaluationGroup(code), peOrder++, "CheckCircle2"));
// [S155 đợt 2] Nhãn ở đây CHỈ có tác dụng cho DB TRỐNG. Với DB đang chạy, upsert
// `:1893-1905` gặp key đã tồn tại thì chỉ update `Order` rồi `continue` — KHÔNG
// đụng Label ⇒ rename "Duyệt" → "Đang duyệt" phải qua `labelBackfill` bên dưới.
tree.Add((MenuKeys.PurchaseEvaluationPending(code), "Đang duyệt", MenuKeys.PurchaseEvaluationGroup(code), peOrder++, "CheckCircle2"));
// 2 mục MỚI (owner chốt 2026-07-27) — key mới ⇒ đi nhánh INSERT bình thường.
tree.Add((MenuKeys.PurchaseEvaluationApproved(code), "Đã duyệt", MenuKeys.PurchaseEvaluationGroup(code), peOrder++, "CheckCheck"));
tree.Add((MenuKeys.PurchaseEvaluationDeleted(code), "Đã xóa", MenuKeys.PurchaseEvaluationGroup(code), peOrder++, "Trash2"));
}
// PE workflow admin leaves dưới `PeWorkflows`
@ -1921,6 +1927,11 @@ public static class DbInitializer
[MenuKeys.PurchaseEvaluationGroup("DuyetNccPhuongAn")] = "Duyệt NCC và Giải pháp",
[MenuKeys.PeWorkflowTypeLeaf("DuyetNcc")] = "Duyệt NCC",
[MenuKeys.PeWorkflowTypeLeaf("DuyetNccPhuongAn")] = "Duyệt NCC và Giải pháp",
// [S155 đợt 2] 🔴 Rename "Duyệt" → "Đang duyệt" (owner, ảnh menu 2026-07-27).
// BẮT BUỘC đi qua đây: sửa chữ trong `tree.Add(...)` là VÔ TÁC DỤNG trên DB đang
// chạy vì upsert `:1893-1905` chỉ update `Order` cho key đã tồn tại.
[MenuKeys.PurchaseEvaluationPending("DuyetNcc")] = "Đang duyệt",
[MenuKeys.PurchaseEvaluationPending("DuyetNccPhuongAn")] = "Đang duyệt",
};
var updatedLabels = 0;
foreach (var (key, expectedLabel) in labelBackfill)
@ -2094,6 +2105,10 @@ public static class DbInitializer
peKeys.Add(MenuKeys.PurchaseEvaluationList(typeCode));
peKeys.Add(MenuKeys.PurchaseEvaluationCreate(typeCode));
peKeys.Add(MenuKeys.PurchaseEvaluationPending(typeCode));
// [S155 đợt 2] 2 mục menu mới — phải có mặt ở CẢ HAI danh sách permission
// (đây + `SeedPurchaseEvaluationPermissionDefaultsAsync`), quên 1 bên = quyền lệch.
peKeys.Add(MenuKeys.PurchaseEvaluationApproved(typeCode));
peKeys.Add(MenuKeys.PurchaseEvaluationDeleted(typeCode));
}
var reviewKeys = MenuKeys.All.Where(InReviewScope)
@ -2494,6 +2509,10 @@ public static class DbInitializer
menuKeys.Add(MenuKeys.PurchaseEvaluationList(typeCode));
menuKeys.Add(MenuKeys.PurchaseEvaluationCreate(typeCode));
menuKeys.Add(MenuKeys.PurchaseEvaluationPending(typeCode));
// [S155 đợt 2] 2 mục menu mới ("Đã duyệt" / "Đã xóa") — mirror danh sách
// `peKeys` trong `UpgradeReviewModulePermissionsForAllRolesAsync` (~:2092).
menuKeys.Add(MenuKeys.PurchaseEvaluationApproved(typeCode));
menuKeys.Add(MenuKeys.PurchaseEvaluationDeleted(typeCode));
}
int added = 0;