"use client"; import { Box, Button, Dialog, DialogActions, DialogContent, DialogTitle, FormControl, Grid, InputLabel, MenuItem, Select, TextField, Typography, } from "@mui/material"; import { useCallback, useEffect, useState, useRef } from "react"; import { useTranslation } from "react-i18next"; import { GetPickOrderLineInfo, PickExecutionIssueData, } from "@/app/api/pickOrder/actions"; import { fetchEscalationCombo } from "@/app/api/user/actions"; 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; } interface PickExecutionFormProps { open: boolean; onClose: () => void; onSubmit: (data: PickExecutionIssueData) => Promise; selectedLot: LotPickData | null; selectedPickOrderLine: (GetPickOrderLineInfo & { pickOrderCode: string }) | null; pickOrderId?: number; pickOrderCreateDate: any; } interface FormErrors { actualPickQty?: string; missQty?: string; badItemQty?: string; badReason?: string; issueRemark?: string; handledBy?: string; } const PickExecutionForm: React.FC = ({ open, onClose, onSubmit, selectedLot, selectedPickOrderLine, pickOrderId, pickOrderCreateDate, }) => { const { t } = useTranslation("pickOrder"); const [formData, setFormData] = useState>({}); const [errors, setErrors] = useState({}); const [loading, setLoading] = useState(false); const [handlers, setHandlers] = useState>( [] ); const calculateRemainingAvailableQty = useCallback((lot: LotPickData) => { return lot.availableQty || 0; }, []); const calculateRequiredQty = useCallback((lot: LotPickData) => { return lot.requiredQty || 0; }, []); useEffect(() => { const fetchHandlers = async () => { try { const escalationCombo = await fetchEscalationCombo(); setHandlers(escalationCombo); } catch (error) { console.error("Error fetching handlers:", error); } }; fetchHandlers(); }, []); const initKeyRef = useRef(null); useEffect(() => { if (!open || !selectedLot || !selectedPickOrderLine || !pickOrderId) return; const key = `${selectedPickOrderLine.id}-${selectedLot.lotId}`; if (initKeyRef.current === key) return; 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); } }; 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: selectedLot.actualPickQty || 0, missQty: 0, badItemQty: 0, // Bad Item Qty badPackageQty: 0, // Bad Package Qty (frontend only) issueRemark: "", pickerName: "", handledBy: undefined, reason: "", badReason: "", }); initKeyRef.current = key; }, [ open, selectedPickOrderLine?.id, selectedLot?.lotId, pickOrderId, pickOrderCreateDate, ]); const handleInputChange = useCallback( (field: keyof PickExecutionIssueData, value: any) => { setFormData((prev) => ({ ...prev, [field]: value })); if (errors[field as keyof FormErrors]) { setErrors((prev) => ({ ...prev, [field]: undefined })); } }, [errors] ); // Updated validation logic const validateForm = (): boolean => { const newErrors: FormErrors = {}; const ap = Number(formData.actualPickQty) || 0; const miss = Number(formData.missQty) || 0; const badItem = Number(formData.badItemQty) || 0; const badPackage = Number((formData as any).badPackageQty) || 0; const totalBad = badItem + badPackage; const total = ap + miss + totalBad; const availableQty = selectedLot?.availableQty || 0; // 1. Check actualPickQty cannot be negative if (ap < 0) { newErrors.actualPickQty = t("Qty cannot be negative"); } // 2. Check actualPickQty cannot exceed available quantity if (ap > availableQty) { newErrors.actualPickQty = t("Actual pick qty cannot exceed available qty"); } // 3. Check missQty and both bad qtys cannot be negative if (miss < 0) { newErrors.missQty = t("Invalid qty"); } if (badItem < 0 || badPackage < 0) { newErrors.badItemQty = t("Invalid qty"); } // 4. Total (actualPickQty + missQty + badItemQty + badPackageQty) cannot exceed lot available qty if (total > availableQty) { const errorMsg = t( "Total qty (actual pick + miss + bad) cannot exceed available qty: {available}", { available: availableQty } ); newErrors.actualPickQty = errorMsg; newErrors.missQty = errorMsg; newErrors.badItemQty = errorMsg; } // 5. At least one field must have a value if (ap === 0 && miss === 0 && totalBad === 0) { newErrors.actualPickQty = t("Enter pick qty or issue qty"); } setErrors(newErrors); return Object.keys(newErrors).length === 0; }; const handleSubmit = async () => { if (!validateForm()) { console.error("Form validation failed:", errors); return; } if (!formData.pickOrderId) { console.error("Missing pickOrderId"); return; } const badItem = Number(formData.badItemQty) || 0; const badPackage = Number((formData as any).badPackageQty) || 0; const totalBadQty = badItem + badPackage; let badReason: string | undefined; if (totalBadQty > 0) { // assumption: only one of them is > 0 badReason = badPackage > 0 ? "package_problem" : "quantity_problem"; } const submitData: PickExecutionIssueData = { ...(formData as PickExecutionIssueData), badItemQty: totalBadQty, badReason, }; setLoading(true); try { await onSubmit(submitData); } catch (error: any) { console.error("Error submitting pick execution issue:", error); alert( t("Failed to submit issue. Please try again.") + (error.message ? `: ${error.message}` : "") ); } finally { setLoading(false); } }; const handleClose = () => { setFormData({}); setErrors({}); onClose(); }; if (!selectedLot || !selectedPickOrderLine) { return null; } const remainingAvailableQty = calculateRemainingAvailableQty(selectedLot); const requiredQty = calculateRequiredQty(selectedLot); return ( {t("Pick Execution Issue Form") + " - "+selectedPickOrderLine.itemCode+ " "+ selectedPickOrderLine.itemName}
{selectedLot.lotNo}
handleInputChange( "actualPickQty", e.target.value === "" ? undefined : Math.max(0, Number(e.target.value) || 0) ) } error={!!errors.actualPickQty} helperText={ errors.actualPickQty || `${t("Max")}: ${remainingAvailableQty}` } variant="outlined" /> {t("Reason")} handleInputChange( "missQty", e.target.value === "" ? undefined : Math.max(0, Number(e.target.value) || 0) ) } error={!!errors.missQty} variant="outlined" /> handleInputChange( "badItemQty", e.target.value === "" ? undefined : Math.max(0, Number(e.target.value) || 0) ) } error={!!errors.badItemQty} //helperText={t("Quantity Problem")} variant="outlined" /> handleInputChange( "badPackageQty", e.target.value === "" ? undefined : Math.max(0, Number(e.target.value) || 0) ) } error={!!errors.badItemQty} //helperText={t("Package Problem")} variant="outlined" />
); }; export default PickExecutionForm;