[CLAUDE] PurchaseEvaluation: khóa sửa giá đề xuất PRO/CCM sau khi phiếu đã gửi duyệt (anh Kiệt FDC "duyệt là fix")
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 5m25s
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 5m25s
Bug (anh Kiệt/team, screenshot): drafter=PRO vẫn điền được giá đề xuất Min/Max SAU khi cấp trên (chị Trà) đã duyệt — "duyệt là fix". Root cause: UpdatePeSuggestedPricePro/CcmCommandHandler cố ý KHÔNG ràng Phase (Mig 54 "chỉnh như tài liệu sống") → sửa được mọi phase kể cả DaDuyet. Fix: - BE: +phase-guard — non-admin chỉ sửa giá đề xuất PRO/CCM khi phiếu ở Nháp (DangSoanThao) / Trả lại (TraLai); đã gửi duyệt (ChoDuyet) / đã duyệt (DaDuyet) / từ chối → ConflictException. Admin override (sửa-sai). Role-gate vẫn chạy trước. - FE (2 app): SuggestedPriceRows gate canEditPro/Ccm thêm && isEditablePhase(ev.phase) → ẩn ô sửa (read-only) khi phiếu đã gửi duyệt. Đồng bộ BE. - Test: +4 (PRO ChoDuyet->Conflict no-mutate, PRO DaDuyet->Conflict, Admin DaDuyet->override-set, CCM DaDuyet->Conflict); sửa SeedPeAsync default->DangSoanThao (spec change). 436->440 PASS. Delete phiếu sau duyệt: ĐÃ chặn sẵn (FE nút Xóa chỉ hiện DangSoanThao + BE delete-guard block non-draft) — không cần sửa. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -52,13 +52,25 @@ public class UpdatePeSuggestedPriceProCommandHandler(
|
||||
?? throw new NotFoundException("PurchaseEvaluation", request.PeId);
|
||||
|
||||
// Fail-closed TRƯỚC mọi side-effect.
|
||||
if (!currentUser.Roles.Contains(AppRoles.Admin)
|
||||
&& !currentUser.Roles.Contains(AppRoles.Procurement))
|
||||
var isAdmin = currentUser.Roles.Contains(AppRoles.Admin);
|
||||
if (!isAdmin && !currentUser.Roles.Contains(AppRoles.Procurement))
|
||||
{
|
||||
throw new ForbiddenException(
|
||||
"Chỉ Phòng Cung ứng (PRO) hoặc Admin được nhập giá đề xuất PRO (Min/Max).");
|
||||
}
|
||||
|
||||
// [S96 2026-07-01 — anh Kiệt FDC "duyệt là fix"] Chỉ sửa giá đề xuất khi phiếu CÒN
|
||||
// soạn/trả-lại. Phiếu ĐÃ GỬI DUYỆT (ChoDuyet/sub-phase) / ĐÃ DUYỆT (DaDuyet) / TỪ CHỐI
|
||||
// → KHÓA: giá đã trình cho cấp duyệt xem, KHÔNG đổi ngầm dưới chân họ. Đảo comment cũ
|
||||
// "KHÔNG ràng Phase" (Mig 54). Admin override (sửa-sai). Non-admin fail-closed.
|
||||
if (!isAdmin
|
||||
&& pe.Phase != PurchaseEvaluationPhase.DangSoanThao
|
||||
&& pe.Phase != PurchaseEvaluationPhase.TraLai)
|
||||
{
|
||||
throw new ConflictException(
|
||||
"Phiếu đã gửi duyệt / đã duyệt — không sửa được giá đề xuất PRO. Chỉ sửa khi phiếu ở Nháp hoặc Trả lại.");
|
||||
}
|
||||
|
||||
var oldMin = pe.ProSuggestedMinPrice;
|
||||
var oldMax = pe.ProSuggestedMaxPrice;
|
||||
var oldNote = pe.ProSuggestedPriceNote;
|
||||
@ -114,13 +126,23 @@ public class UpdatePeSuggestedPriceCcmCommandHandler(
|
||||
var pe = await db.PurchaseEvaluations.FirstOrDefaultAsync(x => x.Id == request.PeId, ct)
|
||||
?? throw new NotFoundException("PurchaseEvaluation", request.PeId);
|
||||
|
||||
if (!currentUser.Roles.Contains(AppRoles.Admin)
|
||||
&& !currentUser.Roles.Contains(AppRoles.CostControl))
|
||||
var isAdmin = currentUser.Roles.Contains(AppRoles.Admin);
|
||||
if (!isAdmin && !currentUser.Roles.Contains(AppRoles.CostControl))
|
||||
{
|
||||
throw new ForbiddenException(
|
||||
"Chỉ Phòng Kiểm soát Chi phí (CCM) hoặc Admin được nhập giá đề xuất CCM.");
|
||||
}
|
||||
|
||||
// [S96 2026-07-01 — anh Kiệt FDC "duyệt là fix"] Chỉ sửa giá CCM khi phiếu CÒN soạn/
|
||||
// trả-lại — đã gửi duyệt / đã duyệt / từ chối → KHÓA. Admin override. (mirror PRO handler)
|
||||
if (!isAdmin
|
||||
&& pe.Phase != PurchaseEvaluationPhase.DangSoanThao
|
||||
&& pe.Phase != PurchaseEvaluationPhase.TraLai)
|
||||
{
|
||||
throw new ConflictException(
|
||||
"Phiếu đã gửi duyệt / đã duyệt — không sửa được giá đề xuất CCM. Chỉ sửa khi phiếu ở Nháp hoặc Trả lại.");
|
||||
}
|
||||
|
||||
var oldCcm = pe.CcmSuggestedPrice;
|
||||
var oldNote = pe.CcmSuggestedPriceNote;
|
||||
pe.CcmSuggestedPrice = request.CcmPrice; // absolute-set (null = clear)
|
||||
|
||||
Reference in New Issue
Block a user