diff --git a/src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DeliveryOrderService.kt b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DeliveryOrderService.kt index 7fe6a6d..f900240 100644 --- a/src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DeliveryOrderService.kt +++ b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DeliveryOrderService.kt @@ -63,7 +63,10 @@ import com.ffii.fpsms.modules.deliveryOrder.entity.DoPickOrderLineRepository import com.ffii.fpsms.modules.deliveryOrder.entity.DoPickOrderRecordRepository import com.ffii.fpsms.modules.deliveryOrder.entity.DoPickOrderRepository import com.ffii.fpsms.modules.deliveryOrder.web.models.ExportDNLabelsRequest +import com.ffii.fpsms.modules.deliveryOrder.web.models.ExportDNLabelsReprintRequest +import com.ffii.fpsms.modules.deliveryOrder.web.models.DnCartonLabelSheetRow import com.ffii.fpsms.modules.deliveryOrder.web.models.PrintDNLabelsRequest +import com.ffii.fpsms.modules.deliveryOrder.web.models.PrintDNLabelsReprintRequest import com.ffii.fpsms.modules.purchaseOrder.entity.PurchaseOrderRepository import com.ffii.fpsms.modules.stock.entity.InventoryLotRepository import com.ffii.fpsms.modules.stock.service.InventoryLotService @@ -1444,62 +1447,36 @@ open class DeliveryOrderService( //Carton Labels open fun exportDNLabels(request: ExportDNLabelsRequest): Map { - val DNLABELS_PDF = "DeliveryNote/DeliveryNoteCartonLabelsPDF.jrxml" - val resource = ClassPathResource(DNLABELS_PDF) - if (!resource.exists()) { - throw FileNotFoundException("Label file not found: $DNLABELS_PDF") - } - - val doPickOrderRecord = doPickOrderRecordRepository.findById(request.doPickOrderId).orElseThrow { - NoSuchElementException("DoPickOrderRecord not found with ID: ${request.doPickOrderId}") - } - - val doPickOrderLineRecords = doPickOrderLineRecordRepository.findByDoPickOrderId(doPickOrderRecord.recordId) - - val deliveryOrderIds = doPickOrderLineRecords.mapNotNull { it.doOrderId }.distinct() - if (deliveryOrderIds.isEmpty()) { - throw IllegalStateException("DoPickOrderRecord ${request.doPickOrderId} has no associated delivery orders") - } - - val inputStream = resource.inputStream - val cartonLabel = JasperCompileManager.compileReport(inputStream) - val cartonLabelInfo = deliveryOrderIds.flatMap { deliveryOrderId -> - deliveryOrderRepository.findDeliveryOrderInfoById(deliveryOrderId) - }.toMutableList() - - val params = mutableMapOf() - val fields = mutableListOf>() - - for (info in cartonLabelInfo) { - val field = mutableMapOf() - } - - if (cartonLabelInfo.size > 1) { + val context = buildDNLabelContext(request.doPickOrderId) + val rows = buildDnLabelRows( + startCarton = 1, + endCarton = request.numOfCarton, + totalCartonsOnShipment = request.numOfCarton, + ) - } + return mapOf( + "report" to PdfUtils.fillReport(context.cartonLabelTemplate, rows, context.params), + "filename" to context.filename, + ) + } - params["shopPurchaseOrderNo"] = if (deliveryOrderIds.size > 0) { - "請查閲送貨單(採購單共${deliveryOrderIds.size}張)" - } else { - doPickOrderRecord.deliveryOrderCode ?: cartonLabelInfo[0].code - } - params["deliveryNoteCode"] = doPickOrderRecord.deliveryNoteCode ?: "" - params["shopAddress"] = cartonLabelInfo[0].shopAddress ?: "" - val rawShopLabel = doPickOrderRecord.shopName ?: cartonLabelInfo[0].shopName ?: "" - val parsedShopLabel = parseShopLabelForCartonLabel(rawShopLabel) - params["shopCode"] = parsedShopLabel.shopCode - params["shopCodeAbbr"] = parsedShopLabel.shopCodeAbbr - params["shopName"] = parsedShopLabel.shopNameForLabel - params["truckNo"] = doPickOrderRecord.truckLanceCode ?: "" + open fun exportDNLabelsReprint(request: ExportDNLabelsReprintRequest): Map { + validateCartonReprintRange( + fromCarton = request.fromCarton, + toCarton = request.toCarton, + totalCartonsOnShipment = request.totalCartonsOnShipment, + ) - for (cartonNumber in 1..request.numOfCarton) { - val field = mutableMapOf() - fields.add(field) - } + val context = buildDNLabelContext(request.doPickOrderId) + val rows = buildDnLabelRows( + startCarton = request.fromCarton, + endCarton = request.toCarton, + totalCartonsOnShipment = request.totalCartonsOnShipment, + ) return mapOf( - "report" to PdfUtils.fillReport(cartonLabel, fields, params), - "filename" to "${cartonLabelInfo[0].code}_carton_labels" + "report" to PdfUtils.fillReport(context.cartonLabelTemplate, rows, context.params), + "filename" to "${context.filename}_reprint_${request.fromCarton}-${request.toCarton}", ) } @@ -1586,6 +1563,138 @@ open class DeliveryOrderService( } + @Transactional + open fun printDNLabelsReprint(request: PrintDNLabelsReprintRequest) { + validateCartonReprintRange( + fromCarton = request.fromCarton, + toCarton = request.toCarton, + totalCartonsOnShipment = request.totalCartonsOnShipment, + ) + + val printer = + printerService.findById(request.printerId) ?: throw java.util.NoSuchElementException("No such printer") + + val pdf = exportDNLabelsReprint( + ExportDNLabelsReprintRequest( + doPickOrderId = request.doPickOrderId, + fromCarton = request.fromCarton, + toCarton = request.toCarton, + totalCartonsOnShipment = request.totalCartonsOnShipment, + ) + ) + + val jasperPrint = pdf["report"] as JasperPrint + val tempPdfFile = File.createTempFile("print_job_", ".pdf") + + try { + JasperExportManager.exportReportToPdfFile(jasperPrint, tempPdfFile.absolutePath) + + val printQty = if (request.printQty == null || request.printQty <= 0) 1 else request.printQty + printer.ip?.let { ip -> + printer.port?.let { port -> + ZebraPrinterUtil.printPdfToZebra( + tempPdfFile, + ip, + port, + printQty, + ZebraPrinterUtil.PrintDirection.ROTATED, + printer.dpi + ) + } + } + + // Reprint should not update cartonQty persisted on the record. + println("Reprint PDF saved to: ${tempPdfFile.absolutePath}") + } finally { + //tempPdfFile.delete() + } + } + + private data class DnLabelExportContext( + val cartonLabelTemplate: net.sf.jasperreports.engine.JasperReport, + val params: MutableMap, + val filename: String, + ) + + private fun buildDNLabelContext(doPickOrderId: Long): DnLabelExportContext { + val dnLabelsPdf = "DeliveryNote/DeliveryNoteCartonLabelsPDF.jrxml" + val resource = ClassPathResource(dnLabelsPdf) + if (!resource.exists()) { + throw FileNotFoundException("Label file not found: $dnLabelsPdf") + } + + val doPickOrderRecord = doPickOrderRecordRepository.findById(doPickOrderId).orElseThrow { + NoSuchElementException("DoPickOrderRecord not found with ID: $doPickOrderId") + } + val doPickOrderLineRecords = doPickOrderLineRecordRepository.findByDoPickOrderId(doPickOrderRecord.recordId) + val deliveryOrderIds = doPickOrderLineRecords.mapNotNull { it.doOrderId }.distinct() + if (deliveryOrderIds.isEmpty()) { + throw IllegalStateException("DoPickOrderRecord $doPickOrderId has no associated delivery orders") + } + + val cartonLabelTemplate = JasperCompileManager.compileReport(resource.inputStream) + val cartonLabelInfo = deliveryOrderIds.flatMap { deliveryOrderId -> + deliveryOrderRepository.findDeliveryOrderInfoById(deliveryOrderId) + }.toMutableList() + if (cartonLabelInfo.isEmpty()) { + throw IllegalStateException("No delivery order info found for DoPickOrderRecord $doPickOrderId") + } + + val params = mutableMapOf() + params["shopPurchaseOrderNo"] = "請查閲送貨單(採購單共${deliveryOrderIds.size}張)" + params["deliveryNoteCode"] = doPickOrderRecord.deliveryNoteCode ?: "" + params["shopAddress"] = cartonLabelInfo[0].shopAddress ?: "" + + val rawShopLabel = doPickOrderRecord.shopName ?: cartonLabelInfo[0].shopName ?: "" + val parsedShopLabel = parseShopLabelForCartonLabel(rawShopLabel) + params["shopCode"] = parsedShopLabel.shopCode + params["shopCodeAbbr"] = parsedShopLabel.shopCodeAbbr + params["shopName"] = parsedShopLabel.shopNameForLabel + params["truckNo"] = doPickOrderRecord.truckLanceCode ?: "" + + return DnLabelExportContext( + cartonLabelTemplate = cartonLabelTemplate, + params = params, + filename = "${cartonLabelInfo[0].code}_carton_labels", + ) + } + + private fun buildDnLabelRows( + startCarton: Int, + endCarton: Int, + totalCartonsOnShipment: Int, + ): List { + if (endCarton < startCarton) { + return emptyList() + } + + return (startCarton..endCarton).map { cartonIndex -> + DnCartonLabelSheetRow( + cartonIndex = cartonIndex, + cartonTotal = totalCartonsOnShipment, + ) + } + } + + private fun validateCartonReprintRange( + fromCarton: Int, + toCarton: Int, + totalCartonsOnShipment: Int, + ) { + require(totalCartonsOnShipment >= 1) { + "totalCartonsOnShipment must be at least 1" + } + require(fromCarton >= 1) { + "fromCarton must be at least 1" + } + require(toCarton >= fromCarton) { + "toCarton must be greater than or equal to fromCarton" + } + require(toCarton <= totalCartonsOnShipment) { + "toCarton must be less than or equal to totalCartonsOnShipment" + } + } + private fun updateRecordCartonQty(doPickOrderRecordId: Long, cartonQty: Int) { if (cartonQty <= 0) return val record = doPickOrderRecordRepository.findById(doPickOrderRecordId).orElse(null) ?: return diff --git a/src/main/java/com/ffii/fpsms/modules/deliveryOrder/web/DeliveryOrderController.kt b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/web/DeliveryOrderController.kt index 08cb3d8..2ddc236 100644 --- a/src/main/java/com/ffii/fpsms/modules/deliveryOrder/web/DeliveryOrderController.kt +++ b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/web/DeliveryOrderController.kt @@ -16,8 +16,10 @@ import org.springframework.web.bind.annotation.RequestParam import org.springframework.web.bind.annotation.RestController import com.ffii.fpsms.modules.deliveryOrder.web.models.DoDetailResponse import com.ffii.fpsms.modules.deliveryOrder.web.models.ExportDNLabelsRequest +import com.ffii.fpsms.modules.deliveryOrder.web.models.ExportDNLabelsReprintRequest import com.ffii.fpsms.modules.deliveryOrder.web.models.ExportDeliveryNoteRequest import com.ffii.fpsms.modules.deliveryOrder.web.models.PrintDNLabelsRequest +import com.ffii.fpsms.modules.deliveryOrder.web.models.PrintDNLabelsReprintRequest import com.ffii.fpsms.modules.deliveryOrder.web.models.PrintDeliveryNoteRequest import com.ffii.fpsms.modules.deliveryOrder.web.models.ReleaseDoRequest import com.ffii.fpsms.modules.master.web.models.MessageResponse @@ -261,6 +263,27 @@ class DeliveryOrderController( fun printDN(@ModelAttribute request: PrintDNLabelsRequest) { deliveryOrderService.printDNLabels(request) } + + @PostMapping("/DNLabels/reprint") + @Throws(UnsupportedEncodingException::class, NoSuchMessageException::class, ParseException::class, Exception::class) + fun printDNLabelsReprint( + @Valid @RequestBody request: ExportDNLabelsReprintRequest, + response: HttpServletResponse + ) { + response.characterEncoding = "utf-8" + response.contentType = "application/pdf" + val out: OutputStream = response.outputStream + val pdf = deliveryOrderService.exportDNLabelsReprint(request) + val jasperPrint = pdf["report"] as JasperPrint + response.addHeader("filename", "${pdf["filename"]}.pdf") + out.write(JasperExportManager.exportReportToPdf(jasperPrint)) + } + + @GetMapping("print-DNLabels-reprint") + fun printDNLabelsReprint(@ModelAttribute request: PrintDNLabelsReprintRequest) { + deliveryOrderService.printDNLabelsReprint(request) + } + @GetMapping("/batchPrintQrCode") fun printQrCodeForDeliveryOrder(@ModelAttribute request: PrintQrCodeForDoRequest) { stockInLineService.printQrCodeForDeliveryOrder(request) diff --git a/src/main/java/com/ffii/fpsms/modules/deliveryOrder/web/models/DnCartonLabelSheetRow.kt b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/web/models/DnCartonLabelSheetRow.kt new file mode 100644 index 0000000..ead0194 --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/web/models/DnCartonLabelSheetRow.kt @@ -0,0 +1,6 @@ +package com.ffii.fpsms.modules.deliveryOrder.web.models + +data class DnCartonLabelSheetRow( + val cartonIndex: Int, + val cartonTotal: Int, +) diff --git a/src/main/java/com/ffii/fpsms/modules/deliveryOrder/web/models/ExportDNLabelsReprintRequest.kt b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/web/models/ExportDNLabelsReprintRequest.kt new file mode 100644 index 0000000..0f49a84 --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/web/models/ExportDNLabelsReprintRequest.kt @@ -0,0 +1,13 @@ +package com.ffii.fpsms.modules.deliveryOrder.web.models + +import jakarta.validation.constraints.Min + +data class ExportDNLabelsReprintRequest( + val doPickOrderId: Long, + @field:Min(1) + val fromCarton: Int, + @field:Min(1) + val toCarton: Int, + @field:Min(1) + val totalCartonsOnShipment: Int, +) diff --git a/src/main/java/com/ffii/fpsms/modules/deliveryOrder/web/models/PrintDNLabelsReprintRequest.kt b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/web/models/PrintDNLabelsReprintRequest.kt new file mode 100644 index 0000000..6150e37 --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/web/models/PrintDNLabelsReprintRequest.kt @@ -0,0 +1,15 @@ +package com.ffii.fpsms.modules.deliveryOrder.web.models + +import jakarta.validation.constraints.Min + +data class PrintDNLabelsReprintRequest( + val doPickOrderId: Long, + val printerId: Long, + val printQty: Int?, + @field:Min(1) + val fromCarton: Int, + @field:Min(1) + val toCarton: Int, + @field:Min(1) + val totalCartonsOnShipment: Int, +) diff --git a/src/main/resources/DeliveryNote/DeliveryNoteCartonLabelsPDF.jrxml b/src/main/resources/DeliveryNote/DeliveryNoteCartonLabelsPDF.jrxml index 74c39be..5a46005 100644 --- a/src/main/resources/DeliveryNote/DeliveryNoteCartonLabelsPDF.jrxml +++ b/src/main/resources/DeliveryNote/DeliveryNoteCartonLabelsPDF.jrxml @@ -20,6 +20,8 @@ + + @@ -153,7 +155,7 @@ - + @@ -163,7 +165,7 @@ - +