[CLAUDE] PurchaseEvaluation: User chọn quy trình duyệt V2 lúc tạo phiếu (Mig 23)
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 3m11s
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 3m11s
User feedback: thay field "Loại quy trình (theo menu — khóa)" disabled
→ Select dropdown cho User pick quy trình ApprovalWorkflowsV2 (Mig 22)
ngay từ workspace tạo mới. Hiển thị "Mã + Tên + Version".
BE Domain:
- PurchaseEvaluation +ApprovalWorkflowId Guid? (nullable, FK Restrict)
- EF Configuration: Index + FK Restrict to ApprovalWorkflows
- Migration 23 `AddApprovalWorkflowIdToPurchaseEvaluation` (1 ALTER +
1 IX + 1 FK), applied cả _Design + _Dev LocalDB
- Field WorkflowDefinitionId (Mig 21 legacy) giữ song song để Service
PE chạy logic cũ tới khi Session sau wire qua schema mới
BE Application:
- CreatePurchaseEvaluationCommand +ApprovalWorkflowId? Guid? optional
param (default null)
- Validate: nếu set, phải tồn tại + ApplicableType khớp PE.Type
(DuyetNcc=1 → ApprovalWorkflowApplicableType.DuyetNcc, etc)
- Handler set entity.ApprovalWorkflowId từ request
- UpdatePurchaseEvaluationDraftCommand mirror — cho User đổi quy trình
khi sửa Nháp/Trả lại (validate same)
- PurchaseEvaluationDetailBundleDto +ApprovalWorkflowId/Code/Name/Version
- GetPurchaseEvaluationByIdQuery handler load workflow info join
- Update Phase guard: cho sửa cả DangSoanThao + TraLai (Trả lại =
editable per Session 17 spec)
FE (cả 2 app mirror):
- types/purchaseEvaluation.ts: PeDetail +approvalWorkflowId/Code/Name/Version
- PeWorkspaceCreateView.tsx:
- Replace field disabled "Loại quy trình" → Select bắt buộc
- useQuery `/api/approval-workflows-v2?applicableType=N` filter theo
defaultType (1=DuyetNcc / 2=DuyetNccPhuongAn)
- Display option: "QT-DN-V2-001 v01 — Quy trình Duyệt NCC (đang áp dụng)"
- List cả version active + archived (UAT cần test compare)
- Empty state hint amber "Chưa có quy trình, vào /system/approval-workflows-v2"
- canSubmit require approvalWorkflowId set
- POST payload include approvalWorkflowId
Verify: dotnet build OK · 81 test pass · npm build × 2 OK · Mig 23 applied
cả 2 LocalDB.
Logic Service PE chưa wire qua ApprovalWorkflowId — vẫn pin
WorkflowDefinitionId Mig 21 legacy chạy. Session sau wire Service iterate
ApprovalWorkflowSteps + match approver theo schema V2 + drop legacy.
This commit is contained in:
@ -59,6 +59,8 @@ export function PeWorkspaceCreateView({
|
||||
budgetManual: false,
|
||||
budgetManualName: '',
|
||||
budgetManualAmount: 0,
|
||||
// Mig 23 — Pin quy trình duyệt V2 (User tự chọn lúc tạo)
|
||||
approvalWorkflowId: '',
|
||||
})
|
||||
// Payment terms: select preset OR "Khác" → text input
|
||||
const [paymentMode, setPaymentMode] = useState<string>('') // '' / preset / __custom__
|
||||
@ -69,6 +71,19 @@ export function PeWorkspaceCreateView({
|
||||
queryFn: async () => (await api.get<{ items: Project[] }>('/projects', { params: { pageSize: 1000 } })).data.items,
|
||||
})
|
||||
|
||||
// Mig 23 — fetch list quy trình duyệt V2 (filter ApplicableType khớp defaultType).
|
||||
const approvalWorkflows = useQuery({
|
||||
queryKey: ['approval-workflows-v2-active', defaultType],
|
||||
queryFn: async () => {
|
||||
const res = await api.get<{ types: { applicableType: number; history: { id: string; code: string; version: number; name: string; isActive: boolean }[] }[] }>(
|
||||
'/approval-workflows-v2',
|
||||
{ params: { applicableType: defaultType } },
|
||||
)
|
||||
const typeBucket = res.data.types.find(t => t.applicableType === defaultType)
|
||||
return typeBucket?.history ?? []
|
||||
},
|
||||
})
|
||||
|
||||
const eligibleBudgets = useQuery({
|
||||
queryKey: ['eligible-budgets', form.projectId],
|
||||
queryFn: async () => {
|
||||
@ -93,6 +108,7 @@ export function PeWorkspaceCreateView({
|
||||
diaDiem: form.diaDiem || null,
|
||||
moTa: form.moTa || null,
|
||||
paymentTerms: form.paymentTerms || null,
|
||||
approvalWorkflowId: form.approvalWorkflowId || null,
|
||||
...budgetPayload,
|
||||
})
|
||||
return res.data.id
|
||||
@ -105,7 +121,7 @@ export function PeWorkspaceCreateView({
|
||||
onError: e => toast.error(getErrorMessage(e)),
|
||||
})
|
||||
|
||||
const canSubmit = !!form.tenGoiThau && !!form.projectId && !create.isPending
|
||||
const canSubmit = !!form.tenGoiThau && !!form.projectId && !!form.approvalWorkflowId && !create.isPending
|
||||
|
||||
return (
|
||||
<div className="rounded-lg border border-slate-200 bg-white shadow-sm">
|
||||
@ -126,15 +142,30 @@ export function PeWorkspaceCreateView({
|
||||
{/* Section 1 — Thông tin gói thầu (editable) */}
|
||||
<Section title="1. Thông tin gói thầu">
|
||||
<div className="grid gap-3 md:grid-cols-2">
|
||||
<div>
|
||||
<Label className="text-[11px]">Loại quy trình (theo menu — khóa)</Label>
|
||||
<Input
|
||||
value={PurchaseEvaluationTypeLabel[form.type]}
|
||||
disabled
|
||||
className="bg-slate-100 font-medium"
|
||||
/>
|
||||
<div className="md:col-span-2">
|
||||
<Label className="text-[11px]">
|
||||
Quy trình duyệt * <span className="text-[10px] font-normal text-slate-400">(theo {PurchaseEvaluationTypeLabel[form.type]})</span>
|
||||
</Label>
|
||||
<Select
|
||||
value={form.approvalWorkflowId}
|
||||
onChange={e => setForm({ ...form, approvalWorkflowId: e.target.value })}
|
||||
required
|
||||
>
|
||||
<option value="">— Chọn quy trình duyệt —</option>
|
||||
{approvalWorkflows.data?.map(w => (
|
||||
<option key={w.id} value={w.id}>
|
||||
{w.code} v{String(w.version).padStart(2, '0')} — {w.name}
|
||||
{w.isActive ? ' (đang áp dụng)' : ''}
|
||||
</option>
|
||||
))}
|
||||
</Select>
|
||||
{approvalWorkflows.data && approvalWorkflows.data.length === 0 && (
|
||||
<p className="mt-1 text-[11px] text-amber-700">
|
||||
⚠ Chưa có quy trình duyệt cho loại {PurchaseEvaluationTypeLabel[form.type]}. Liên hệ admin tạo trước.
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="md:col-span-1">
|
||||
<div className="md:col-span-2">
|
||||
<Label className="text-[11px]">a. Tên gói thầu *</Label>
|
||||
<Input
|
||||
value={form.tenGoiThau}
|
||||
|
||||
@ -309,6 +309,11 @@ export type PeDetailBundle = {
|
||||
// Mig 17 — manual budget fallback khi không link Budget entity. Cả 2 cùng null OK.
|
||||
budgetManualName: string | null
|
||||
budgetManualAmount: number | null
|
||||
// Mig 23 — Pin schema mới ApprovalWorkflowsV2 (User chọn lúc create).
|
||||
approvalWorkflowId: string | null
|
||||
approvalWorkflowCode: string | null
|
||||
approvalWorkflowName: string | null
|
||||
approvalWorkflowVersion: number | null
|
||||
suppliers: PeSupplier[]
|
||||
details: PeDetailRow[]
|
||||
approvals: PeApproval[]
|
||||
|
||||
Reference in New Issue
Block a user