From c2326f0314a5456f86a0a26c59b88bf2f747438e Mon Sep 17 00:00:00 2001 From: "CANCERYS\\kw093" Date: Tue, 2 Sep 2025 15:28:58 +0800 Subject: [PATCH] update --- src/components/PickOrderSearch/LotTable.tsx | 267 ++++++++- .../PickOrderSearch/PickExecution.tsx | 72 ++- .../PickQcStockInModalVer3.tsx | 96 ++-- .../PickOrderSearch/assignTo copy.tsx | 511 ++++++++++++++++++ 4 files changed, 876 insertions(+), 70 deletions(-) create mode 100644 src/components/PickOrderSearch/assignTo copy.tsx diff --git a/src/components/PickOrderSearch/LotTable.tsx b/src/components/PickOrderSearch/LotTable.tsx index d588dbf..f8db92f 100644 --- a/src/components/PickOrderSearch/LotTable.tsx +++ b/src/components/PickOrderSearch/LotTable.tsx @@ -15,11 +15,13 @@ import { TextField, Typography, TablePagination, + Modal, } from "@mui/material"; -import { useCallback, useMemo, useState } from "react"; +import { useCallback, useMemo, useState, useEffect } from "react"; import { useTranslation } from "react-i18next"; import QrCodeIcon from '@mui/icons-material/QrCode'; import { GetPickOrderLineInfo } from "@/app/api/pickOrder/actions"; +import { useQrCodeScannerContext } from '../QrCodeScannerProvider/QrCodeScannerProvider'; interface LotPickData { id: number; @@ -63,6 +65,164 @@ interface LotTableProps { generateInputBody: () => any; } +// ✅ QR Code Modal Component +const QrCodeModal: React.FC<{ + open: boolean; + onClose: () => void; + lot: LotPickData | null; + onQrCodeSubmit: (lotNo: string) => void; +}> = ({ open, onClose, lot, onQrCodeSubmit }) => { + const { t } = useTranslation("pickOrder"); + const { values: qrValues, isScanning, startScan, stopScan, resetScan } = useQrCodeScannerContext(); + const [manualInput, setManualInput] = useState(''); + + // ✅ Add state to track manual input submission + const [manualInputSubmitted, setManualInputSubmitted] = useState(false); + const [manualInputError, setManualInputError] = useState(false); + + // ✅ Process scanned QR codes + useEffect(() => { + if (qrValues.length > 0 && lot) { + const latestQr = qrValues[qrValues.length - 1]; + const qrContent = latestQr.replace(/[{}]/g, ''); + + if (qrContent === lot.lotNo) { + onQrCodeSubmit(lot.lotNo); + onClose(); + resetScan(); + } else { + // ✅ Set error state for helper text + setManualInputError(true); + setManualInputSubmitted(true); + } + } + }, [qrValues, lot, onQrCodeSubmit, onClose, resetScan]); + + // ✅ Clear states when modal opens or lot changes + useEffect(() => { + if (open) { + setManualInput(''); + setManualInputSubmitted(false); + setManualInputError(false); + } + }, [open]); + + useEffect(() => { + if (lot) { + setManualInput(''); + setManualInputSubmitted(false); + setManualInputError(false); + } + }, [lot]); + + const handleManualSubmit = () => { + if (manualInput.trim() === lot?.lotNo) { + // ✅ Success - no error helper text needed + onQrCodeSubmit(lot.lotNo); + onClose(); + setManualInput(''); + } else { + // ✅ Show error helper text after submit + setManualInputError(true); + setManualInputSubmitted(true); + // Don't clear input - let user see what they typed + } + }; + + return ( + + + + {t("QR Code Scan for Lot")}: {lot?.lotNo} + + + {/* QR Scanner Status */} + + + Scanner Status: {isScanning ? 'Scanning...' : 'Ready'} + + + + + + + + {/* Manual Input with Submit-Triggered Helper Text */} + + + Manual Input: + + setManualInput(e.target.value)} + sx={{ mb: 1 }} + // ✅ Only show error after submit button is clicked + error={manualInputSubmitted && manualInputError} + helperText={ + // ✅ Show helper text only after submit with error + manualInputSubmitted && manualInputError + ? `The input is not the same as the expected lot number. Expected: ${lot?.lotNo}` + : '' + } + /> + + + + {/* ✅ Show QR Scan Status */} + {qrValues.length > 0 && ( + + + QR Scan Result: {qrValues[qrValues.length - 1]} + + {manualInputError && ( + + ❌ Mismatch! Expected: {lot?.lotNo} + + )} + + )} + + + + + + + ); +}; + const LotTable: React.FC = ({ lotData, selectedRowId, @@ -83,6 +243,14 @@ const LotTable: React.FC = ({ }) => { const { t } = useTranslation("pickOrder"); + // ✅ Add QR scanner context + const { values: qrValues, isScanning, startScan, stopScan, resetScan } = useQrCodeScannerContext(); + + // ✅ Add state for QR input modal + const [qrModalOpen, setQrModalOpen] = useState(false); + const [selectedLotForQr, setSelectedLotForQr] = useState(null); + const [manualQrInput, setManualQrInput] = useState(''); + // 分页控制器 const [lotTablePagingController, setLotTablePagingController] = useState({ pageNum: 0, @@ -95,10 +263,10 @@ const LotTable: React.FC = ({ return "Please finish QR code scan, QC check and pick order."; } - switch (lot.stockOutLineStatus?.toUpperCase()) { - case 'PENDING': + switch (lot.stockOutLineStatus?.toLowerCase()) { + case 'pending': return "Please finish QC check and pick order."; - case 'COMPLETE': + case 'completed': return "Please submit the pick order."; case 'unavailable': return "This order is insufficient, please pick another lot."; @@ -137,6 +305,23 @@ const LotTable: React.FC = ({ }); }, []); + // ✅ Handle QR code submission + const handleQrCodeSubmit = useCallback((lotNo: string) => { + if (selectedLotForQr && selectedLotForQr.lotNo === lotNo) { + console.log(`✅ QR Code verified for lot: ${lotNo}`); + + // ✅ Create stock out line + onCreateStockOutLine(selectedLotForQr.lotId); + + // ✅ Show success message + console.log("Stock out line created successfully!"); + + // ✅ Close modal + setQrModalOpen(false); + setSelectedLotForQr(null); + } + }, [selectedLotForQr, onCreateStockOutLine]); + return ( <> @@ -198,26 +383,47 @@ const LotTable: React.FC = ({ {/* QR Code Scan Button */} - + + + + + {/* QC Check Button */} @@ -320,6 +526,19 @@ const LotTable: React.FC = ({ `${from}-${to} of ${count !== -1 ? count : `more than ${to}`}` } /> + + {/* ✅ QR Code Modal */} + { + setQrModalOpen(false); + setSelectedLotForQr(null); + stopScan(); + resetScan(); + }} + lot={selectedLotForQr} + onQrCodeSubmit={handleQrCodeSubmit} + /> ); }; diff --git a/src/components/PickOrderSearch/PickExecution.tsx b/src/components/PickOrderSearch/PickExecution.tsx index 57110fe..1fe2eb0 100644 --- a/src/components/PickOrderSearch/PickExecution.tsx +++ b/src/components/PickOrderSearch/PickExecution.tsx @@ -490,20 +490,22 @@ const PickExecution: React.FC = ({ filterArgs }) => { }, [selectedLotRowId]); // ✅ Add function to handle row selection that resets lot selection - const handleRowSelect = useCallback(async (lineId: number) => { + const handleRowSelect = useCallback(async (lineId: number, preserveLotSelection: boolean = false) => { setSelectedRowId(lineId); - // ✅ Reset lot selection when changing pick order line - setSelectedLotRowId(null); - setSelectedLotId(null); + // ✅ Only reset lot selection if not preserving + if (!preserveLotSelection) { + setSelectedLotRowId(null); + setSelectedLotId(null); + } try { const lotDetails = await fetchPickOrderLineLotDetails(lineId); console.log("Lot details from API:", lotDetails); const realLotData: LotPickData[] = lotDetails.map((lot: any) => ({ - id: lot.lotId, // This is actually ill.id (inventory lot line ID) - lotId: lot.lotId, // This should be the unique inventory lot line ID + id: lot.id, // This should be the unique row ID for the table + lotId: lot.lotId, // This is the inventory lot line ID lotNo: lot.lotNo, expiryDate: lot.expiryDate ? new Date(lot.expiryDate).toLocaleDateString() : 'N/A', location: lot.location, @@ -513,7 +515,6 @@ const PickExecution: React.FC = ({ filterArgs }) => { actualPickQty: lot.actualPickQty || 0, lotStatus: lot.lotStatus, lotAvailability: lot.lotAvailability, - // ✅ Add StockOutLine fields stockOutLineId: lot.stockOutLineId, stockOutLineStatus: lot.stockOutLineStatus, stockOutLineQty: lot.stockOutLineQty @@ -545,6 +546,7 @@ const PickExecution: React.FC = ({ filterArgs }) => { pickOrderCode: pickOrder.code, targetDate: formattedTargetDate, // ✅ 使用 dayjs 格式化的日期 balanceToPick: balanceToPick, + pickedQty: line.pickedQty, // 确保 availableQty 不为 null availableQty: availableQty, }; @@ -675,6 +677,10 @@ const PickExecution: React.FC = ({ filterArgs }) => { } try { + // ✅ Store current lot selection before refresh + const currentSelectedLotRowId = selectedLotRowId; + const currentSelectedLotId = selectedLotId; + const stockOutLineData: CreateStockOutLine = { consoCode: pickOrderDetails.consoCode, pickOrderLineId: selectedRowId, @@ -692,19 +698,57 @@ const PickExecution: React.FC = ({ filterArgs }) => { if (result) { console.log("Stock out line created successfully:", result); - //alert(`Stock out line created successfully! ID: ${result.id}`); - // ✅ Don't refresh immediately - let user see the result first + // ✅ Auto-refresh data after successful creation + console.log("🔄 Refreshing data after stock out line creation..."); + + try { + // ✅ Refresh lot data for the selected row (maintains selection) + if (selectedRowId) { + await handleRowSelect(selectedRowId, true); // ✅ Preserve lot selection + } + + // ✅ Refresh main pick order details + await handleFetchAllPickOrderDetails(); + + console.log("✅ Data refresh completed - lot selection maintained!"); + } catch (refreshError) { + console.error("❌ Error refreshing data:", refreshError); + } + setShowInputBody(false); // Hide preview after successful creation } else { console.error("Failed to create stock out line: No response"); - //alert("Failed to create stock out line: No response"); } } catch (error) { console.error("Error creating stock out line:", error); - //alert("Error creating stock out line. Please try again."); } - }, [selectedRowId, pickOrderDetails?.consoCode]); + }, [selectedRowId, pickOrderDetails?.consoCode, handleRowSelect, handleFetchAllPickOrderDetails, selectedLotRowId, selectedLotId]); + + // ✅ New function to refresh data while preserving lot selection + const handleRefreshDataPreserveSelection = useCallback(async () => { + if (!selectedRowId) return; + + // ✅ Store current lot selection + const currentSelectedLotRowId = selectedLotRowId; + const currentSelectedLotId = selectedLotId; + + try { + // ✅ Refresh lot data + await handleRowSelect(selectedRowId, true); // ✅ Preserve selection + + // ✅ Refresh main pick order details + await handleFetchAllPickOrderDetails(); + + // ✅ Restore lot selection + setSelectedLotRowId(currentSelectedLotRowId); + setSelectedLotId(currentSelectedLotId); + + console.log("✅ Data refreshed with selection preserved"); + } catch (error) { + console.error("❌ Error refreshing data:", error); + } + }, [selectedRowId, selectedLotRowId, selectedLotId, handleRowSelect, handleFetchAllPickOrderDetails]); // 自定义主表格组件 const CustomMainTable = () => { @@ -740,7 +784,7 @@ const PickExecution: React.FC = ({ filterArgs }) => { const availableQty = line.availableQty ?? 0; const balanceToPick = Math.max(0, availableQty - line.requiredQty); // 确保不为负数 const totalPickedQty = getTotalPickedQty(line.id); - + const actualPickedQty = line.pickedQty ?? 0; return ( = ({ filterArgs }) => { }}> {availableQty.toLocaleString()} {/* 添加千位分隔符 */} - {totalPickedQty} + {actualPickedQty} {line.uomDesc} {line.targetDate} diff --git a/src/components/PickOrderSearch/PickQcStockInModalVer3.tsx b/src/components/PickOrderSearch/PickQcStockInModalVer3.tsx index f7c91c9..745b0da 100644 --- a/src/components/PickOrderSearch/PickQcStockInModalVer3.tsx +++ b/src/components/PickOrderSearch/PickQcStockInModalVer3.tsx @@ -40,6 +40,8 @@ interface ExtendedQcItem extends QcItemWithChecks { qcPassed?: boolean; failQty?: number; remarks?: string; + order?: number; // ✅ Add order property + stableId?: string; // ✅ Also add stableId for better row identification } const style = { @@ -195,15 +197,22 @@ const PickQcStockInModalVer3: React.FC = ({ // ✅ 修改:在组件开始时自动设置失败数量 useEffect(() => { - if (itemDetail && qcItems.length > 0) { - // ✅ 自动将 Lot Required Pick Qty 设置为所有失败项目的 failQty - const updatedQcItems = qcItems.map(item => ({ - ...item, - failQty: itemDetail.requiredQty || 0 // 使用 Lot Required Pick Qty - })); - setQcItems(updatedQcItems); + if (itemDetail && qcItems.length > 0 && selectedLotId) { + // ✅ 获取选中的批次数据 + const selectedLot = lotData.find(lot => lot.stockOutLineId === selectedLotId); + if (selectedLot) { + // ✅ 自动将 Lot Required Pick Qty 设置为所有失败项目的 failQty + const updatedQcItems = qcItems.map((item, index) => ({ + ...item, + failQty: selectedLot.requiredQty || 0, // 使用 Lot Required Pick Qty + // ✅ Add stable order and ID fields + order: index, + stableId: `qc-${item.id}-${index}` + })); + setQcItems(updatedQcItems); + } } - }, [itemDetail, qcItems.length]); + }, [itemDetail, qcItems.length, selectedLotId, lotData]); // ✅ 修改:移除 alert 弹窗,改为控制台日志 const onSubmitQc = useCallback>( @@ -215,7 +224,8 @@ const PickQcStockInModalVer3: React.FC = ({ const acceptQty = Number(accQty) || null; const validationErrors : string[] = []; - + const selectedLot = lotData.find(lot => lot.stockOutLineId === selectedLotId); + const itemsWithoutResult = qcItems.filter(item => item.qcPassed === undefined); if (itemsWithoutResult.length > 0) { validationErrors.push(`${t("QC items without result")}: ${itemsWithoutResult.map(item => item.code).join(", ")}`); @@ -234,7 +244,7 @@ const PickQcStockInModalVer3: React.FC = ({ qcItem: item.code, qcDescription: item.description || "", isPassed: item.qcPassed, - failQty: item.qcPassed ? 0 : (itemDetail?.requiredQty || 0), + failQty: item.qcPassed ? 0 : (selectedLot?.requiredQty || 0), remarks: item.remarks || "", })), }; @@ -248,7 +258,7 @@ const PickQcStockInModalVer3: React.FC = ({ } // ✅ Fix: Update stock out line status based on QC decision - if (selectedLotId && qcData.qcAccept) { + if (selectedLotId) { // ✅ Remove qcData.qcAccept condition try { const allPassed = qcData.qcItems.every(item => item.isPassed); @@ -261,17 +271,19 @@ const PickQcStockInModalVer3: React.FC = ({ }); // ✅ Fix: 1. Update stock out line status with required qty field - await updateStockOutLineStatus({ - id: selectedLotId, - status: newStockOutLineStatus, - qty: itemDetail?.requiredQty || 0 // ✅ Add required qty field - }); + if (selectedLot) { + await updateStockOutLineStatus({ + id: selectedLotId, + status: newStockOutLineStatus, + qty: selectedLot.requiredQty || 0 + }); + } else { + console.warn("Selected lot not found for stock out line status update"); + } // ✅ Fix: 2. If QC failed, also update inventory lot line status if (!allPassed) { try { - // ✅ Fix: Get the correct lot data - const selectedLot = lotData.find(lot => lot.stockOutLineId === selectedLotId); if (selectedLot) { console.log("Updating inventory lot line status for failed QC:", { inventoryLotLineId: selectedLot.lotId, @@ -280,7 +292,7 @@ const PickQcStockInModalVer3: React.FC = ({ await updateInventoryLotLineStatus({ inventoryLotLineId: selectedLot.lotId, - status: 'unavailable' // ✅ Use correct backend enum value + status: 'unavailable' }); console.log("Inventory lot line status updated to unavailable"); } else { @@ -288,7 +300,6 @@ const PickQcStockInModalVer3: React.FC = ({ } } catch (error) { console.error("Failed to update inventory lot line status:", error); - // ✅ Don't fail the entire operation, just log the error } } @@ -300,12 +311,6 @@ const PickQcStockInModalVer3: React.FC = ({ } } catch (error) { console.error("Error updating stock out line status after QC:", error); - // ✅ Log detailed error information - if (error instanceof Error) { - console.error("Error details:", error.message); - console.error("Error stack:", error.stack); - } - // ✅ Don't fail the entire QC submission, just log the error } } @@ -362,15 +367,20 @@ const PickQcStockInModalVer3: React.FC = ({ value={current.qcPassed === undefined ? "" : (current.qcPassed ? "true" : "false")} onChange={(e) => { const value = e.target.value === "true"; - setQcItems((prev) => - prev.map((r): ExtendedQcItem => (r.id === params.id ? { ...r, qcPassed: value } : r)) + // ✅ Simple state update + setQcItems(prev => + prev.map(item => + item.id === params.id + ? { ...item, qcPassed: value } + : item + ) ); }} name={`qcPassed-${params.id}`} > } + control={} label="合格" sx={{ color: current.qcPassed === true ? "green" : "inherit", @@ -379,7 +389,7 @@ const PickQcStockInModalVer3: React.FC = ({ /> } + control={} label="不合格" sx={{ color: current.qcPassed === false ? "red" : "inherit", @@ -400,7 +410,7 @@ const PickQcStockInModalVer3: React.FC = ({ type="number" size="small" // ✅ 修改:失败项目自动显示 Lot Required Pick Qty - value={!params.row.qcPassed ? (itemDetail?.requiredQty || 0) : 0} + value={!params.row.qcPassed ? (0) : 0} disabled={params.row.qcPassed} // ✅ 移除 onChange,因为数量是固定的 // onChange={(e) => { @@ -444,6 +454,27 @@ const PickQcStockInModalVer3: React.FC = ({ [t], ); + // ✅ Add stable update function + const handleQcResultChange = useCallback((itemId: number, qcPassed: boolean) => { + setQcItems(prevItems => + prevItems.map(item => + item.id === itemId + ? { ...item, qcPassed } + : item + ) + ); + }, []); + + // ✅ Remove duplicate functions + const getRowId = useCallback((row: any) => { + return row.id; // Just use the original ID + }, []); + + // ✅ Remove complex sorting logic + // const stableQcItems = useMemo(() => { ... }); // Remove + // const sortedQcItems = useMemo(() => { ... }); // Remove + + // ✅ Use qcItems directly in DataGrid return ( <> @@ -481,8 +512,9 @@ const PickQcStockInModalVer3: React.FC = ({ diff --git a/src/components/PickOrderSearch/assignTo copy.tsx b/src/components/PickOrderSearch/assignTo copy.tsx new file mode 100644 index 0000000..87ab821 --- /dev/null +++ b/src/components/PickOrderSearch/assignTo copy.tsx @@ -0,0 +1,511 @@ +"use client"; +import { + Autocomplete, + Box, + Button, + CircularProgress, + FormControl, + Grid, + Modal, + TextField, + Typography, + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableRow, + Paper, + Checkbox, + TablePagination, +} from "@mui/material"; +import { useCallback, useEffect, useMemo, useState } from "react"; +import { useTranslation } from "react-i18next"; +import { + newassignPickOrder, + AssignPickOrderInputs, + releaseAssignedPickOrders, + fetchPickOrderWithStockClient, // Add this import +} from "@/app/api/pickOrder/actions"; +import { fetchNameList, NameList } from "@/app/api/user/actions"; +import { + FormProvider, + useForm, +} from "react-hook-form"; +import { isEmpty, upperFirst, groupBy } from "lodash"; +import { OUTPUT_DATE_FORMAT, arrayToDayjs } from "@/app/utils/formatUtil"; +import useUploadContext from "../UploadProvider/useUploadContext"; +import dayjs from "dayjs"; +import arraySupport from "dayjs/plugin/arraySupport"; +import SearchBox, { Criterion } from "../SearchBox"; +import { sortBy, uniqBy } from "lodash"; +import { createStockOutLine, CreateStockOutLine, fetchPickOrderDetails } from "@/app/api/pickOrder/actions"; +dayjs.extend(arraySupport); + +interface Props { + filterArgs: Record; +} + +// Update the interface to match the new API response structure +interface PickOrderRow { + id: string; + code: string; + targetDate: string; + type: string; + status: string; + assignTo: number; + groupName: string; + consoCode?: string; + pickOrderLines: PickOrderLineRow[]; +} + +interface PickOrderLineRow { + id: number; + itemId: number; + itemCode: string; + itemName: string; + availableQty: number | null; + requiredQty: number; + uomCode: string; + uomDesc: string; + suggestedList: any[]; +} + +const style = { + position: "absolute", + top: "50%", + left: "50%", + transform: "translate(-50%, -50%)", + bgcolor: "background.paper", + pt: 5, + px: 5, + pb: 10, + width: { xs: "100%", sm: "100%", md: "100%" }, +}; + +const AssignTo: React.FC = ({ filterArgs }) => { + const { t } = useTranslation("pickOrder"); + const { setIsUploading } = useUploadContext(); + const [isUploading, setIsUploadingLocal] = useState(false); + // Update state to use pick order data directly + const [selectedPickOrderIds, setSelectedPickOrderIds] = useState([]); + const [filteredPickOrders, setFilteredPickOrders] = useState([]); + const [isLoadingItems, setIsLoadingItems] = useState(false); + const [pagingController, setPagingController] = useState({ + pageNum: 1, + pageSize: 10, + }); + const [totalCountItems, setTotalCountItems] = useState(); + const [modalOpen, setModalOpen] = useState(false); + const [usernameList, setUsernameList] = useState([]); + const [searchQuery, setSearchQuery] = useState>({}); + const [originalPickOrderData, setOriginalPickOrderData] = useState([]); + + const formProps = useForm(); + const errors = formProps.formState.errors; + + // Update the handler functions to work with string IDs + const handlePickOrderSelect = useCallback((pickOrderId: string, checked: boolean) => { + if (checked) { + setSelectedPickOrderIds(prev => [...prev, pickOrderId]); + } else { + setSelectedPickOrderIds(prev => prev.filter(id => id !== pickOrderId)); + } + }, []); + + const isPickOrderSelected = useCallback((pickOrderId: string) => { + return selectedPickOrderIds.includes(pickOrderId); + }, [selectedPickOrderIds]); + + // Update the fetch function to use the correct endpoint + const fetchNewPageItems = useCallback( + async (pagingController: Record, filterArgs: Record) => { + setIsLoadingItems(true); + try { + const params = { + ...pagingController, + ...filterArgs, + pageNum: (pagingController.pageNum || 1) - 1, + pageSize: pagingController.pageSize || 10, + // Filter for assigned status only + status: "assigned" + }; + + const res = await fetchPickOrderWithStockClient(params); + + if (res && res.records) { + // Convert pick order data to the expected format + const pickOrderRows: PickOrderRow[] = res.records.map((pickOrder: any) => ({ + id: pickOrder.id, + code: pickOrder.code, + targetDate: pickOrder.targetDate, + type: pickOrder.type, + status: pickOrder.status, + assignTo: pickOrder.assignTo, + groupName: pickOrder.groupName || "No Group", + consoCode: pickOrder.consoCode, + pickOrderLines: pickOrder.pickOrderLines || [] + })); + + setOriginalPickOrderData(pickOrderRows); + setFilteredPickOrders(pickOrderRows); + setTotalCountItems(res.total); + } else { + setFilteredPickOrders([]); + setTotalCountItems(0); + } + } catch (error) { + console.error("Error fetching pick orders:", error); + setFilteredPickOrders([]); + setTotalCountItems(0); + } finally { + setIsLoadingItems(false); + } + }, + [], + ); + + // Handle Release operation +// Handle Release operation +const handleRelease = useCallback(async () => { + if (selectedPickOrderIds.length === 0) return; + + setIsUploading(true); + try { + // Get the assigned user from the selected pick orders + const selectedPickOrders = filteredPickOrders.filter(pickOrder => + selectedPickOrderIds.includes(pickOrder.id) + ); + + // Check if all selected pick orders have the same assigned user + const assignedUsers = selectedPickOrders.map(po => po.assignTo).filter(Boolean); + if (assignedUsers.length === 0) { + alert("Selected pick orders are not assigned to any user."); + return; + } + + const assignToValue = assignedUsers[0]; + + // Validate that all pick orders are assigned to the same user + const allSameUser = assignedUsers.every(userId => userId === assignToValue); + if (!allSameUser) { + alert("All selected pick orders must be assigned to the same user."); + return; + } + + console.log("Using assigned user:", assignToValue); + console.log("selectedPickOrderIds:", selectedPickOrderIds); + + const releaseRes = await releaseAssignedPickOrders({ + pickOrderIds: selectedPickOrderIds.map(id => parseInt(id)), + assignTo: assignToValue + }); + + if (releaseRes.code === "SUCCESS") { + console.log("Pick orders released successfully"); + + // Get the consoCode from the response + const consoCode = (releaseRes.entity as any)?.consoCode; + + if (consoCode) { + // Create StockOutLine records for each pick order line + for (const pickOrder of selectedPickOrders) { + for (const line of pickOrder.pickOrderLines) { + try { + const stockOutLineData = { + consoCode: consoCode, + pickOrderLineId: line.id, + inventoryLotLineId: 0, // This will be set when user scans QR code + qty: line.requiredQty, + }; + + console.log("Creating stock out line:", stockOutLineData); + await createStockOutLine(stockOutLineData); + } catch (error) { + console.error("Error creating stock out line for line", line.id, error); + } + } + } + } + + fetchNewPageItems(pagingController, filterArgs); + } else { + console.error("Release failed:", releaseRes.message); + } + } catch (error) { + console.error("Error releasing pick orders:", error); + } finally { + setIsUploading(false); + } +}, [selectedPickOrderIds, filteredPickOrders, setIsUploading, fetchNewPageItems, pagingController, filterArgs]); + + // Update search criteria to match the new data structure + const searchCriteria: Criterion[] = useMemo( + () => [ + { + label: t("Pick Order Code"), + paramName: "code", + type: "text", + }, + { + label: t("Group Code"), + paramName: "groupName", + type: "text", + }, + { + label: t("Target Date From"), + label2: t("Target Date To"), + paramName: "targetDate", + type: "dateRange", + }, + ], + [t], + ); + + // Update search function to work with pick order data + const handleSearch = useCallback((query: Record) => { + setSearchQuery({ ...query }); + + const filtered = originalPickOrderData.filter((pickOrder) => { + const pickOrderTargetDateStr = arrayToDayjs(pickOrder.targetDate); + + const codeMatch = !query.code || + pickOrder.code?.toLowerCase().includes((query.code || "").toLowerCase()); + + const groupNameMatch = !query.groupName || + pickOrder.groupName?.toLowerCase().includes((query.groupName || "").toLowerCase()); + + // Date range search + let dateMatch = true; + if (query.targetDate || query.targetDateTo) { + try { + if (query.targetDate && !query.targetDateTo) { + const fromDate = dayjs(query.targetDate); + dateMatch = pickOrderTargetDateStr.isSame(fromDate, 'day') || + pickOrderTargetDateStr.isAfter(fromDate, 'day'); + } else if (!query.targetDate && query.targetDateTo) { + const toDate = dayjs(query.targetDateTo); + dateMatch = pickOrderTargetDateStr.isSame(toDate, 'day') || + pickOrderTargetDateStr.isBefore(toDate, 'day'); + } else if (query.targetDate && query.targetDateTo) { + const fromDate = dayjs(query.targetDate); + const toDate = dayjs(query.targetDateTo); + dateMatch = (pickOrderTargetDateStr.isSame(fromDate, 'day') || + pickOrderTargetDateStr.isAfter(fromDate, 'day')) && + (pickOrderTargetDateStr.isSame(toDate, 'day') || + pickOrderTargetDateStr.isBefore(toDate, 'day')); + } + } catch (error) { + console.error("Date parsing error:", error); + dateMatch = true; + } + } + + return codeMatch && groupNameMatch && dateMatch; + }); + + setFilteredPickOrders(filtered); + }, [originalPickOrderData]); + + const handleReset = useCallback(() => { + setSearchQuery({}); + setFilteredPickOrders(originalPickOrderData); + setTimeout(() => { + setSearchQuery({}); + }, 0); + }, [originalPickOrderData]); + + // Pagination handlers + const handlePageChange = useCallback((event: unknown, newPage: number) => { + const newPagingController = { + ...pagingController, + pageNum: newPage + 1, + }; + setPagingController(newPagingController); + }, [pagingController]); + + const handlePageSizeChange = useCallback((event: React.ChangeEvent) => { + const newPageSize = parseInt(event.target.value, 10); + const newPagingController = { + pageNum: 1, + pageSize: newPageSize, + }; + setPagingController(newPagingController); + }, []); + + // Component mount effect + useEffect(() => { + fetchNewPageItems(pagingController, filterArgs || {}); + }, []); + + // Dependencies change effect + useEffect(() => { + if (pagingController && (filterArgs || {})) { + fetchNewPageItems(pagingController, filterArgs || {}); + } + }, [pagingController, filterArgs, fetchNewPageItems]); + + useEffect(() => { + const loadUsernameList = async () => { + try { + const res = await fetchNameList(); + if (res) { + setUsernameList(res); + } + } catch (error) { + console.error("Error loading username list:", error); + } + }; + loadUsernameList(); + }, []); + + // Update the table component to work with pick order data directly + const CustomPickOrderTable = () => { + // Helper function to get user name + const getUserName = useCallback((assignToId: number | null | undefined) => { + if (!assignToId) return '-'; + const user = usernameList.find(u => u.id === assignToId); + return user ? user.name : `User ${assignToId}`; + }, [usernameList]); + + return ( + <> + + + + + {t("Selected")} + {t("Pick Order Code")} + {t("Group Code")} + {t("Item Code")} + {t("Item Name")} + {t("Order Quantity")} + {t("Current Stock")} + {t("Stock Unit")} + {t("Target Date")} + {t("Assigned To")} + + + + {filteredPickOrders.length === 0 ? ( + + + + {t("No data available")} + + + + ) : ( + filteredPickOrders.map((pickOrder) => ( + pickOrder.pickOrderLines.map((line: PickOrderLineRow, index: number) => ( + + {/* Checkbox - only show for first line of each pick order */} + + {index === 0 ? ( + handlePickOrderSelect(pickOrder.id, e.target.checked)} + disabled={!isEmpty(pickOrder.consoCode)} + /> + ) : null} + + {/* Pick Order Code - only show for first line */} + + {index === 0 ? pickOrder.code : null} + + {/* Group Name - only show for first line */} + + {index === 0 ? pickOrder.groupName : null} + + {/* Item Code */} + {line.itemCode} + {/* Item Name */} + {line.itemName} + + {/* Order Quantity */} + {line.requiredQty} + + {/* Current Stock */} + + 0 ? "success.main" : "error.main"} + sx={{ fontWeight: line.availableQty && line.availableQty > 0 ? 'bold' : 'normal' }} + > + {(line.availableQty || 0).toLocaleString()} + + + + {/* Unit */} + {line.uomDesc} + + {/* Target Date - only show for first line */} + + {index === 0 ? ( + arrayToDayjs(pickOrder.targetDate) + .add(-1, "month") + .format(OUTPUT_DATE_FORMAT) + ) : null} + + + {/* Assigned To - only show for first line */} + + {index === 0 ? ( + + {getUserName(pickOrder.assignTo)} + + ) : null} + + + )) + )) + )} + +
+
+ + + `${from}-${to} of ${count !== -1 ? count : `more than ${to}`}` + } + /> + + ); + }; + + return ( + <> + + + + {isLoadingItems ? ( + + ) : ( + + )} + + + + + + + + + ); +}; + +export default AssignTo; \ No newline at end of file