| @@ -19,7 +19,6 @@ import { | |||
| TablePagination, | |||
| Modal, | |||
| Chip, | |||
| Chip, | |||
| } from "@mui/material"; | |||
| import TestQrCodeProvider from '../QrCodeScannerProvider/TestQrCodeProvider'; | |||
| import { fetchLotDetail } from "@/app/api/inventory/actions"; | |||
| @@ -46,7 +45,7 @@ import { | |||
| DoPickOrderDetail, // ✅ 必须添加 | |||
| fetchFGPickOrdersByUserId | |||
| } from "@/app/api/pickOrder/actions"; | |||
| import FGPickOrderInfoCard from "./FGPickOrderInfoCard"; | |||
| import FGPickOrderInfoCard from "./FGPickOrderInfoCard"; | |||
| import LotConfirmationModal from "./LotConfirmationModal"; | |||
| //import { fetchItem } from "@/app/api/settings/item"; | |||
| @@ -445,13 +444,13 @@ const [isConfirmingLot, setIsConfirmingLot] = useState(false); | |||
| return allCompleted; | |||
| }, []); | |||
| const fetchAllCombinedLotData = useCallback(async (userId?: number, pickOrderIdOverride?: number) => { | |||
| const fetchAllCombinedLotData = useCallback(async (userId?: number, pickOrderIdOverride?: number) => { | |||
| setCombinedDataLoading(true); | |||
| try { | |||
| const userIdToUse = userId || currentUserId; | |||
| console.log("🔍 fetchAllCombinedLotData called with userId:", userIdToUse); | |||
| console.log("🔍 fetchAllCombinedLotData called with userId:", userIdToUse); | |||
| if (!userIdToUse) { | |||
| console.warn("⚠️ No userId available, skipping API call"); | |||
| @@ -761,7 +760,6 @@ const [isConfirmingLot, setIsConfirmingLot] = useState(false); | |||
| console.log("✅ Transformed flat lot data:", flatLotData); | |||
| console.log("🔍 Total items (including null stock):", flatLotData.length); | |||
| console.log("🔍 Total items (including null stock):", flatLotData.length); | |||
| setCombinedLotData(flatLotData); | |||
| setOriginalCombinedData(flatLotData); | |||
| @@ -777,7 +775,6 @@ const [isConfirmingLot, setIsConfirmingLot] = useState(false); | |||
| setCombinedDataLoading(false); | |||
| } | |||
| }, [currentUserId, selectedPickOrderId, checkAllLotsCompleted]); | |||
| }, [currentUserId, selectedPickOrderId, checkAllLotsCompleted]); | |||
| // ✅ Add effect to check completion when lot data changes | |||
| useEffect(() => { | |||
| if (combinedLotData.length > 0) { | |||
| @@ -1609,24 +1606,9 @@ const handleSubmitPickQtyWithQty = useCallback(async (lot: any, submitQty: numbe | |||
| console.error("Error switching pick order:", error); | |||
| } finally { | |||
| setPickOrderSwitching(false); | |||
| } | |||
| }, [pickOrderSwitching, currentUserId, fetchAllCombinedLotData]); | |||
| const handlePickOrderSwitch = useCallback(async (pickOrderId: number) => { | |||
| if (pickOrderSwitching) return; | |||
| setPickOrderSwitching(true); | |||
| try { | |||
| console.log("🔍 Switching to pick order:", pickOrderId); | |||
| setSelectedPickOrderId(pickOrderId); | |||
| // ✅ 强制刷新数据,确保显示正确的 pick order 数据 | |||
| await fetchAllCombinedLotData(currentUserId, pickOrderId); | |||
| } catch (error) { | |||
| console.error("Error switching pick order:", error); | |||
| } finally { | |||
| setPickOrderSwitching(false); | |||
| } | |||
| }, [pickOrderSwitching, currentUserId, fetchAllCombinedLotData]); | |||
| } | |||
| }, [pickOrderSwitching, currentUserId, fetchAllCombinedLotData]); | |||
| const handleStopScan = useCallback(() => { | |||
| console.log("⏹️ Stopping manual QR scan..."); | |||
| setIsManualScanning(false); | |||
| @@ -1791,40 +1773,8 @@ const handleSubmitPickQtyWithQty = useCallback(async (lot: any, submitQty: numbe | |||
| <strong>{t("Departure Time")}:</strong> {fgPickOrders[0].DepartureTime || '-'} | |||
| </Typography> | |||
| </Stack> | |||
| lotData={combinedLotData} | |||
| onScanLot={handleQrCodeSubmit} | |||
| filterActive={(lot) => ( | |||
| lot.lotAvailability !== 'rejected' && | |||
| lot.stockOutLineStatus !== 'rejected' && | |||
| lot.stockOutLineStatus !== 'completed' | |||
| )} | |||
| > | |||
| <FormProvider {...formProps}> | |||
| <Stack spacing={2}> | |||
| {/* DO Header */} | |||
| {fgPickOrdersLoading ? ( | |||
| <Box sx={{ display: 'flex', justifyContent: 'center', p: 2 }}> | |||
| <CircularProgress size={20} /> | |||
| </Box> | |||
| ) : ( | |||
| fgPickOrders.length > 0 && ( | |||
| <Paper sx={{ p: 2 }}> | |||
| <Stack spacing={2}> | |||
| {/* 基本信息 */} | |||
| <Stack direction="row" spacing={4} useFlexGap flexWrap="wrap"> | |||
| <Typography variant="subtitle1"> | |||
| <strong>{t("Shop Name")}:</strong> {fgPickOrders[0].shopName || '-'} | |||
| </Typography> | |||
| <Typography variant="subtitle1"> | |||
| <strong>{t("Store ID")}:</strong> {fgPickOrders[0].storeId || '-'} | |||
| </Typography> | |||
| <Typography variant="subtitle1"> | |||
| <strong>{t("Ticket No.")}:</strong> {fgPickOrders[0].ticketNo || '-'} | |||
| </Typography> | |||
| <Typography variant="subtitle1"> | |||
| <strong>{t("Departure Time")}:</strong> {fgPickOrders[0].DepartureTime || '-'} | |||
| </Typography> | |||
| </Stack> | |||
| </Stack> | |||
| </Paper> | |||
| @@ -2163,160 +2113,10 @@ paginatedData.map((lot, index) => { | |||
| /> | |||
| )} | |||
| </FormProvider> | |||
| <TableCell align="center"> | |||
| <Box sx={{ display: 'flex', justifyContent: 'center' }}> | |||
| {isIssueLot ? ( | |||
| // ✅ Issue lot 只显示 Issue 按钮 | |||
| <Button | |||
| variant="outlined" | |||
| size="small" | |||
| onClick={() => handlePickExecutionForm(lot)} | |||
| disabled={true} | |||
| sx={{ | |||
| fontSize: '0.7rem', | |||
| py: 0.5, | |||
| minHeight: '28px', | |||
| minWidth: '60px', | |||
| borderColor: 'warning.main', | |||
| color: 'warning.main' | |||
| }} | |||
| title="Rejected lot - Issue only" | |||
| > | |||
| {t("Issue")} | |||
| </Button> | |||
| ) : ( | |||
| // ✅ Normal lot 显示两个按钮 | |||
| <Stack direction="row" spacing={1} alignItems="center"> | |||
| <Button | |||
| variant="contained" | |||
| onClick={() => { | |||
| const lotKey = `${lot.pickOrderLineId}-${lot.lotId}`; | |||
| const submitQty = lot.requiredQty || lot.pickOrderLineRequiredQty; | |||
| handlePickQtyChange(lotKey, submitQty); | |||
| handleSubmitPickQtyWithQty(lot, submitQty); | |||
| }} | |||
| disabled={ | |||
| lot.lotAvailability === 'expired' || | |||
| lot.lotAvailability === 'status_unavailable' || | |||
| lot.lotAvailability === 'rejected' || | |||
| lot.stockOutLineStatus === 'completed' || | |||
| lot.stockOutLineStatus === 'pending' | |||
| } | |||
| sx={{ fontSize: '0.75rem', py: 0.5, minHeight: '28px', minWidth: '70px' }} | |||
| > | |||
| {t("Submit")} | |||
| </Button> | |||
| <Button | |||
| variant="outlined" | |||
| size="small" | |||
| onClick={() => handlePickExecutionForm(lot)} | |||
| disabled={ | |||
| lot.lotAvailability === 'expired' || | |||
| lot.lotAvailability === 'status_unavailable' || | |||
| lot.lotAvailability === 'rejected' || | |||
| lot.stockOutLineStatus === 'completed' || | |||
| lot.stockOutLineStatus === 'pending' | |||
| } | |||
| sx={{ | |||
| fontSize: '0.7rem', | |||
| py: 0.5, | |||
| minHeight: '28px', | |||
| minWidth: '60px', | |||
| borderColor: 'warning.main', | |||
| color: 'warning.main' | |||
| }} | |||
| title="Report missing or bad items" | |||
| > | |||
| {t("Issue")} | |||
| </Button> | |||
| </Stack> | |||
| )} | |||
| </Box> | |||
| </TableCell> | |||
| </TableRow> | |||
| ); | |||
| }) | |||
| )} | |||
| </TableBody> | |||
| </Table> | |||
| </TableContainer> | |||
| <TablePagination | |||
| component="div" | |||
| count={combinedLotData.length} | |||
| page={paginationController.pageNum} | |||
| rowsPerPage={paginationController.pageSize} | |||
| onPageChange={handlePageChange} | |||
| onRowsPerPageChange={handlePageSizeChange} | |||
| rowsPerPageOptions={[10, 25, 50]} | |||
| labelRowsPerPage={t("Rows per page")} | |||
| labelDisplayedRows={({ from, to, count }) => | |||
| `${from}-${to} of ${count !== -1 ? count : `more than ${to}`}` | |||
| } | |||
| /> | |||
| </Box> | |||
| </Stack> | |||
| {/* ✅ 保留:QR Code Modal */} | |||
| <QrCodeModal | |||
| open={qrModalOpen} | |||
| onClose={() => { | |||
| setQrModalOpen(false); | |||
| setSelectedLotForQr(null); | |||
| stopScan(); | |||
| resetScan(); | |||
| }} | |||
| lot={selectedLotForQr} | |||
| combinedLotData={combinedLotData} | |||
| onQrCodeSubmit={handleQrCodeSubmitFromModal} | |||
| /> | |||
| {/* ✅ 保留:Lot Confirmation Modal */} | |||
| {lotConfirmationOpen && expectedLotData && scannedLotData && ( | |||
| <LotConfirmationModal | |||
| open={lotConfirmationOpen} | |||
| onClose={() => { | |||
| setLotConfirmationOpen(false); | |||
| setExpectedLotData(null); | |||
| setScannedLotData(null); | |||
| }} | |||
| onConfirm={handleLotConfirmation} | |||
| expectedLot={expectedLotData} | |||
| scannedLot={scannedLotData} | |||
| isLoading={isConfirmingLot} | |||
| /> | |||
| )} | |||
| {/* ✅ 保留:Good Pick Execution Form Modal */} | |||
| {pickExecutionFormOpen && selectedLotForExecutionForm && ( | |||
| <GoodPickExecutionForm | |||
| open={pickExecutionFormOpen} | |||
| onClose={() => { | |||
| setPickExecutionFormOpen(false); | |||
| setSelectedLotForExecutionForm(null); | |||
| }} | |||
| onSubmit={handlePickExecutionFormSubmit} | |||
| selectedLot={selectedLotForExecutionForm} | |||
| selectedPickOrderLine={{ | |||
| id: selectedLotForExecutionForm.pickOrderLineId, | |||
| itemId: selectedLotForExecutionForm.itemId, | |||
| itemCode: selectedLotForExecutionForm.itemCode, | |||
| itemName: selectedLotForExecutionForm.itemName, | |||
| pickOrderCode: selectedLotForExecutionForm.pickOrderCode, | |||
| availableQty: selectedLotForExecutionForm.availableQty || 0, | |||
| requiredQty: selectedLotForExecutionForm.requiredQty || 0, | |||
| // uomCode: selectedLotForExecutionForm.uomCode || '', | |||
| uomDesc: selectedLotForExecutionForm.uomDesc || '', | |||
| pickedQty: selectedLotForExecutionForm.actualPickQty || 0, | |||
| uomShortDesc: selectedLotForExecutionForm.uomShortDesc || '', | |||
| suggestedList: [] | |||
| }} | |||
| pickOrderId={selectedLotForExecutionForm.pickOrderId} | |||
| pickOrderCreateDate={new Date()} | |||
| /> | |||
| )} | |||
| </FormProvider> | |||
| </TestQrCodeProvider> | |||
| ); | |||
| }; | |||