| @@ -67,6 +67,8 @@ const GoodPickExecutionWorkbenchRecord: React.FC<Props> = ({ | |||||
| const currentUserId = session?.id ? parseInt(session.id, 10) : undefined; | const currentUserId = session?.id ? parseInt(session.id, 10) : undefined; | ||||
| const [loading, setLoading] = useState(false); | const [loading, setLoading] = useState(false); | ||||
| const [isPrinting, setIsPrinting] = useState(false); | |||||
| const printInFlightRef = useRef(false); | |||||
| const [records, setRecords] = useState<CompletedDoPickOrderResponse[]>([]); | const [records, setRecords] = useState<CompletedDoPickOrderResponse[]>([]); | ||||
| const initialSearchTargetDate = useMemo( | const initialSearchTargetDate = useMemo( | ||||
| () => resolveWorkbenchRecordTargetDate(initialTargetDate), | () => resolveWorkbenchRecordTargetDate(initialTargetDate), | ||||
| @@ -183,6 +185,65 @@ const GoodPickExecutionWorkbenchRecord: React.FC<Props> = ({ | |||||
| return t("All dates"); | return t("All dates"); | ||||
| }, [searchQuery.targetDate, t]); | }, [searchQuery.targetDate, t]); | ||||
| const rememberCartonQty = useCallback((recordId: number, cartonQty: number) => { | |||||
| setRecords((prev) => | |||||
| prev.map((row) => | |||||
| row.doPickOrderRecordId === recordId ? { ...row, numberOfCartons: cartonQty } : row, | |||||
| ), | |||||
| ); | |||||
| }, []); | |||||
| const submitPrint = useCallback( | |||||
| async (job: () => Promise<{ success: boolean }>) => { | |||||
| await new Promise<void>((resolve) => { | |||||
| void Swal.fire({ | |||||
| title: t("Printing..."), | |||||
| allowOutsideClick: false, | |||||
| allowEscapeKey: false, | |||||
| showConfirmButton: false, | |||||
| didOpen: () => { | |||||
| Swal.showLoading(); | |||||
| resolve(); | |||||
| }, | |||||
| }); | |||||
| }); | |||||
| try { | |||||
| const response = await job(); | |||||
| Swal.close(); | |||||
| if (response.success) { | |||||
| await Swal.fire({ | |||||
| position: "bottom-end", | |||||
| icon: "success", | |||||
| text: t("Printed Successfully."), | |||||
| showConfirmButton: false, | |||||
| timer: 1500, | |||||
| }); | |||||
| return true; | |||||
| } | |||||
| await Swal.fire({ | |||||
| position: "bottom-end", | |||||
| icon: "error", | |||||
| text: t("Print failed"), | |||||
| showConfirmButton: false, | |||||
| timer: 2000, | |||||
| }); | |||||
| return false; | |||||
| } catch (error) { | |||||
| console.error("print error:", error); | |||||
| Swal.close(); | |||||
| await Swal.fire({ | |||||
| position: "bottom-end", | |||||
| icon: "error", | |||||
| text: t("Print failed"), | |||||
| showConfirmButton: false, | |||||
| timer: 2000, | |||||
| }); | |||||
| return false; | |||||
| } | |||||
| }, | |||||
| [t], | |||||
| ); | |||||
| const askNumOfCarton = useCallback(async () => { | const askNumOfCarton = useCallback(async () => { | ||||
| const result = await Swal.fire({ | const result = await Swal.fire({ | ||||
| title: t("Enter the number of cartons: "), | title: t("Enter the number of cartons: "), | ||||
| @@ -210,6 +271,7 @@ const GoodPickExecutionWorkbenchRecord: React.FC<Props> = ({ | |||||
| const handlePrintDN = useCallback( | const handlePrintDN = useCallback( | ||||
| async (recordId: number) => { | async (recordId: number) => { | ||||
| if (printInFlightRef.current) return; | |||||
| if (!a4Printer) { | if (!a4Printer) { | ||||
| await Swal.fire({ | await Swal.fire({ | ||||
| position: "bottom-end", | position: "bottom-end", | ||||
| @@ -220,31 +282,32 @@ const GoodPickExecutionWorkbenchRecord: React.FC<Props> = ({ | |||||
| }); | }); | ||||
| return; | return; | ||||
| } | } | ||||
| const cartonQty = await askNumOfCarton(); | |||||
| if (!cartonQty) return; | |||||
| const response = await printDNWorkbench({ | |||||
| deliveryOrderPickOrderId: recordId, | |||||
| printerId: a4Printer.id, | |||||
| printQty: 1, | |||||
| isDraft: false, | |||||
| numOfCarton: cartonQty, | |||||
| }); | |||||
| if (response.success) { | |||||
| await Swal.fire({ | |||||
| position: "bottom-end", | |||||
| icon: "success", | |||||
| text: t("Printed Successfully."), | |||||
| showConfirmButton: false, | |||||
| timer: 1500, | |||||
| }); | |||||
| await loadData(); | |||||
| printInFlightRef.current = true; | |||||
| setIsPrinting(true); | |||||
| try { | |||||
| const cartonQty = await askNumOfCarton(); | |||||
| if (!cartonQty) return; | |||||
| const ok = await submitPrint(() => | |||||
| printDNWorkbench({ | |||||
| deliveryOrderPickOrderId: recordId, | |||||
| printerId: a4Printer.id, | |||||
| printQty: 1, | |||||
| isDraft: false, | |||||
| numOfCarton: cartonQty, | |||||
| }), | |||||
| ); | |||||
| if (ok) rememberCartonQty(recordId, cartonQty); | |||||
| } finally { | |||||
| setIsPrinting(false); | |||||
| printInFlightRef.current = false; | |||||
| } | } | ||||
| }, | }, | ||||
| [a4Printer, askNumOfCarton, loadData, t], | |||||
| [a4Printer, askNumOfCarton, rememberCartonQty, submitPrint, t], | |||||
| ); | ); | ||||
| const handlePrintLabel = useCallback( | const handlePrintLabel = useCallback( | ||||
| async (recordId: number) => { | async (recordId: number) => { | ||||
| if (printInFlightRef.current) return; | |||||
| if (!labelPrinter) { | if (!labelPrinter) { | ||||
| await Swal.fire({ | await Swal.fire({ | ||||
| position: "bottom-end", | position: "bottom-end", | ||||
| @@ -255,30 +318,31 @@ const GoodPickExecutionWorkbenchRecord: React.FC<Props> = ({ | |||||
| }); | }); | ||||
| return; | return; | ||||
| } | } | ||||
| const cartonQty = await askNumOfCarton(); | |||||
| if (!cartonQty) return; | |||||
| const response = await printDNLabelsWorkbench({ | |||||
| deliveryOrderPickOrderId: recordId, | |||||
| printerId: labelPrinter.id, | |||||
| printQty: 1, | |||||
| numOfCarton: cartonQty, | |||||
| }); | |||||
| if (response.success) { | |||||
| await Swal.fire({ | |||||
| position: "bottom-end", | |||||
| icon: "success", | |||||
| text: t("Printed Successfully."), | |||||
| showConfirmButton: false, | |||||
| timer: 1500, | |||||
| }); | |||||
| await loadData(); | |||||
| printInFlightRef.current = true; | |||||
| setIsPrinting(true); | |||||
| try { | |||||
| const cartonQty = await askNumOfCarton(); | |||||
| if (!cartonQty) return; | |||||
| const ok = await submitPrint(() => | |||||
| printDNLabelsWorkbench({ | |||||
| deliveryOrderPickOrderId: recordId, | |||||
| printerId: labelPrinter.id, | |||||
| printQty: 1, | |||||
| numOfCarton: cartonQty, | |||||
| }), | |||||
| ); | |||||
| if (ok) rememberCartonQty(recordId, cartonQty); | |||||
| } finally { | |||||
| setIsPrinting(false); | |||||
| printInFlightRef.current = false; | |||||
| } | } | ||||
| }, | }, | ||||
| [askNumOfCarton, labelPrinter, loadData, t], | |||||
| [askNumOfCarton, labelPrinter, rememberCartonQty, submitPrint, t], | |||||
| ); | ); | ||||
| const handlePrintBoth = useCallback( | const handlePrintBoth = useCallback( | ||||
| async (recordId: number) => { | async (recordId: number) => { | ||||
| if (printInFlightRef.current) return; | |||||
| if (!a4Printer || !labelPrinter) { | if (!a4Printer || !labelPrinter) { | ||||
| await Swal.fire({ | await Swal.fire({ | ||||
| position: "bottom-end", | position: "bottom-end", | ||||
| @@ -289,37 +353,39 @@ const GoodPickExecutionWorkbenchRecord: React.FC<Props> = ({ | |||||
| }); | }); | ||||
| return; | return; | ||||
| } | } | ||||
| const cartonQty = await askNumOfCarton(); | |||||
| if (!cartonQty) return; | |||||
| const [labelRes, dnRes] = await Promise.all([ | |||||
| printDNLabelsWorkbench({ | |||||
| deliveryOrderPickOrderId: recordId, | |||||
| printerId: labelPrinter.id, | |||||
| printQty: 1, | |||||
| numOfCarton: cartonQty, | |||||
| }), | |||||
| printDNWorkbench({ | |||||
| deliveryOrderPickOrderId: recordId, | |||||
| printerId: a4Printer.id, | |||||
| printQty: 1, | |||||
| isDraft: false, | |||||
| numOfCarton: cartonQty, | |||||
| }), | |||||
| ]); | |||||
| if (labelRes.success && dnRes.success) { | |||||
| await Swal.fire({ | |||||
| position: "bottom-end", | |||||
| icon: "success", | |||||
| text: t("Printed Successfully."), | |||||
| showConfirmButton: false, | |||||
| timer: 1500, | |||||
| printInFlightRef.current = true; | |||||
| setIsPrinting(true); | |||||
| try { | |||||
| const cartonQty = await askNumOfCarton(); | |||||
| if (!cartonQty) return; | |||||
| const ok = await submitPrint(async () => { | |||||
| const [labelRes, dnRes] = await Promise.all([ | |||||
| printDNLabelsWorkbench({ | |||||
| deliveryOrderPickOrderId: recordId, | |||||
| printerId: labelPrinter.id, | |||||
| printQty: 1, | |||||
| numOfCarton: cartonQty, | |||||
| }), | |||||
| printDNWorkbench({ | |||||
| deliveryOrderPickOrderId: recordId, | |||||
| printerId: a4Printer.id, | |||||
| printQty: 1, | |||||
| isDraft: false, | |||||
| numOfCarton: cartonQty, | |||||
| }), | |||||
| ]); | |||||
| return { success: labelRes.success && dnRes.success }; | |||||
| }); | }); | ||||
| await loadData(); | |||||
| if (ok) rememberCartonQty(recordId, cartonQty); | |||||
| } finally { | |||||
| setIsPrinting(false); | |||||
| printInFlightRef.current = false; | |||||
| } | } | ||||
| }, | }, | ||||
| [a4Printer, askNumOfCarton, labelPrinter, loadData, t], | |||||
| [a4Printer, askNumOfCarton, labelPrinter, rememberCartonQty, submitPrint, t], | |||||
| ); | ); | ||||
| const handleLabelReprint = useCallback(async (doPickOrder: CompletedDoPickOrderResponse) => { | const handleLabelReprint = useCallback(async (doPickOrder: CompletedDoPickOrderResponse) => { | ||||
| if (printInFlightRef.current) return; | |||||
| if (!labelPrinter) { | if (!labelPrinter) { | ||||
| Swal.fire({ | Swal.fire({ | ||||
| position: "bottom-end", | position: "bottom-end", | ||||
| @@ -331,6 +397,9 @@ const GoodPickExecutionWorkbenchRecord: React.FC<Props> = ({ | |||||
| return; | return; | ||||
| } | } | ||||
| printInFlightRef.current = true; | |||||
| setIsPrinting(true); | |||||
| try { | |||||
| const defaultTotalCartons = Math.max(1, doPickOrder.numberOfCartons || 1); | const defaultTotalCartons = Math.max(1, doPickOrder.numberOfCartons || 1); | ||||
| const result = await Swal.fire({ | const result = await Swal.fire({ | ||||
| title: t("Reprint DN Label"), | title: t("Reprint DN Label"), | ||||
| @@ -390,31 +459,22 @@ const GoodPickExecutionWorkbenchRecord: React.FC<Props> = ({ | |||||
| return; | return; | ||||
| } | } | ||||
| try { | |||||
| const response = await printDNLabelsReprintWorkbench({ | |||||
| deliveryOrderPickOrderId: doPickOrder.doPickOrderRecordId, | |||||
| printerId: labelPrinter.id, | |||||
| printQty: 1, | |||||
| fromCarton: result.value.fromCarton, | |||||
| toCarton: result.value.toCarton, | |||||
| totalCartonsOnShipment: result.value.totalCartonsOnShipment, | |||||
| }); | |||||
| if (response.success) { | |||||
| Swal.fire({ | |||||
| position: "bottom-end", | |||||
| icon: "success", | |||||
| text: t("Printed Successfully."), | |||||
| showConfirmButton: false, | |||||
| timer: 1500 | |||||
| }); | |||||
| } else { | |||||
| console.error("Reprint failed:", response.message); | |||||
| } | |||||
| } catch (error) { | |||||
| console.error("reprint error: ", error); | |||||
| const ok = await submitPrint(() => | |||||
| printDNLabelsReprintWorkbench({ | |||||
| deliveryOrderPickOrderId: doPickOrder.doPickOrderRecordId, | |||||
| printerId: labelPrinter.id, | |||||
| printQty: 1, | |||||
| fromCarton: result.value.fromCarton, | |||||
| toCarton: result.value.toCarton, | |||||
| totalCartonsOnShipment: result.value.totalCartonsOnShipment, | |||||
| }), | |||||
| ); | |||||
| if (ok) rememberCartonQty(doPickOrder.doPickOrderRecordId, result.value.totalCartonsOnShipment); | |||||
| } finally { | |||||
| setIsPrinting(false); | |||||
| printInFlightRef.current = false; | |||||
| } | } | ||||
| }, [labelPrinter, t]); | |||||
| }, [labelPrinter, rememberCartonQty, submitPrint, t]); | |||||
| const handleDetailClick = useCallback( | const handleDetailClick = useCallback( | ||||
| async (record: CompletedDoPickOrderResponse) => { | async (record: CompletedDoPickOrderResponse) => { | ||||
| @@ -735,16 +795,16 @@ const GoodPickExecutionWorkbenchRecord: React.FC<Props> = ({ | |||||
| <Button variant="outlined" onClick={() => void handleDetailClick(row)}> | <Button variant="outlined" onClick={() => void handleDetailClick(row)}> | ||||
| {t("View Details")} | {t("View Details")} | ||||
| </Button> | </Button> | ||||
| <Button variant="contained" onClick={() => void handlePrintDN(row.doPickOrderRecordId)}> | |||||
| <Button variant="contained" disabled={isPrinting} onClick={() => void handlePrintDN(row.doPickOrderRecordId)}> | |||||
| {t("Print Pick Order")} | {t("Print Pick Order")} | ||||
| </Button> | </Button> | ||||
| <Button variant="contained" onClick={() => void handlePrintBoth(row.doPickOrderRecordId)}> | |||||
| <Button variant="contained" disabled={isPrinting} onClick={() => void handlePrintBoth(row.doPickOrderRecordId)}> | |||||
| {t("Print DN & Label")} | {t("Print DN & Label")} | ||||
| </Button> | </Button> | ||||
| <Button variant="contained" onClick={() => void handlePrintLabel(row.doPickOrderRecordId)}> | |||||
| <Button variant="contained" disabled={isPrinting} onClick={() => void handlePrintLabel(row.doPickOrderRecordId)}> | |||||
| {t("Print Label")} | {t("Print Label")} | ||||
| </Button> | </Button> | ||||
| <Button variant="contained" onClick={() => void handleLabelReprint(row)}> | |||||
| <Button variant="contained" disabled={isPrinting} onClick={() => void handleLabelReprint(row)}> | |||||
| {t("Reprint Label(s)")} | {t("Reprint Label(s)")} | ||||
| </Button> | </Button> | ||||
| </CardActions> | </CardActions> | ||||