[CLAUDE] PurchaseEvaluation: go field "Dieu khoan thanh toan" khoi TAT CA form phieu (UAT vong 5 - anh chot "bo not ra luon tat ca cac form")
Some checks failed
Deploy SOLUTION_ERP / build-deploy (push) Has been cancelled
Some checks failed
Deploy SOLUTION_ERP / build-deploy (push) Has been cancelled
- PeWorkspaceCreateView: bo Select preset + Textarea custom + PAYMENT_PRESETS/ PAYMENT_CUSTOM/paymentMode drop, payload paymentTerms: null. - PeHeaderForm + PeDetailTabs inline-edit: bo Textarea; state paymentTerms GIU (load tu phieu cu + save giu nguyen -> data cu KHONG mat, van hien read-only). - GIU cot "Dieu khoan TT" per-NCC trong bang so sanh (data tung NCC, khac field). - SHA256 mirror x2 app IDENTICAL, build tsc+vite x2 PASS.
This commit is contained in:
@ -13,7 +13,6 @@ import { Input } from '@/components/ui/Input'
|
|||||||
import { Label } from '@/components/ui/Label'
|
import { Label } from '@/components/ui/Label'
|
||||||
import { SearchableSelect } from '@/components/ui/SearchableSelect'
|
import { SearchableSelect } from '@/components/ui/SearchableSelect'
|
||||||
import { Select } from '@/components/ui/Select'
|
import { Select } from '@/components/ui/Select'
|
||||||
import { Textarea } from '@/components/ui/Textarea'
|
|
||||||
import { api } from '@/lib/api'
|
import { api } from '@/lib/api'
|
||||||
import { getErrorMessage } from '@/lib/apiError'
|
import { getErrorMessage } from '@/lib/apiError'
|
||||||
import { cn } from '@/lib/cn'
|
import { cn } from '@/lib/cn'
|
||||||
@ -722,16 +721,9 @@ function InfoTab({ ev, readOnly, autoEdit }: { ev: PeDetailBundle; readOnly: boo
|
|||||||
placeholder="Phương án A: ..."
|
placeholder="Phương án A: ..."
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="md:col-span-2">
|
{/* S59 vòng 5: field "Điều khoản thanh toán" GỠ khỏi inline-edit (anh chốt
|
||||||
<Label className="text-[11px]">Điều khoản thanh toán</Label>
|
"bỏ nốt ra luôn tất cả các form"). State paymentTerms giữ — save giữ nguyên
|
||||||
{/* S59 UAT "nhập tay chỉ được 1 dòng?" → Textarea đa dòng (render đã pre-wrap). */}
|
data cũ; phiếu đã nhập vẫn hiển thị read-only ở header info. */}
|
||||||
<Textarea
|
|
||||||
rows={3}
|
|
||||||
value={paymentTerms}
|
|
||||||
onChange={e => setPaymentTerms(e.target.value)}
|
|
||||||
placeholder={'Nhập điều khoản — Enter để xuống dòng'}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center justify-end gap-2">
|
<div className="flex items-center justify-end gap-2">
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@ -307,15 +307,8 @@ export function PeHeaderForm({
|
|||||||
<Textarea rows={3} value={form.moTa} onChange={e => setForm({ ...form, moTa: e.target.value })} />
|
<Textarea rows={3} value={form.moTa} onChange={e => setForm({ ...form, moTa: e.target.value })} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
{/* S59 vòng 5: field "Điều khoản thanh toán" GỠ khỏi form (anh chốt). State
|
||||||
<Label>Điều khoản thanh toán (JSON hoặc text)</Label>
|
paymentTerms giữ — load từ phiếu cũ + save giữ nguyên, data cũ KHÔNG mất. */}
|
||||||
<Textarea
|
|
||||||
rows={3}
|
|
||||||
value={form.paymentTerms}
|
|
||||||
onChange={e => setForm({ ...form, paymentTerms: e.target.value })}
|
|
||||||
placeholder='{"tamUng":"10%","thanhToanTam":"100% W.done","quyetToan":"Final Account","baoHanh":"5%"}'
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex justify-end gap-2">
|
<div className="flex justify-end gap-2">
|
||||||
{onCancel && (
|
{onCancel && (
|
||||||
|
|||||||
@ -15,7 +15,6 @@ import { Input } from '@/components/ui/Input'
|
|||||||
import { Label } from '@/components/ui/Label'
|
import { Label } from '@/components/ui/Label'
|
||||||
import { SearchableSelect } from '@/components/ui/SearchableSelect'
|
import { SearchableSelect } from '@/components/ui/SearchableSelect'
|
||||||
import { Select } from '@/components/ui/Select'
|
import { Select } from '@/components/ui/Select'
|
||||||
import { Textarea } from '@/components/ui/Textarea'
|
|
||||||
import { api } from '@/lib/api'
|
import { api } from '@/lib/api'
|
||||||
import { getErrorMessage } from '@/lib/apiError'
|
import { getErrorMessage } from '@/lib/apiError'
|
||||||
import { PurchaseEvaluationTypeLabel } from '@/types/purchaseEvaluation'
|
import { PurchaseEvaluationTypeLabel } from '@/types/purchaseEvaluation'
|
||||||
@ -36,21 +35,10 @@ type WorkItemOption = {
|
|||||||
isActive?: boolean
|
isActive?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
// Preset điều khoản thanh toán phổ biến — user chọn 1 trong list, hoặc "Khác"
|
// S59 UAT vòng 5 (anh chốt "điều khoản thanh toán bỏ nốt ra luôn tất cả các form"):
|
||||||
// để nhập tay. Save as plain text (không JSON như cũ — code-style không phù
|
// field Điều khoản TT phiếu-level GỠ khỏi mọi form (Create/HeaderForm/inline-edit).
|
||||||
// hợp UI cho end-user). User 2026-05-07 chỉnh.
|
// PAYMENT_PRESETS + PAYMENT_CUSTOM drop. Cột "Điều khoản TT" per-NCC trong bảng
|
||||||
const PAYMENT_PRESETS = [
|
// so sánh GIỮ (data của từng NCC, khác field). Phiếu cũ đã nhập → display read-only.
|
||||||
'100% sau khi nghiệm thu',
|
|
||||||
'Tạm ứng 30% / Thanh toán 70% sau nghiệm thu',
|
|
||||||
'Tạm ứng 50% / Thanh toán 50% sau nghiệm thu',
|
|
||||||
'TGN-30 ngày (Thanh toán giao nhận 30 ngày)',
|
|
||||||
'TGN-45 ngày',
|
|
||||||
'TGN-60 ngày',
|
|
||||||
'Tiến độ theo từng đợt',
|
|
||||||
'Bảo hành 5% trong 12 tháng',
|
|
||||||
] as const
|
|
||||||
|
|
||||||
const PAYMENT_CUSTOM = '__custom__'
|
|
||||||
|
|
||||||
export function PeWorkspaceCreateView({
|
export function PeWorkspaceCreateView({
|
||||||
defaultType,
|
defaultType,
|
||||||
@ -80,8 +68,6 @@ export function PeWorkspaceCreateView({
|
|||||||
approvalWorkflowId: '',
|
approvalWorkflowId: '',
|
||||||
})
|
})
|
||||||
// Payment terms: select preset OR "Khác" → text input
|
// Payment terms: select preset OR "Khác" → text input
|
||||||
const [paymentMode, setPaymentMode] = useState<string>('') // '' / preset / __custom__
|
|
||||||
const isPaymentCustom = paymentMode === PAYMENT_CUSTOM
|
|
||||||
|
|
||||||
const projects = useQuery({
|
const projects = useQuery({
|
||||||
queryKey: ['all-projects'],
|
queryKey: ['all-projects'],
|
||||||
@ -142,7 +128,7 @@ export function PeWorkspaceCreateView({
|
|||||||
workItemId: form.workItemId || null,
|
workItemId: form.workItemId || null,
|
||||||
diaDiem: form.diaDiem || null,
|
diaDiem: form.diaDiem || null,
|
||||||
moTa: form.moTa || null,
|
moTa: form.moTa || null,
|
||||||
paymentTerms: form.paymentTerms || null,
|
paymentTerms: null, // S59 vòng 5: field gỡ khỏi form
|
||||||
approvalWorkflowId: form.approvalWorkflowId || null,
|
approvalWorkflowId: form.approvalWorkflowId || null,
|
||||||
...budgetPayload,
|
...budgetPayload,
|
||||||
})
|
})
|
||||||
@ -261,38 +247,6 @@ export function PeWorkspaceCreateView({
|
|||||||
placeholder="Phương án A: ..."
|
placeholder="Phương án A: ..."
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="md:col-span-2">
|
|
||||||
<Label className="text-[11px]">Điều khoản thanh toán</Label>
|
|
||||||
<Select
|
|
||||||
value={paymentMode}
|
|
||||||
onChange={e => {
|
|
||||||
const v = e.target.value
|
|
||||||
setPaymentMode(v)
|
|
||||||
if (v === '' || v === PAYMENT_CUSTOM) {
|
|
||||||
setForm(f => ({ ...f, paymentTerms: '' }))
|
|
||||||
} else {
|
|
||||||
setForm(f => ({ ...f, paymentTerms: v }))
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<option value="">— Chọn điều khoản —</option>
|
|
||||||
{PAYMENT_PRESETS.map(p => (
|
|
||||||
<option key={p} value={p}>{p}</option>
|
|
||||||
))}
|
|
||||||
<option value={PAYMENT_CUSTOM}>Khác (nhập tay)</option>
|
|
||||||
</Select>
|
|
||||||
{isPaymentCustom && (
|
|
||||||
/* S59 UAT "nhập tay chỉ được 1 dòng?" → Textarea đa dòng (render
|
|
||||||
detail đã whitespace-pre-wrap sẵn nên xuống dòng hiển thị đúng). */
|
|
||||||
<Textarea
|
|
||||||
rows={3}
|
|
||||||
value={form.paymentTerms}
|
|
||||||
onChange={e => setForm({ ...form, paymentTerms: e.target.value })}
|
|
||||||
placeholder={'Nhập điều khoản tùy chỉnh — Enter để xuống dòng, vd:\n1. Tạm ứng: 10%\n2. Thanh toán hàng tháng: 80% - 45 ngày'}
|
|
||||||
className="mt-2"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</Section>
|
</Section>
|
||||||
|
|
||||||
|
|||||||
@ -13,7 +13,6 @@ import { Input } from '@/components/ui/Input'
|
|||||||
import { Label } from '@/components/ui/Label'
|
import { Label } from '@/components/ui/Label'
|
||||||
import { SearchableSelect } from '@/components/ui/SearchableSelect'
|
import { SearchableSelect } from '@/components/ui/SearchableSelect'
|
||||||
import { Select } from '@/components/ui/Select'
|
import { Select } from '@/components/ui/Select'
|
||||||
import { Textarea } from '@/components/ui/Textarea'
|
|
||||||
import { api } from '@/lib/api'
|
import { api } from '@/lib/api'
|
||||||
import { getErrorMessage } from '@/lib/apiError'
|
import { getErrorMessage } from '@/lib/apiError'
|
||||||
import { cn } from '@/lib/cn'
|
import { cn } from '@/lib/cn'
|
||||||
@ -722,16 +721,9 @@ function InfoTab({ ev, readOnly, autoEdit }: { ev: PeDetailBundle; readOnly: boo
|
|||||||
placeholder="Phương án A: ..."
|
placeholder="Phương án A: ..."
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="md:col-span-2">
|
{/* S59 vòng 5: field "Điều khoản thanh toán" GỠ khỏi inline-edit (anh chốt
|
||||||
<Label className="text-[11px]">Điều khoản thanh toán</Label>
|
"bỏ nốt ra luôn tất cả các form"). State paymentTerms giữ — save giữ nguyên
|
||||||
{/* S59 UAT "nhập tay chỉ được 1 dòng?" → Textarea đa dòng (render đã pre-wrap). */}
|
data cũ; phiếu đã nhập vẫn hiển thị read-only ở header info. */}
|
||||||
<Textarea
|
|
||||||
rows={3}
|
|
||||||
value={paymentTerms}
|
|
||||||
onChange={e => setPaymentTerms(e.target.value)}
|
|
||||||
placeholder={'Nhập điều khoản — Enter để xuống dòng'}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center justify-end gap-2">
|
<div className="flex items-center justify-end gap-2">
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@ -307,15 +307,8 @@ export function PeHeaderForm({
|
|||||||
<Textarea rows={3} value={form.moTa} onChange={e => setForm({ ...form, moTa: e.target.value })} />
|
<Textarea rows={3} value={form.moTa} onChange={e => setForm({ ...form, moTa: e.target.value })} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
{/* S59 vòng 5: field "Điều khoản thanh toán" GỠ khỏi form (anh chốt). State
|
||||||
<Label>Điều khoản thanh toán (JSON hoặc text)</Label>
|
paymentTerms giữ — load từ phiếu cũ + save giữ nguyên, data cũ KHÔNG mất. */}
|
||||||
<Textarea
|
|
||||||
rows={3}
|
|
||||||
value={form.paymentTerms}
|
|
||||||
onChange={e => setForm({ ...form, paymentTerms: e.target.value })}
|
|
||||||
placeholder='{"tamUng":"10%","thanhToanTam":"100% W.done","quyetToan":"Final Account","baoHanh":"5%"}'
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex justify-end gap-2">
|
<div className="flex justify-end gap-2">
|
||||||
{onCancel && (
|
{onCancel && (
|
||||||
|
|||||||
@ -15,7 +15,6 @@ import { Input } from '@/components/ui/Input'
|
|||||||
import { Label } from '@/components/ui/Label'
|
import { Label } from '@/components/ui/Label'
|
||||||
import { SearchableSelect } from '@/components/ui/SearchableSelect'
|
import { SearchableSelect } from '@/components/ui/SearchableSelect'
|
||||||
import { Select } from '@/components/ui/Select'
|
import { Select } from '@/components/ui/Select'
|
||||||
import { Textarea } from '@/components/ui/Textarea'
|
|
||||||
import { api } from '@/lib/api'
|
import { api } from '@/lib/api'
|
||||||
import { getErrorMessage } from '@/lib/apiError'
|
import { getErrorMessage } from '@/lib/apiError'
|
||||||
import { PurchaseEvaluationTypeLabel } from '@/types/purchaseEvaluation'
|
import { PurchaseEvaluationTypeLabel } from '@/types/purchaseEvaluation'
|
||||||
@ -36,21 +35,10 @@ type WorkItemOption = {
|
|||||||
isActive?: boolean
|
isActive?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
// Preset điều khoản thanh toán phổ biến — user chọn 1 trong list, hoặc "Khác"
|
// S59 UAT vòng 5 (anh chốt "điều khoản thanh toán bỏ nốt ra luôn tất cả các form"):
|
||||||
// để nhập tay. Save as plain text (không JSON như cũ — code-style không phù
|
// field Điều khoản TT phiếu-level GỠ khỏi mọi form (Create/HeaderForm/inline-edit).
|
||||||
// hợp UI cho end-user). User 2026-05-07 chỉnh.
|
// PAYMENT_PRESETS + PAYMENT_CUSTOM drop. Cột "Điều khoản TT" per-NCC trong bảng
|
||||||
const PAYMENT_PRESETS = [
|
// so sánh GIỮ (data của từng NCC, khác field). Phiếu cũ đã nhập → display read-only.
|
||||||
'100% sau khi nghiệm thu',
|
|
||||||
'Tạm ứng 30% / Thanh toán 70% sau nghiệm thu',
|
|
||||||
'Tạm ứng 50% / Thanh toán 50% sau nghiệm thu',
|
|
||||||
'TGN-30 ngày (Thanh toán giao nhận 30 ngày)',
|
|
||||||
'TGN-45 ngày',
|
|
||||||
'TGN-60 ngày',
|
|
||||||
'Tiến độ theo từng đợt',
|
|
||||||
'Bảo hành 5% trong 12 tháng',
|
|
||||||
] as const
|
|
||||||
|
|
||||||
const PAYMENT_CUSTOM = '__custom__'
|
|
||||||
|
|
||||||
export function PeWorkspaceCreateView({
|
export function PeWorkspaceCreateView({
|
||||||
defaultType,
|
defaultType,
|
||||||
@ -80,8 +68,6 @@ export function PeWorkspaceCreateView({
|
|||||||
approvalWorkflowId: '',
|
approvalWorkflowId: '',
|
||||||
})
|
})
|
||||||
// Payment terms: select preset OR "Khác" → text input
|
// Payment terms: select preset OR "Khác" → text input
|
||||||
const [paymentMode, setPaymentMode] = useState<string>('') // '' / preset / __custom__
|
|
||||||
const isPaymentCustom = paymentMode === PAYMENT_CUSTOM
|
|
||||||
|
|
||||||
const projects = useQuery({
|
const projects = useQuery({
|
||||||
queryKey: ['all-projects'],
|
queryKey: ['all-projects'],
|
||||||
@ -142,7 +128,7 @@ export function PeWorkspaceCreateView({
|
|||||||
workItemId: form.workItemId || null,
|
workItemId: form.workItemId || null,
|
||||||
diaDiem: form.diaDiem || null,
|
diaDiem: form.diaDiem || null,
|
||||||
moTa: form.moTa || null,
|
moTa: form.moTa || null,
|
||||||
paymentTerms: form.paymentTerms || null,
|
paymentTerms: null, // S59 vòng 5: field gỡ khỏi form
|
||||||
approvalWorkflowId: form.approvalWorkflowId || null,
|
approvalWorkflowId: form.approvalWorkflowId || null,
|
||||||
...budgetPayload,
|
...budgetPayload,
|
||||||
})
|
})
|
||||||
@ -261,38 +247,6 @@ export function PeWorkspaceCreateView({
|
|||||||
placeholder="Phương án A: ..."
|
placeholder="Phương án A: ..."
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="md:col-span-2">
|
|
||||||
<Label className="text-[11px]">Điều khoản thanh toán</Label>
|
|
||||||
<Select
|
|
||||||
value={paymentMode}
|
|
||||||
onChange={e => {
|
|
||||||
const v = e.target.value
|
|
||||||
setPaymentMode(v)
|
|
||||||
if (v === '' || v === PAYMENT_CUSTOM) {
|
|
||||||
setForm(f => ({ ...f, paymentTerms: '' }))
|
|
||||||
} else {
|
|
||||||
setForm(f => ({ ...f, paymentTerms: v }))
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<option value="">— Chọn điều khoản —</option>
|
|
||||||
{PAYMENT_PRESETS.map(p => (
|
|
||||||
<option key={p} value={p}>{p}</option>
|
|
||||||
))}
|
|
||||||
<option value={PAYMENT_CUSTOM}>Khác (nhập tay)</option>
|
|
||||||
</Select>
|
|
||||||
{isPaymentCustom && (
|
|
||||||
/* S59 UAT "nhập tay chỉ được 1 dòng?" → Textarea đa dòng (render
|
|
||||||
detail đã whitespace-pre-wrap sẵn nên xuống dòng hiển thị đúng). */
|
|
||||||
<Textarea
|
|
||||||
rows={3}
|
|
||||||
value={form.paymentTerms}
|
|
||||||
onChange={e => setForm({ ...form, paymentTerms: e.target.value })}
|
|
||||||
placeholder={'Nhập điều khoản tùy chỉnh — Enter để xuống dòng, vd:\n1. Tạm ứng: 10%\n2. Thanh toán hàng tháng: 80% - 45 ngày'}
|
|
||||||
className="mt-2"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</Section>
|
</Section>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user