| @@ -67,6 +67,8 @@ const GoodPickExecutionWorkbenchRecord: React.FC<Props> = ({ | |||
| const currentUserId = session?.id ? parseInt(session.id, 10) : undefined; | |||
| const [loading, setLoading] = useState(false); | |||
| const [isPrinting, setIsPrinting] = useState(false); | |||
| const printInFlightRef = useRef(false); | |||
| const [records, setRecords] = useState<CompletedDoPickOrderResponse[]>([]); | |||
| const initialSearchTargetDate = useMemo( | |||
| () => resolveWorkbenchRecordTargetDate(initialTargetDate), | |||
| @@ -183,6 +185,65 @@ const GoodPickExecutionWorkbenchRecord: React.FC<Props> = ({ | |||
| return t("All dates"); | |||
| }, [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 result = await Swal.fire({ | |||
| title: t("Enter the number of cartons: "), | |||
| @@ -210,6 +271,7 @@ const GoodPickExecutionWorkbenchRecord: React.FC<Props> = ({ | |||
| const handlePrintDN = useCallback( | |||
| async (recordId: number) => { | |||
| if (printInFlightRef.current) return; | |||
| if (!a4Printer) { | |||
| await Swal.fire({ | |||
| position: "bottom-end", | |||
| @@ -220,31 +282,32 @@ const GoodPickExecutionWorkbenchRecord: React.FC<Props> = ({ | |||
| }); | |||
| 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( | |||
| async (recordId: number) => { | |||
| if (printInFlightRef.current) return; | |||
| if (!labelPrinter) { | |||
| await Swal.fire({ | |||
| position: "bottom-end", | |||
| @@ -255,30 +318,31 @@ const GoodPickExecutionWorkbenchRecord: React.FC<Props> = ({ | |||
| }); | |||
| 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( | |||
| async (recordId: number) => { | |||
| if (printInFlightRef.current) return; | |||
| if (!a4Printer || !labelPrinter) { | |||
| await Swal.fire({ | |||
| position: "bottom-end", | |||
| @@ -289,37 +353,39 @@ const GoodPickExecutionWorkbenchRecord: React.FC<Props> = ({ | |||
| }); | |||
| 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) => { | |||
| if (printInFlightRef.current) return; | |||
| if (!labelPrinter) { | |||
| Swal.fire({ | |||
| position: "bottom-end", | |||
| @@ -331,6 +397,9 @@ const GoodPickExecutionWorkbenchRecord: React.FC<Props> = ({ | |||
| return; | |||
| } | |||
| printInFlightRef.current = true; | |||
| setIsPrinting(true); | |||
| try { | |||
| const defaultTotalCartons = Math.max(1, doPickOrder.numberOfCartons || 1); | |||
| const result = await Swal.fire({ | |||
| title: t("Reprint DN Label"), | |||
| @@ -390,31 +459,22 @@ const GoodPickExecutionWorkbenchRecord: React.FC<Props> = ({ | |||
| 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( | |||
| async (record: CompletedDoPickOrderResponse) => { | |||
| @@ -735,16 +795,16 @@ const GoodPickExecutionWorkbenchRecord: React.FC<Props> = ({ | |||
| <Button variant="outlined" onClick={() => void handleDetailClick(row)}> | |||
| {t("View Details")} | |||
| </Button> | |||
| <Button variant="contained" onClick={() => void handlePrintDN(row.doPickOrderRecordId)}> | |||
| <Button variant="contained" disabled={isPrinting} onClick={() => void handlePrintDN(row.doPickOrderRecordId)}> | |||
| {t("Print Pick Order")} | |||
| </Button> | |||
| <Button variant="contained" onClick={() => void handlePrintBoth(row.doPickOrderRecordId)}> | |||
| <Button variant="contained" disabled={isPrinting} onClick={() => void handlePrintBoth(row.doPickOrderRecordId)}> | |||
| {t("Print DN & Label")} | |||
| </Button> | |||
| <Button variant="contained" onClick={() => void handlePrintLabel(row.doPickOrderRecordId)}> | |||
| <Button variant="contained" disabled={isPrinting} onClick={() => void handlePrintLabel(row.doPickOrderRecordId)}> | |||
| {t("Print Label")} | |||
| </Button> | |||
| <Button variant="contained" onClick={() => void handleLabelReprint(row)}> | |||
| <Button variant="contained" disabled={isPrinting} onClick={() => void handleLabelReprint(row)}> | |||
| {t("Reprint Label(s)")} | |||
| </Button> | |||
| </CardActions> | |||