| @@ -19,7 +19,6 @@ import { | |||||
| TablePagination, | TablePagination, | ||||
| Modal, | Modal, | ||||
| Chip, | Chip, | ||||
| Chip, | |||||
| } from "@mui/material"; | } from "@mui/material"; | ||||
| import TestQrCodeProvider from '../QrCodeScannerProvider/TestQrCodeProvider'; | import TestQrCodeProvider from '../QrCodeScannerProvider/TestQrCodeProvider'; | ||||
| import { fetchLotDetail } from "@/app/api/inventory/actions"; | import { fetchLotDetail } from "@/app/api/inventory/actions"; | ||||
| @@ -46,7 +45,7 @@ import { | |||||
| DoPickOrderDetail, // ✅ 必须添加 | DoPickOrderDetail, // ✅ 必须添加 | ||||
| fetchFGPickOrdersByUserId | fetchFGPickOrdersByUserId | ||||
| } from "@/app/api/pickOrder/actions"; | } from "@/app/api/pickOrder/actions"; | ||||
| import FGPickOrderInfoCard from "./FGPickOrderInfoCard"; | |||||
| import FGPickOrderInfoCard from "./FGPickOrderInfoCard"; | import FGPickOrderInfoCard from "./FGPickOrderInfoCard"; | ||||
| import LotConfirmationModal from "./LotConfirmationModal"; | import LotConfirmationModal from "./LotConfirmationModal"; | ||||
| //import { fetchItem } from "@/app/api/settings/item"; | //import { fetchItem } from "@/app/api/settings/item"; | ||||
| @@ -445,13 +444,13 @@ const [isConfirmingLot, setIsConfirmingLot] = useState(false); | |||||
| return allCompleted; | return allCompleted; | ||||
| }, []); | }, []); | ||||
| const fetchAllCombinedLotData = useCallback(async (userId?: number, pickOrderIdOverride?: number) => { | const fetchAllCombinedLotData = useCallback(async (userId?: number, pickOrderIdOverride?: number) => { | ||||
| const fetchAllCombinedLotData = useCallback(async (userId?: number, pickOrderIdOverride?: number) => { | |||||
| setCombinedDataLoading(true); | setCombinedDataLoading(true); | ||||
| try { | try { | ||||
| const userIdToUse = userId || currentUserId; | const userIdToUse = userId || currentUserId; | ||||
| console.log("🔍 fetchAllCombinedLotData called with userId:", userIdToUse); | console.log("🔍 fetchAllCombinedLotData called with userId:", userIdToUse); | ||||
| console.log("🔍 fetchAllCombinedLotData called with userId:", userIdToUse); | |||||
| if (!userIdToUse) { | if (!userIdToUse) { | ||||
| console.warn("⚠️ No userId available, skipping API call"); | 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("✅ Transformed flat lot data:", flatLotData); | ||||
| console.log("🔍 Total items (including null stock):", flatLotData.length); | console.log("🔍 Total items (including null stock):", flatLotData.length); | ||||
| console.log("🔍 Total items (including null stock):", flatLotData.length); | |||||
| setCombinedLotData(flatLotData); | setCombinedLotData(flatLotData); | ||||
| setOriginalCombinedData(flatLotData); | setOriginalCombinedData(flatLotData); | ||||
| @@ -777,7 +775,6 @@ const [isConfirmingLot, setIsConfirmingLot] = useState(false); | |||||
| setCombinedDataLoading(false); | setCombinedDataLoading(false); | ||||
| } | } | ||||
| }, [currentUserId, selectedPickOrderId, checkAllLotsCompleted]); | }, [currentUserId, selectedPickOrderId, checkAllLotsCompleted]); | ||||
| }, [currentUserId, selectedPickOrderId, checkAllLotsCompleted]); | |||||
| // ✅ Add effect to check completion when lot data changes | // ✅ Add effect to check completion when lot data changes | ||||
| useEffect(() => { | useEffect(() => { | ||||
| if (combinedLotData.length > 0) { | if (combinedLotData.length > 0) { | ||||
| @@ -1609,24 +1606,9 @@ const handleSubmitPickQtyWithQty = useCallback(async (lot: any, submitQty: numbe | |||||
| console.error("Error switching pick order:", error); | console.error("Error switching pick order:", error); | ||||
| } finally { | } finally { | ||||
| setPickOrderSwitching(false); | 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(() => { | const handleStopScan = useCallback(() => { | ||||
| console.log("⏹️ Stopping manual QR scan..."); | console.log("⏹️ Stopping manual QR scan..."); | ||||
| setIsManualScanning(false); | setIsManualScanning(false); | ||||
| @@ -1791,40 +1773,8 @@ const handleSubmitPickQtyWithQty = useCallback(async (lot: any, submitQty: numbe | |||||
| <strong>{t("Departure Time")}:</strong> {fgPickOrders[0].DepartureTime || '-'} | <strong>{t("Departure Time")}:</strong> {fgPickOrders[0].DepartureTime || '-'} | ||||
| </Typography> | </Typography> | ||||
| </Stack> | </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> | </Stack> | ||||
| </Paper> | </Paper> | ||||
| @@ -2163,160 +2113,10 @@ paginatedData.map((lot, index) => { | |||||
| /> | /> | ||||
| )} | )} | ||||
| </FormProvider> | </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> | </TestQrCodeProvider> | ||||
| ); | ); | ||||
| }; | }; | ||||