From 8655ebf1ba28e7bf3891ff764247a8fc27292b25 Mon Sep 17 00:00:00 2001 From: pqhuy1987 Date: Thu, 18 Jun 2026 19:05:10 +0700 Subject: [PATCH] =?UTF-8?q?[CLAUDE]=20PurchaseEvaluation:=20Mig=2055=20?= =?UTF-8?q?=C3=B4=20"Ghi=20ch=C3=BA=20t=E1=BB=AB=20CCM"=20ng=C3=A2n=20s?= =?UTF-8?q?=C3=A1ch=20g=C3=B3i=20th=E1=BA=A7u=20=E2=80=94=20CCM=20nh?= =?UTF-8?q?=E1=BA=ADp=20s=E1=BB=91=20+=20ghi=20l=C3=BD=20do=20gi=E1=BB=91n?= =?UTF-8?q?g=20PRO?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Entity PeWorkItemBudget +CcmNote (mirror ProNote, nvarchar 1000) + Mig 55 additive-nullable - UpdatePeBudgetCcmCommand +CcmNote absolute-set, role-gate CostControl/Admin fail-closed - DTO PeBudgetSummaryDto +CcmNote + controller BudgetCcmBody + GET mapping - FE 2 app SHA-mirror: dòng "Ghi chú từ CCM" gate canEditCcm (sau V0/hiệu chỉnh), absolute-set đủ 3 field - Test +5 (set CCM/Admin, null-clear, non-priv Forbidden, all-3-persist) -> 339 pass Co-Authored-By: Claude Opus 4.8 (1M context) --- fe-admin/src/components/pe/PeDetailTabs.tsx | 40 +- fe-admin/src/types/purchaseEvaluation.ts | 1 + fe-user/src/components/pe/PeDetailTabs.tsx | 40 +- fe-user/src/types/purchaseEvaluation.ts | 1 + .../PurchaseEvaluationsController.cs | 4 +- .../Dtos/PurchaseEvaluationDtos.cs | 1 + .../PeWorkItemBudgetFeatures.cs | 10 +- .../PurchaseEvaluationFeatures.cs | 2 +- .../PurchaseEvaluations/PeWorkItemBudget.cs | 3 +- .../PeWorkItemBudgetConfiguration.cs | 1 + ...6_AddCcmNoteToPeWorkItemBudget.Designer.cs | 6243 +++++++++++++++++ ...0618105426_AddCcmNoteToPeWorkItemBudget.cs | 29 + .../ApplicationDbContextModelSnapshot.cs | 4 + .../Application/PeWorkItemBudgetTests.cs | 123 +- 14 files changed, 6487 insertions(+), 15 deletions(-) create mode 100644 src/Backend/SolutionErp.Infrastructure/Persistence/Migrations/20260618105426_AddCcmNoteToPeWorkItemBudget.Designer.cs create mode 100644 src/Backend/SolutionErp.Infrastructure/Persistence/Migrations/20260618105426_AddCcmNoteToPeWorkItemBudget.cs diff --git a/fe-admin/src/components/pe/PeDetailTabs.tsx b/fe-admin/src/components/pe/PeDetailTabs.tsx index ab08dad..3288152 100644 --- a/fe-admin/src/components/pe/PeDetailTabs.tsx +++ b/fe-admin/src/components/pe/PeDetailTabs.tsx @@ -1079,7 +1079,7 @@ function PeBudgetSummaryTable({ ev, readOnly }: { ev: PeDetailBundle; readOnly: }) // PUT /budget/ccm — chỉ khi canEditCcm. initialAmount + adjustmentAmount. const ccmMut = useMutation({ - mutationFn: async (body: { initialAmount: number | null; adjustmentAmount: number | null }) => + mutationFn: async (body: { initialAmount: number | null; adjustmentAmount: number | null; ccmNote: string | null }) => api.put(`/purchase-evaluations/${ev.id}/budget/ccm`, body), onSuccess: () => { toast.success('Đã lưu ngân sách ban hành'); invalidate() }, onError: e => toast.error(getErrorMessage(e)), @@ -1097,6 +1097,9 @@ function PeBudgetSummaryTable({ ev, readOnly }: { ev: PeDetailBundle; readOnly: // proNote inline-edit state (Textarea — không dùng VndInlineEdit) const [proNoteText, setProNoteText] = useState(bs?.proNote ?? '') useEffect(() => { setProNoteText(bs?.proNote ?? '') }, [bs?.proNote]) + // ccmNote inline-edit state (mirror proNoteText) — [Mig anh Kiệt FDC] + const [ccmNoteText, setCcmNoteText] = useState(bs?.ccmNote ?? '') + useEffect(() => { setCcmNoteText(bs?.ccmNote ?? '') }, [bs?.ccmNote]) // Phiếu cũ chưa gắn Hạng mục công việc → budgetSummary null. if (!bs) { @@ -1172,7 +1175,7 @@ function PeBudgetSummaryTable({ ev, readOnly }: { ev: PeDetailBundle; readOnly: initial={bs.initialAmount} saving={ccmMut.isPending} label="Ngân sách ban hành lần đầu" - onSave={v => ccmMut.mutate({ initialAmount: v, adjustmentAmount: bs.adjustmentAmount })} + onSave={v => ccmMut.mutate({ initialAmount: v, adjustmentAmount: bs.adjustmentAmount, ccmNote: bs.ccmNote })} /> ) : bs.initialAmount != null ? fmtVnd(bs.initialAmount) : } @@ -1188,7 +1191,7 @@ function PeBudgetSummaryTable({ ev, readOnly }: { ev: PeDetailBundle; readOnly: allowNegative saving={ccmMut.isPending} label="Ngân sách hiệu chỉnh tăng giảm" - onSave={v => ccmMut.mutate({ initialAmount: bs.initialAmount, adjustmentAmount: v })} + onSave={v => ccmMut.mutate({ initialAmount: bs.initialAmount, adjustmentAmount: v, ccmNote: bs.ccmNote })} /> ) : bs.adjustmentAmount != null ? ( {fmtVndSigned(bs.adjustmentAmount)} @@ -1196,6 +1199,37 @@ function PeBudgetSummaryTable({ ev, readOnly }: { ev: PeDetailBundle; readOnly: } /> + {/* Ghi chú từ CCM (CCM editable — Textarea, mirror Ghi chú từ PRO) — [Mig anh Kiệt FDC] */} +
+
Ghi chú từ CCM
+
+ {bs.canEditCcm ? ( +
+