// FPSMS-frontend/src/components/PickOrderSearch/PickExecutionForm.tsx "use client"; import { Box, Button, Dialog, DialogActions, DialogContent, DialogTitle, FormControl, Grid, InputLabel, MenuItem, Select, TextField, Typography, } from "@mui/material"; import { useCallback, useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { GetPickOrderLineInfo, PickExecutionIssueData } from "@/app/api/pickOrder/actions"; import { fetchEscalationCombo } from "@/app/api/user/actions"; import { useSession } from "next-auth/react"; import { SessionWithTokens } from "@/config/authConfig"; import dayjs from 'dayjs'; import { INPUT_DATE_FORMAT } from "@/app/utils/formatUtil"; interface LotPickData { id: number; lotId: number; lotNo: string; expiryDate: string; location: string; stockUnit: string; inQty: number; outQty: number; holdQty: number; totalPickedByAllPickOrders: number; availableQty: number; requiredQty: number; actualPickQty: number; lotStatus: string; lotAvailability: 'available' | 'insufficient_stock' | 'expired' | 'status_unavailable'|'rejected'; stockOutLineId?: number; stockOutLineStatus?: string; stockOutLineQty?: number; pickOrderLineId?: number; pickOrderId?: number; pickOrderCode?: string; } interface PickExecutionFormProps { open: boolean; onClose: () => void; onSubmit: (data: PickExecutionIssueData) => Promise; selectedLot: LotPickData | null; selectedPickOrderLine: (GetPickOrderLineInfo & { pickOrderCode: string }) | null; pickOrderId?: number; pickOrderCreateDate: any; onNormalPickSubmit?: (lot: LotPickData, submitQty: number) => Promise; // Remove these props since we're not handling normal cases // onNormalPickSubmit?: (lineId: number, lotId: number, qty: number) => Promise; // selectedRowId?: number | null; } // 定义错误类型 interface FormErrors { actualPickQty?: string; missQty?: string; badItemQty?: string; issueRemark?: string; handledBy?: string; } const PickExecutionForm: React.FC = ({ open, onClose, onSubmit, selectedLot, selectedPickOrderLine, pickOrderId, pickOrderCreateDate, onNormalPickSubmit, }) => { const { t } = useTranslation(); const [formData, setFormData] = useState>({}); const [errors, setErrors] = useState({}); const [loading, setLoading] = useState(false); const [handlers, setHandlers] = useState>([]); const [verifiedQty, setVerifiedQty] = useState(0); const { data: session } = useSession() as { data: SessionWithTokens | null }; const calculateRemainingAvailableQty = useCallback((lot: LotPickData) => { return lot.availableQty || 0; }, []); const calculateRequiredQty = useCallback((lot: LotPickData) => { // Use the original required quantity, not subtracting actualPickQty // The actualPickQty in the form should be independent of the database value return lot.requiredQty || 0; }, []); useEffect(() => { console.log('PickExecutionForm props:', { open, onNormalPickSubmit: typeof onNormalPickSubmit, hasOnNormalPickSubmit: !!onNormalPickSubmit, onSubmit: typeof onSubmit, }); }, [open, onNormalPickSubmit, onSubmit]); // 获取处理人员列表 useEffect(() => { const fetchHandlers = async () => { try { const escalationCombo = await fetchEscalationCombo(); setHandlers(escalationCombo); } catch (error) { console.error("Error fetching handlers:", error); } }; fetchHandlers(); }, []); // 初始化表单数据 - 每次打开时都重新初始化 useEffect(() => { if (open && selectedLot && selectedPickOrderLine && pickOrderId) { const getSafeDate = (dateValue: any): string => { if (!dateValue) return dayjs().format(INPUT_DATE_FORMAT); try { const date = dayjs(dateValue); if (!date.isValid()) { return dayjs().format(INPUT_DATE_FORMAT); } return date.format(INPUT_DATE_FORMAT); } catch { return dayjs().format(INPUT_DATE_FORMAT); } }; // Initialize verified quantity to the received quantity (actualPickQty) const initialVerifiedQty = selectedLot.actualPickQty || 0; setVerifiedQty(initialVerifiedQty); console.log("=== PickExecutionForm Debug ==="); console.log("selectedLot:", selectedLot); console.log("initialVerifiedQty:", initialVerifiedQty); console.log("=== End Debug ==="); setFormData({ pickOrderId: pickOrderId, pickOrderCode: selectedPickOrderLine.pickOrderCode, pickOrderCreateDate: getSafeDate(pickOrderCreateDate), pickExecutionDate: dayjs().format(INPUT_DATE_FORMAT), pickOrderLineId: selectedPickOrderLine.id, itemId: selectedPickOrderLine.itemId, itemCode: selectedPickOrderLine.itemCode, itemDescription: selectedPickOrderLine.itemName, lotId: selectedLot.lotId, lotNo: selectedLot.lotNo, storeLocation: selectedLot.location, requiredQty: selectedLot.requiredQty, actualPickQty: initialVerifiedQty, missQty: 0, badItemQty: 0, issueRemark: '', handledBy: undefined, }); } // 只在 open 状态改变时重新初始化,移除其他依赖 // eslint-disable-next-line react-hooks/exhaustive-deps }, [open]); const handleInputChange = useCallback((field: keyof PickExecutionIssueData, value: any) => { setFormData(prev => ({ ...prev, [field]: value })); // Update verified quantity state when actualPickQty changes if (field === 'actualPickQty') { setVerifiedQty(value); } // 清除错误 if (errors[field as keyof FormErrors]) { setErrors(prev => ({ ...prev, [field]: undefined })); } }, [errors]); // Update form validation to require either missQty > 0 OR badItemQty > 0 const validateForm = (): boolean => { const newErrors: FormErrors = {}; const requiredQty = selectedLot?.requiredQty || 0; const badItemQty = formData.badItemQty || 0; const missQty = formData.missQty || 0; if (verifiedQty === undefined || verifiedQty < 0) { newErrors.actualPickQty = t('Qty is required'); } const totalQty = verifiedQty + badItemQty + missQty; const hasAnyValue = verifiedQty > 0 || badItemQty > 0 || missQty > 0; // ✅ 新增:必须至少有一个 > 0 if (!hasAnyValue) { newErrors.actualPickQty = t('At least one of Verified / Missing / Bad must be greater than 0'); } if (hasAnyValue && totalQty !== requiredQty) { newErrors.actualPickQty = t('Total (Verified + Bad + Missing) must equal Required quantity'); } setErrors(newErrors); return Object.keys(newErrors).length === 0; }; const handleSubmit = async () => { if (!formData.pickOrderId || !selectedLot) { return; } // ✅ 只允许 Verified>0 且没有问题时,走 normal pick const isNormalPick = verifiedQty > 0 && formData.missQty == 0 && formData.badItemQty == 0; if (isNormalPick) { if (onNormalPickSubmit) { setLoading(true); try { console.log('Calling onNormalPickSubmit with:', { lot: selectedLot, submitQty: verifiedQty }); await onNormalPickSubmit(selectedLot, verifiedQty); onClose(); } catch (error) { console.error('Error submitting normal pick:', error); } finally { setLoading(false); } } else { console.warn('onNormalPickSubmit callback not provided'); } return; } // ❌ 有问题(或全部为 0)才进入 Issue 提报流程 if (!validateForm() || !formData.pickOrderId) { return; } setLoading(true); try { const submissionData = { ...formData, actualPickQty: verifiedQty, lotId: formData.lotId || selectedLot?.lotId || 0, lotNo: formData.lotNo || selectedLot?.lotNo || '', pickOrderCode: formData.pickOrderCode || selectedPickOrderLine?.pickOrderCode || '', pickerName: session?.user?.name || '' } as PickExecutionIssueData; await onSubmit(submissionData); onClose(); } catch (error) { console.error('Error submitting pick execution issue:', error); } finally { setLoading(false); } }; const handleClose = () => { setFormData({}); setErrors({}); setVerifiedQty(0); onClose(); }; if (!selectedLot || !selectedPickOrderLine) { return null; } const remainingAvailableQty = calculateRemainingAvailableQty(selectedLot); const requiredQty = calculateRequiredQty(selectedLot); return ( {t('Pick Execution Issue Form')} {/* Always show issue form title */} {/* Add instruction text */} {t('Note:')} {t('This form is for reporting issues only. You must report either missing items or bad items.')} {/* Keep the existing form fields */} { const newValue = parseFloat(e.target.value) || 0; setVerifiedQty(newValue); // handleInputChange('actualPickQty', newValue); }} error={!!errors.actualPickQty} helperText={errors.actualPickQty || `${t('Max')}: ${selectedLot?.actualPickQty || 0}`} // 使用原始接收数量 variant="outlined" /> { const newMissQty = parseFloat(e.target.value) || 0; handleInputChange('missQty', newMissQty); // 不要自动修改其他字段 }} error={!!errors.missQty} helperText={errors.missQty} variant="outlined" /> { const newBadItemQty = parseFloat(e.target.value) || 0; handleInputChange('badItemQty', newBadItemQty); // 不要自动修改其他字段 }} error={!!errors.badItemQty} helperText={errors.badItemQty} variant="outlined" /> {/* Show issue description and handler fields when bad items > 0 */} {(formData.badItemQty && formData.badItemQty > 0) ? ( <> { handleInputChange('issueRemark', e.target.value); // Don't reset badItemQty when typing in issue remark }} error={!!errors.issueRemark} helperText={errors.issueRemark} //placeholder={t('Describe the issue with bad items')} variant="outlined" /> {t('handler')} {errors.handledBy && ( {errors.handledBy} )} ) : (<>)} ); }; export default PickExecutionForm;