diff --git a/src/app/api/pickOrder/actions.ts b/src/app/api/pickOrder/actions.ts index 2e192c3..eb1c639 100644 --- a/src/app/api/pickOrder/actions.ts +++ b/src/app/api/pickOrder/actions.ts @@ -113,6 +113,7 @@ export interface GetPickOrderLineInfo { requiredQty: number; uomShortDesc: string; uomDesc: string; + suggestedList: any[]; pickedQty: number; } diff --git a/src/components/FinishedGoodSearch/FGPickOrderTicketReleaseTable.tsx b/src/components/FinishedGoodSearch/FGPickOrderTicketReleaseTable.tsx new file mode 100644 index 0000000..b63c939 --- /dev/null +++ b/src/components/FinishedGoodSearch/FGPickOrderTicketReleaseTable.tsx @@ -0,0 +1,91 @@ +"use client"; + +import React, { useState } from 'react'; +import { + Box, + Typography, + FormControl, + InputLabel, + Select, + MenuItem, + Card, + CardContent, + Stack, +} from '@mui/material'; +import { useTranslation } from 'react-i18next'; +import dayjs from 'dayjs'; + +const FGPickOrderTicketReleaseTable: React.FC = () => { + const { t } = useTranslation("pickOrder"); + const [selectedDate, setSelectedDate] = useState("today"); + const [selectedFloor, setSelectedFloor] = useState(""); + + const getDateLabel = (offset: number) => { + return dayjs().add(offset, 'day').format('YYYY-MM-DD'); + }; + + return ( + + + {/* Title */} + + Ticket Release Table + + + {/* Dropdown Menus */} + + {/* Date Selection Dropdown */} + + {t("Select Date")} + + + + {/* Floor Selection Dropdown */} + + {t("Floor")} + + + + + {/* Table content will go here */} + + + {/* Add your table component here */} + Table content goes here... + + + + + ); +}; + +export default FGPickOrderTicketReleaseTable; \ No newline at end of file diff --git a/src/components/FinishedGoodSearch/FinishedGoodSearch.tsx b/src/components/FinishedGoodSearch/FinishedGoodSearch.tsx index 1186aa3..e6b43df 100644 --- a/src/components/FinishedGoodSearch/FinishedGoodSearch.tsx +++ b/src/components/FinishedGoodSearch/FinishedGoodSearch.tsx @@ -38,6 +38,7 @@ import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; import { DatePicker } from '@mui/x-date-pickers/DatePicker'; import dayjs, { Dayjs } from 'dayjs'; +import FGPickOrderTicketReleaseTable from "./FGPickOrderTicketReleaseTable"; interface Props { pickOrders: PickOrderResult[]; @@ -217,228 +218,7 @@ const PickOrderSearch: React.FC = ({ pickOrders }) => { },[t]); - const handleDN = useCallback(async () =>{ - const askNumofCarton = await Swal.fire({ - title: t("Enter the number of cartons: "), - icon: "info", - input: "number", - inputPlaceholder: t("Number of cartons"), - inputAttributes:{ - min: "1", - step: "1" - }, - inputValidator: (value) => { - if(!value){ - return t("You need to enter a number") - } - if(parseInt(value) < 1){ - return t("Number must be at least 1"); - } - return null - }, - showCancelButton: true, - confirmButtonText: t("Confirm"), - cancelButtonText: t("Cancel"), - confirmButtonColor: "#8dba00", - cancelButtonColor: "#F04438", - showLoaderOnConfirm: true, - allowOutsideClick: () => !Swal.isLoading() - }); - - if (askNumofCarton.isConfirmed) { - const numOfCartons = askNumofCarton.value; - try{ - if (fgPickOrdersData.length === 0) { - console.error("No FG Pick order data available"); - return; - } - - const currentFgOrder = fgPickOrdersData[0]; - - const printRequest = { - printerId: 1, - printQty: 1, - isDraft: false, - numOfCarton: numOfCartons, - deliveryOrderId: currentFgOrder.deliveryOrderId, - pickOrderId: currentFgOrder.pickOrderId - }; - - console.log("Printing Delivery Note with request: ", printRequest); - - const response = await printDN(printRequest); - - console.log("Print Delivery Note response: ", response); - - if(response.success){ - Swal.fire({ - position: "bottom-end", - icon: "success", - text: t("Printed Successfully."), - showConfirmButton: false, - timer: 1500 - }); - } else { - console.error("Print failed: ", response.message); - } - } catch(error){ - console.error("error: ", error) - } - } - },[t, fgPickOrdersData]); - - const handleDNandLabel = useCallback(async () =>{ - const askNumofCarton = await Swal.fire({ - title: t("Enter the number of cartons: "), - icon: "info", - input: "number", - inputPlaceholder: t("Number of cartons"), - inputAttributes:{ - min: "1", - step: "1" - }, - inputValidator: (value) => { - if(!value){ - return t("You need to enter a number") - } - if(parseInt(value) < 1){ - return t("Number must be at least 1"); - } - return null - }, - showCancelButton: true, - confirmButtonText: t("Confirm"), - cancelButtonText: t("Cancel"), - confirmButtonColor: "#8dba00", - cancelButtonColor: "#F04438", - showLoaderOnConfirm: true, - allowOutsideClick: () => !Swal.isLoading() - }); - - if (askNumofCarton.isConfirmed) { - const numOfCartons = askNumofCarton.value; - try{ - if (fgPickOrdersData.length === 0) { - console.error("No FG Pick order data available"); - return; - } - - const currentFgOrder = fgPickOrdersData[0]; - - const printDNRequest = { - printerId: 1, - printQty: 1, - isDraft: false, - numOfCarton: numOfCartons, - deliveryOrderId: currentFgOrder.deliveryOrderId, - pickOrderId: currentFgOrder.pickOrderId - }; - - const printDNLabelsRequest = { - printerId: 1, - printQty: 1, - numOfCarton: numOfCartons, - deliveryOrderId: currentFgOrder.deliveryOrderId - }; - - console.log("Printing Labels with request: ", printDNLabelsRequest); - console.log("Printing DN with request: ", printDNRequest); - - const LabelsResponse = await printDNLabels(printDNLabelsRequest); - const DNResponse = await printDN(printDNRequest); - - console.log("Print Labels response: ", LabelsResponse); - console.log("Print DN response: ", DNResponse); - - if(LabelsResponse.success && DNResponse.success){ - Swal.fire({ - position: "bottom-end", - icon: "success", - text: t("Printed Successfully."), - showConfirmButton: false, - timer: 1500 - }); - } else { - if(!LabelsResponse.success){ - console.error("Print failed: ", LabelsResponse.message); - } - else{ - console.error("Print failed: ", DNResponse.message); - } - } - } catch(error){ - console.error("error: ", error) - } - } - },[t, fgPickOrdersData]); - - const handleLabel = useCallback(async () =>{ - const askNumofCarton = await Swal.fire({ - title: t("Enter the number of cartons: "), - icon: "info", - input: "number", - inputPlaceholder: t("Number of cartons"), - inputAttributes:{ - min: "1", - step: "1" - }, - inputValidator: (value) => { - if(!value){ - return t("You need to enter a number") - } - if(parseInt(value) < 1){ - return t("Number must be at least 1"); - } - return null - }, - showCancelButton: true, - confirmButtonText: t("Confirm"), - cancelButtonText: t("Cancel"), - confirmButtonColor: "#8dba00", - cancelButtonColor: "#F04438", - showLoaderOnConfirm: true, - allowOutsideClick: () => !Swal.isLoading() - }); - - if (askNumofCarton.isConfirmed) { - const numOfCartons = askNumofCarton.value; - try{ - if (fgPickOrdersData.length === 0) { - console.error("No FG Pick order data available"); - return; - } - - const currentFgOrder = fgPickOrdersData[0]; - - const printRequest = { - printerId: 1, - printQty: 1, - numOfCarton: numOfCartons, - deliveryOrderId: currentFgOrder.deliveryOrderId, - }; - console.log("Printing Labels with request: ", printRequest); - - const response = await printDNLabels(printRequest); - - console.log("Print Labels response: ", response); - - if(response.success){ - Swal.fire({ - position: "bottom-end", - icon: "success", - text: t("Printed Successfully."), - showConfirmButton: false, - timer: 1500 - }); - } else { - console.error("Print failed: ", response.message); - } - } catch(error){ - console.error("error: ", error) - } - } - },[t, fgPickOrdersData]); useEffect(() => { fetchReleasedOrderCount(); @@ -776,7 +556,6 @@ const handleAssignByLane = useCallback(async ( > - - - @@ -895,6 +607,7 @@ const handleAssignByLane = useCallback(async ( + @@ -906,6 +619,7 @@ const handleAssignByLane = useCallback(async ( {tabIndex === 0 && } {tabIndex === 1 && } {tabIndex === 2 && } + {tabIndex === 3 && } ); diff --git a/src/components/FinishedGoodSearch/GoodPickExecutionRecord.tsx b/src/components/FinishedGoodSearch/GoodPickExecutionRecord.tsx index 5b02999..8ea96b2 100644 --- a/src/components/FinishedGoodSearch/GoodPickExecutionRecord.tsx +++ b/src/components/FinishedGoodSearch/GoodPickExecutionRecord.tsx @@ -41,10 +41,10 @@ import { checkPickOrderCompletion, PickOrderCompletionResponse, checkAndCompletePickOrderByConsoCode, - fetchCompletedDoPickOrders, // ✅ 新增:使用新的 API + fetchCompletedDoPickOrders, CompletedDoPickOrderResponse, CompletedDoPickOrderSearchParams, - fetchLotDetailsByPickOrderId // ✅ 修复:导入类型 + fetchLotDetailsByPickOrderId } from "@/app/api/pickOrder/actions"; import { fetchNameList, NameList } from "@/app/api/user/actions"; import { @@ -63,6 +63,9 @@ import GoodPickExecutionForm from "./GoodPickExecutionForm"; import FGPickOrderCard from "./FGPickOrderCard"; import dayjs from "dayjs"; import { OUTPUT_DATE_FORMAT } from "@/app/utils/formatUtil"; +import { printDN, printDNLabels } from "@/app/api/do/actions"; +import Swal from "sweetalert2"; + interface Props { filterArgs: Record; @@ -108,6 +111,205 @@ const GoodPickExecutionRecord: React.FC = ({ filterArgs }) => { const formProps = useForm(); const errors = formProps.formState.errors; + // ✅ Print handler functions + const handleDN = useCallback(async (deliveryOrderId: number, pickOrderId: number) => { + const askNumofCarton = await Swal.fire({ + title: t("Enter the number of cartons: "), + icon: "info", + input: "number", + inputPlaceholder: t("Number of cartons"), + inputAttributes:{ + min: "1", + step: "1" + }, + inputValidator: (value) => { + if(!value){ + return t("You need to enter a number") + } + if(parseInt(value) < 1){ + return t("Number must be at least 1"); + } + return null + }, + showCancelButton: true, + confirmButtonText: t("Confirm"), + cancelButtonText: t("Cancel"), + confirmButtonColor: "#8dba00", + cancelButtonColor: "#F04438", + showLoaderOnConfirm: true, + allowOutsideClick: () => !Swal.isLoading() + }); + + if (askNumofCarton.isConfirmed) { + const numOfCartons = askNumofCarton.value; + try{ + const printRequest = { + printerId: 1, + printQty: 1, + isDraft: false, + numOfCarton: numOfCartons, + deliveryOrderId: deliveryOrderId, + pickOrderId: pickOrderId + }; + + console.log("Printing Delivery Note with request: ", printRequest); + const response = await printDN(printRequest); + console.log("Print Delivery Note response: ", response); + + if(response.success){ + Swal.fire({ + position: "bottom-end", + icon: "success", + text: t("Printed Successfully."), + showConfirmButton: false, + timer: 1500 + }); + } else { + console.error("Print failed: ", response.message); + } + } catch(error){ + console.error("error: ", error) + } + } + }, [t]); + + const handleDNandLabel = useCallback(async (deliveryOrderId: number, pickOrderId: number) => { + const askNumofCarton = await Swal.fire({ + title: t("Enter the number of cartons: "), + icon: "info", + input: "number", + inputPlaceholder: t("Number of cartons"), + inputAttributes:{ + min: "1", + step: "1" + }, + inputValidator: (value) => { + if(!value){ + return t("You need to enter a number") + } + if(parseInt(value) < 1){ + return t("Number must be at least 1"); + } + return null + }, + showCancelButton: true, + confirmButtonText: t("Confirm"), + cancelButtonText: t("Cancel"), + confirmButtonColor: "#8dba00", + cancelButtonColor: "#F04438", + showLoaderOnConfirm: true, + allowOutsideClick: () => !Swal.isLoading() + }); + + if (askNumofCarton.isConfirmed) { + const numOfCartons = askNumofCarton.value; + try{ + const printDNRequest = { + printerId: 1, + printQty: 1, + isDraft: false, + numOfCarton: numOfCartons, + deliveryOrderId: deliveryOrderId, + pickOrderId: pickOrderId + }; + + const printDNLabelsRequest = { + printerId: 1, + printQty: 1, + numOfCarton: numOfCartons, + deliveryOrderId: deliveryOrderId + }; + + console.log("Printing Labels with request: ", printDNLabelsRequest); + console.log("Printing DN with request: ", printDNRequest); + + const LabelsResponse = await printDNLabels(printDNLabelsRequest); + const DNResponse = await printDN(printDNRequest); + + console.log("Print Labels response: ", LabelsResponse); + console.log("Print DN response: ", DNResponse); + + if(LabelsResponse.success && DNResponse.success){ + Swal.fire({ + position: "bottom-end", + icon: "success", + text: t("Printed Successfully."), + showConfirmButton: false, + timer: 1500 + }); + } else { + if(!LabelsResponse.success){ + console.error("Print failed: ", LabelsResponse.message); + } + else{ + console.error("Print failed: ", DNResponse.message); + } + } + } catch(error){ + console.error("error: ", error) + } + } + }, [t]); + + const handleLabel = useCallback(async (deliveryOrderId: number) => { + const askNumofCarton = await Swal.fire({ + title: t("Enter the number of cartons: "), + icon: "info", + input: "number", + inputPlaceholder: t("Number of cartons"), + inputAttributes:{ + min: "1", + step: "1" + }, + inputValidator: (value) => { + if(!value){ + return t("You need to enter a number") + } + if(parseInt(value) < 1){ + return t("Number must be at least 1"); + } + return null + }, + showCancelButton: true, + confirmButtonText: t("Confirm"), + cancelButtonText: t("Cancel"), + confirmButtonColor: "#8dba00", + cancelButtonColor: "#F04438", + showLoaderOnConfirm: true, + allowOutsideClick: () => !Swal.isLoading() + }); + + if (askNumofCarton.isConfirmed) { + const numOfCartons = askNumofCarton.value; + try{ + const printRequest = { + printerId: 1, + printQty: 1, + numOfCarton: numOfCartons, + deliveryOrderId: deliveryOrderId, + }; + + console.log("Printing Labels with request: ", printRequest); + const response = await printDNLabels(printRequest); + console.log("Print Labels response: ", response); + + if(response.success){ + Swal.fire({ + position: "bottom-end", + icon: "success", + text: t("Printed Successfully."), + showConfirmButton: false, + timer: 1500 + }); + } else { + console.error("Print failed: ", response.message); + } + } catch(error){ + console.error("error: ", error) + } + } + }, [t]); + // ✅ 修改:使用新的 API 获取已完成的 DO Pick Orders const fetchCompletedDoPickOrdersData = useCallback(async (searchParams?: CompletedDoPickOrderSearchParams) => { if (!currentUserId) return; @@ -406,6 +608,37 @@ const GoodPickExecutionRecord: React.FC = ({ filterArgs }) => { > {t("View Details")} + + {doPickOrder.fgPickOrders && doPickOrder.fgPickOrders.length > 0 && ( + <> + + + + + )} ))} diff --git a/src/components/FinishedGoodSearch/GoodPickExecutiondetail.tsx b/src/components/FinishedGoodSearch/GoodPickExecutiondetail.tsx index 7dcf66e..ec6386b 100644 --- a/src/components/FinishedGoodSearch/GoodPickExecutiondetail.tsx +++ b/src/components/FinishedGoodSearch/GoodPickExecutiondetail.tsx @@ -19,6 +19,7 @@ import { TablePagination, Modal, Chip, + Chip, } from "@mui/material"; import TestQrCodeProvider from '../QrCodeScannerProvider/TestQrCodeProvider'; import { fetchLotDetail } from "@/app/api/inventory/actions"; @@ -34,6 +35,7 @@ import { fetchFGPickOrders, // ✅ Add this import FGPickOrderResponse, + checkPickOrderCompletion, fetchAllPickOrderLotsHierarchical, PickOrderCompletionResponse, @@ -45,6 +47,7 @@ import { 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"; import { updateInventoryLotLineStatus, analyzeQrCode } from "@/app/api/inventory/actions"; @@ -441,11 +444,13 @@ const [isConfirmingLot, setIsConfirmingLot] = useState(false); setAllLotsCompleted(allCompleted); 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) { @@ -456,6 +461,7 @@ const [isConfirmingLot, setIsConfirmingLot] = useState(false); return; } + // ✅ 获取新结构的层级数据 // ✅ 获取新结构的层级数据 const hierarchicalData = await fetchAllPickOrderLotsHierarchical(userIdToUse); console.log("✅ Hierarchical data (new structure):", hierarchicalData); @@ -565,6 +571,99 @@ const [isConfirmingLot, setIsConfirmingLot] = useState(false); // ✅ 将层级数据转换为平铺格式(用于表格显示) const flatLotData: any[] = []; + targetPickOrder.pickOrderLines.forEach((line: any) => { + if (line.lots && line.lots.length > 0) { + // ✅ 有 lots 的情况 + line.lots.forEach((lot: any) => { + flatLotData.push({ + pickOrderId: targetPickOrder.pickOrderId, + pickOrderCode: targetPickOrder.pickOrderCode, + pickOrderConsoCode: targetPickOrder.consoCode, + pickOrderTargetDate: targetPickOrder.targetDate, + pickOrderStatus: targetPickOrder.status, + + pickOrderLineId: line.id, + pickOrderLineRequiredQty: line.requiredQty, + pickOrderLineStatus: line.status, + + itemId: line.item.id, + itemCode: line.item.code, + itemName: line.item.name, + //uomCode: line.item.uomCode, + uomDesc: line.item.uomDesc, + uomShortDesc: line.item.uomShortDesc, + lotId: lot.id, + lotNo: lot.lotNo, + expiryDate: lot.expiryDate, + location: lot.location, + stockUnit: lot.stockUnit, + availableQty: lot.availableQty, + requiredQty: lot.requiredQty, + actualPickQty: lot.actualPickQty, + inQty: lot.inQty, + outQty: lot.outQty, + holdQty: lot.holdQty, + lotStatus: lot.lotStatus, + lotAvailability: lot.lotAvailability, + processingStatus: lot.processingStatus, + suggestedPickLotId: lot.suggestedPickLotId, + stockOutLineId: lot.stockOutLineId, + stockOutLineStatus: lot.stockOutLineStatus, + stockOutLineQty: lot.stockOutLineQty, + + routerId: lot.router?.id, + routerIndex: lot.router?.index, + routerRoute: lot.router?.route, + routerArea: lot.router?.area, + }); + }); + } else { + // ✅ 没有 lots 的情况(null stock)- 也要显示 + flatLotData.push({ + pickOrderId: targetPickOrder.pickOrderId, + pickOrderCode: targetPickOrder.pickOrderCode, + pickOrderConsoCode: targetPickOrder.consoCode, + pickOrderTargetDate: targetPickOrder.targetDate, + pickOrderStatus: targetPickOrder.status, + + pickOrderLineId: line.id, + pickOrderLineRequiredQty: line.requiredQty, + pickOrderLineStatus: line.status, + + itemId: line.item.id, + itemCode: line.item.code, + itemName: line.item.name, + //uomCode: line.item.uomCode, + uomDesc: line.item.uomDesc, + + // ✅ Null stock 字段 + lotId: null, + lotNo: null, + expiryDate: null, + location: null, + stockUnit: line.item.uomDesc, + availableQty: 0, + requiredQty: line.requiredQty, + actualPickQty: 0, + inQty: 0, + outQty: 0, + holdQty: 0, + lotStatus: 'unavailable', + lotAvailability: 'insufficient_stock', + processingStatus: 'pending', + suggestedPickLotId: null, + stockOutLineId: null, + stockOutLineStatus: null, + stockOutLineQty: 0, + + routerId: null, + routerIndex: 999999, // ✅ 放到最后 + routerRoute: null, + routerArea: null, + uomShortDesc: line.item.uomShortDesc + }); + } + }); targetPickOrder.pickOrderLines.forEach((line: any) => { if (line.lots && line.lots.length > 0) { // ✅ 有 lots 的情况 @@ -662,10 +761,13 @@ 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); checkAllLotsCompleted(flatLotData); + } catch (error) { console.error("❌ Error fetching combined lot data:", error); setCombinedLotData([]); @@ -675,6 +777,7 @@ 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) { @@ -1508,6 +1611,22 @@ const handleSubmitPickQtyWithQty = useCallback(async (lot: any, submitQty: numbe 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]); const handleStopScan = useCallback(() => { console.log("⏹️ Stopping manual QR scan..."); setIsManualScanning(false); @@ -1645,6 +1764,40 @@ const handleSubmitPickQtyWithQty = useCallback(async (lot: any, submitQty: numbe lot.stockOutLineStatus !== 'rejected' && lot.stockOutLineStatus !== 'completed' )} + > + + + {/* DO Header */} + {fgPickOrdersLoading ? ( + + + + ) : ( + fgPickOrders.length > 0 && ( + + + {/* 基本信息 */} + + + {t("Shop Name")}: {fgPickOrders[0].shopName || '-'} + + + {t("Store ID")}: {fgPickOrders[0].storeId || '-'} + + + {t("Ticket No.")}: {fgPickOrders[0].ticketNo || '-'} + + + {t("Departure Time")}: {fgPickOrders[0].DepartureTime || '-'} + + + lotData={combinedLotData} + onScanLot={handleQrCodeSubmit} + filterActive={(lot) => ( + lot.lotAvailability !== 'rejected' && + lot.stockOutLineStatus !== 'rejected' && + lot.stockOutLineStatus !== 'completed' + )} > @@ -1981,6 +2134,160 @@ paginatedData.map((lot, index) => { /> )} + {/* ✅ 保留:Good Pick Execution Form Modal */} + {pickExecutionFormOpen && selectedLotForExecutionForm && ( + { + 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()} + /> + )} + + + + {isIssueLot ? ( + // ✅ Issue lot 只显示 Issue 按钮 + + ) : ( + // ✅ Normal lot 显示两个按钮 + + + + + + )} + + + + ); +}) + )} + + + + + + `${from}-${to} of ${count !== -1 ? count : `more than ${to}`}` + } + /> + + + + {/* ✅ 保留:QR Code Modal */} + { + setQrModalOpen(false); + setSelectedLotForQr(null); + stopScan(); + resetScan(); + }} + lot={selectedLotForQr} + combinedLotData={combinedLotData} + onQrCodeSubmit={handleQrCodeSubmitFromModal} + /> + + {/* ✅ 保留:Lot Confirmation Modal */} + {lotConfirmationOpen && expectedLotData && scannedLotData && ( + { + setLotConfirmationOpen(false); + setExpectedLotData(null); + setScannedLotData(null); + }} + onConfirm={handleLotConfirmation} + expectedLot={expectedLotData} + scannedLot={scannedLotData} + isLoading={isConfirmingLot} + /> + )} + {/* ✅ 保留:Good Pick Execution Form Modal */} {pickExecutionFormOpen && selectedLotForExecutionForm && ( >; type SearchParamNames = keyof SearchQuery; -const JoSearch: React.FC = ({ defaultInputs, bomCombo }) => { +const JoSearch: React.FC = ({ defaultInputs, bomCombo, printerCombo }) => { const { t } = useTranslation("jo"); const router = useRouter() const [filteredJos, setFilteredJos] = useState([]); @@ -426,7 +428,7 @@ const JoSearch: React.FC = ({ defaultInputs, bomCombo }) => { open={openModal} onClose={closeNewModal} inputDetail={modalInfo} - printerCombo={[]} + printerCombo={printerCombo} // skipQc={true} /> diff --git a/src/components/JoSearch/JoSearchWrapper.tsx b/src/components/JoSearch/JoSearchWrapper.tsx index 7972d56..78470d6 100644 --- a/src/components/JoSearch/JoSearchWrapper.tsx +++ b/src/components/JoSearch/JoSearchWrapper.tsx @@ -3,6 +3,7 @@ import GeneralLoading from "../General/GeneralLoading"; import JoSearch from "./JoSearch"; import { SearchJoResultRequest } from "@/app/api/jo/actions"; import { fetchBomCombo } from "@/app/api/bom"; +import { fetchPrinterCombo } from "@/app/api/settings/printer"; interface SubComponents { Loading: typeof GeneralLoading; @@ -15,12 +16,14 @@ const JoSearchWrapper: React.FC & SubComponents = async () => { } const [ - bomCombo + bomCombo, + printerCombo ] = await Promise.all([ - fetchBomCombo() + fetchBomCombo(), + fetchPrinterCombo() ]) - return + return } JoSearchWrapper.Loading = GeneralLoading; diff --git a/src/components/Logo/Logo.tsx b/src/components/Logo/Logo.tsx index 4750e72..de2ea41 100644 --- a/src/components/Logo/Logo.tsx +++ b/src/components/Logo/Logo.tsx @@ -14,16 +14,16 @@ const Logo: React.FC = ({ width, height }) => { diff --git a/src/components/MailField/MailField.css b/src/components/MailField/MailField.css index 50237e1..96e7fb9 100644 --- a/src/components/MailField/MailField.css +++ b/src/components/MailField/MailField.css @@ -123,7 +123,7 @@ /* Input styles */ /* .tiptap-input { - font-size: 14px; + fontSize: 14px; font-weight: 500; line-height: 12px; } */ \ No newline at end of file diff --git a/src/components/StockIn/FgStockInForm.tsx b/src/components/StockIn/FgStockInForm.tsx index af46970..467f3e6 100644 --- a/src/components/StockIn/FgStockInForm.tsx +++ b/src/components/StockIn/FgStockInForm.tsx @@ -67,7 +67,7 @@ const textfieldSx = { transform: "translate(14px, 1.2rem) scale(1)", "&.MuiInputLabel-shrink": { fontSize: 24, - transform: "translate(14px, -0.5rem) scale(1)", + transform: "translate(14px, -9px) scale(1)", }, // [theme.breakpoints.down("sm")]: { // fontSize: "1rem", diff --git a/src/components/StockIn/StockInForm.tsx b/src/components/StockIn/StockInForm.tsx index 8b88692..e48c0d9 100644 --- a/src/components/StockIn/StockInForm.tsx +++ b/src/components/StockIn/StockInForm.tsx @@ -60,7 +60,7 @@ const textfieldSx = { transform: "translate(14px, 1.2rem) scale(1)", "&.MuiInputLabel-shrink": { fontSize: 24, - transform: "translate(14px, -0.5rem) scale(1)", + transform: "translate(14px, -9px) scale(1)", }, // [theme.breakpoints.down("sm")]: { // fontSize: "1rem", diff --git a/src/i18n/zh/pickOrder.json b/src/i18n/zh/pickOrder.json index 647acd7..4515ad2 100644 --- a/src/i18n/zh/pickOrder.json +++ b/src/i18n/zh/pickOrder.json @@ -278,9 +278,9 @@ "Confirm":"確認", "Update your suggested lot to the this scanned lot":"更新您的建議批次為此掃描的批次", "Print Draft":"列印草稿", - "Print Pick Order and DN Label":"列印提料單和送貨單標貼", + "Print Pick Order and DN Label":"列印提料單和送貨單標籤", "Print Pick Order":"列印提料單", - "Print DN Label":"列印送貨單標貼", + "Print DN Label":"列印送貨單標籤", "Print All Draft" : "列印全部草稿", "If you confirm, the system will:":"如果您確認,系統將:", "QR code verified.":"QR 碼驗證成功。", @@ -292,7 +292,6 @@ "Completed DO pick orders: ":"已完成送貨單提料單:", "No completed DO pick orders found":"沒有已完成送貨單提料單", - "Print DN Label":"列印送貨單標貼", "Enter the number of cartons: ": "請輸入總箱數", "Number of cartons": "箱數", "Select an action for the assigned pick orders.": "選擇分配提料單的動作。", @@ -390,5 +389,8 @@ "Today": "是日", "Tomorrow": "翌日", "Day After Tomorrow": "後日", - "Select Date": "請選擇日期" + "Select Date": "請選擇日期", + "Print DN & Label": "列印提料單和送貨單標籤", + "Print Label": "列印送貨單標籤", + "Ticket Release Table": "查看提貨情況" } \ No newline at end of file diff --git a/src/i18n/zh/purchaseOrder.json b/src/i18n/zh/purchaseOrder.json index 806a1e8..6dd7d9c 100644 --- a/src/i18n/zh/purchaseOrder.json +++ b/src/i18n/zh/purchaseOrder.json @@ -164,5 +164,6 @@ "Expiry Date cannot be earlier than Production Date": "到期日不可早於生產日期", "Production Date must be earlier than Expiry Date": "生產日期必須早於到期日", "confirm expiry date": "確認到期日", - "Invalid Date": "無效日期" + "Invalid Date": "無效日期", + "Missing QC Template, please contact administrator": "找不到品檢模板,請聯絡管理員" }