From 8cd4ac2efe9d326e00b0849cdadae4af8db80874 Mon Sep 17 00:00:00 2001 From: "kelvin.yau" Date: Mon, 22 Sep 2025 17:51:08 +0800 Subject: [PATCH] Delivery Note PDF Gen --- .../entity/DeliveryOrderRepository.kt | 3 +- .../entity/models/DeliveryOrderInfo.kt | 3 + .../entity/models/DeliveryOrderLineInfo.kt | 3 + .../service/DeliveryOrderService.kt | 97 ++++- .../web/DeliveryOrderController.kt | 28 ++ .../web/models/ExportDeliveryNoteRequest.kt | 5 + .../web/models/PrintDeliveryNoteRequest.kt | 7 + .../pickOrder/entity/RouterRepository.kt | 1 + .../DeliveryNote/DeliveryNotePDF.jrxml | 373 ++++++++++++++++++ 9 files changed, 516 insertions(+), 4 deletions(-) create mode 100644 src/main/java/com/ffii/fpsms/modules/deliveryOrder/web/models/ExportDeliveryNoteRequest.kt create mode 100644 src/main/java/com/ffii/fpsms/modules/deliveryOrder/web/models/PrintDeliveryNoteRequest.kt create mode 100644 src/main/resources/DeliveryNote/DeliveryNotePDF.jrxml diff --git a/src/main/java/com/ffii/fpsms/modules/deliveryOrder/entity/DeliveryOrderRepository.kt b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/entity/DeliveryOrderRepository.kt index c9e1781..c533343 100644 --- a/src/main/java/com/ffii/fpsms/modules/deliveryOrder/entity/DeliveryOrderRepository.kt +++ b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/entity/DeliveryOrderRepository.kt @@ -3,8 +3,6 @@ package com.ffii.fpsms.modules.deliveryOrder.entity import com.ffii.core.support.AbstractRepository import com.ffii.fpsms.modules.deliveryOrder.entity.models.DeliveryOrderInfo import com.ffii.fpsms.modules.deliveryOrder.enums.DeliveryOrderStatus -import com.ffii.fpsms.modules.master.entity.projections.SearchId -import org.springframework.data.jpa.repository.Query import org.springframework.stereotype.Repository import java.io.Serializable import java.time.LocalDateTime @@ -25,6 +23,7 @@ interface DeliveryOrderRepository : AbstractRepository { fun findByOrderDateBetweenAndDeletedIsFalse(startDate: LocalDateTime?, endDate: LocalDateTime?) : List + fun findDeliveryOrderInfoById(id: Long): List //GET FULL LIST fun findAllBy() : List; diff --git a/src/main/java/com/ffii/fpsms/modules/deliveryOrder/entity/models/DeliveryOrderInfo.kt b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/entity/models/DeliveryOrderInfo.kt index 4d51b86..0248c7a 100644 --- a/src/main/java/com/ffii/fpsms/modules/deliveryOrder/entity/models/DeliveryOrderInfo.kt +++ b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/entity/models/DeliveryOrderInfo.kt @@ -23,6 +23,9 @@ interface DeliveryOrderInfo{ @get:Value("#{target.supplier?.name}") val supplierName: String? + @get:Value("#{target.shop?.addr3}") + val shopAddress: String? + val deliveryOrderLines: List diff --git a/src/main/java/com/ffii/fpsms/modules/deliveryOrder/entity/models/DeliveryOrderLineInfo.kt b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/entity/models/DeliveryOrderLineInfo.kt index 4777137..a2ef79f 100644 --- a/src/main/java/com/ffii/fpsms/modules/deliveryOrder/entity/models/DeliveryOrderLineInfo.kt +++ b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/entity/models/DeliveryOrderLineInfo.kt @@ -10,6 +10,9 @@ interface DeliveryOrderLineInfo { val qty: BigDecimal @get:Value("#{target.uom?.udfudesc}") val uom: String? + @get:Value("#{target.uom?.udfShortDesc}") + val uomShortDesc: String? val price: BigDecimal? val status: String? + } \ No newline at end of file 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 c54d035..c403a0a 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 @@ -1,5 +1,7 @@ package com.ffii.fpsms.modules.deliveryOrder.service +import com.ffii.core.utils.PdfUtils +import com.ffii.core.utils.ZebraPrinterUtil import com.ffii.fpsms.m18.entity.M18DataLogRepository import com.ffii.fpsms.m18.service.M18DataLogService import com.ffii.fpsms.modules.deliveryOrder.entity.DeliveryOrder @@ -29,9 +31,11 @@ import java.time.LocalDate import java.math.BigDecimal import com.ffii.fpsms.modules.master.web.models.MessageResponse import java.time.LocalDateTime -import com.ffii.fpsms.modules.deliveryOrder.service.DoPickOrderService import com.ffii.fpsms.modules.deliveryOrder.entity.DoPickOrder import com.ffii.fpsms.modules.deliveryOrder.enums.DoPickOrderStatus +import com.ffii.fpsms.modules.deliveryOrder.web.models.ExportDeliveryNoteRequest +import com.ffii.fpsms.modules.deliveryOrder.web.models.PrintDeliveryNoteRequest +import com.ffii.fpsms.modules.master.service.PrinterService import java.time.format.DateTimeFormatter import com.ffii.fpsms.modules.pickOrder.entity.TruckRepository import java.time.LocalTime @@ -46,6 +50,12 @@ import com.ffii.fpsms.modules.stock.web.model.StockOutLineStatus import com.ffii.fpsms.modules.stock.web.model.SuggestedPickLotForPolRequest import com.ffii.fpsms.modules.pickOrder.entity.PickOrderRepository import com.ffii.fpsms.modules.pickOrder.entity.PickOrderLineRepository +import net.sf.jasperreports.engine.JasperCompileManager +import net.sf.jasperreports.engine.JasperExportManager +import net.sf.jasperreports.engine.JasperPrint +import org.springframework.core.io.ClassPathResource +import java.io.File +import java.io.FileNotFoundException @Service open class DeliveryOrderService( @@ -64,7 +74,8 @@ open class DeliveryOrderService( private val inventoryLotLineRepository: InventoryLotLineRepository, private val stockOutRepository: StockOutRepository, private val stockOutLineRepository: StockOutLIneRepository, - private val pickOrderLineRepository: PickOrderLineRepository + private val pickOrderLineRepository: PickOrderLineRepository, + private val printerService: PrinterService ) { open fun findByM18DataLogId(m18DataLogId: Long): DeliveryOrder? { @@ -495,4 +506,86 @@ open class DeliveryOrderService( ) } + private fun buildShopAddress(shop: com.ffii.fpsms.modules.master.entity.Shop?): String { + if (shop == null) return "" + + val addressParts = mutableListOf() + + shop.addr3?.let { if (it.isNotBlank()) addressParts.add(it) } + + return addressParts.joinToString(", ") + } + + @Throws(IOException::class) + @Transactional + open fun exportDeliveryNote(request: ExportDeliveryNoteRequest): Map { + val DELIVERYNOTE_PDF = "DeliveryNote/DeliveryNotePDF.jrxml" + val resource = ClassPathResource(DELIVERYNOTE_PDF) + if(!resource.exists()){ + throw FileNotFoundException("Report file not fount: $DELIVERYNOTE_PDF") + } + val inputStream = resource.inputStream + val deliveryNote = JasperCompileManager.compileReport(inputStream) + val deliveryNoteInfo = deliveryOrderRepository.findDeliveryOrderInfoById(request.deliveryOrderIds).toMutableList() + val fields = mutableListOf>() + val params = mutableMapOf() + + for (info in deliveryNoteInfo) { + info.deliveryOrderLines.forEachIndexed { index, line -> + val field = mutableMapOf() + field["sequenceNumber"] = (index + 1).toString() + field["itemNo"] = line.itemNo + field["itemName"] = line.itemName ?:"" + field["uom"] = line.uom ?:"" + field["qty"] = line.qty.toString() + field["shortName"] = line.uomShortDesc ?:"" + + + fields.add(field) + } + } + + params["deliveryOrderCode"] = deliveryNoteInfo[0].code + params["shopName"] = deliveryNoteInfo[0].shopName ?: "" + params["shopAddress"] = deliveryNoteInfo[0].shopAddress ?: "" + params["deliveryDate"] = deliveryNoteInfo[0].estimatedArrivalDate?.format(DateTimeFormatter.ISO_LOCAL_DATE) ?: "" + //numOfCarton + //truckNo + + //FGPickOrderNo + //ShopPurchaseOrderNo + + return mapOf( + "report" to PdfUtils.fillReport(deliveryNote, fields, params), + "filename" to deliveryNoteInfo[0].code + ) + } + + @Transactional + open fun printDeliveryNote(request: PrintDeliveryNoteRequest){ + val printer = printerService.findById(request.printerId) ?: throw java.util.NoSuchElementException("No such printer") + + val pdf = exportDeliveryNote( + ExportDeliveryNoteRequest( + deliveryOrderIds = request.deliveryOrderId + ) + ) + + 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) + }} + } finally { + tempPdfFile.delete() + } + + } + } \ No newline at end of file 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 e3f25d3..b19bee6 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 @@ -15,9 +15,20 @@ import org.springframework.web.bind.annotation.RequestMapping 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.ExportDeliveryNoteRequest +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 +import jakarta.servlet.http.HttpServletResponse import jakarta.validation.Valid +import net.sf.jasperreports.engine.JasperExportManager +import net.sf.jasperreports.engine.JasperPrint +import org.hibernate.result.Output +import org.springframework.context.NoSuchMessageException +import org.springframework.web.bind.annotation.ModelAttribute +import java.io.OutputStream +import java.io.UnsupportedEncodingException +import java.text.ParseException import java.time.LocalDateTime @RequestMapping("/do") @@ -169,4 +180,21 @@ class DeliveryOrderController( // ✅ Simply pass the request directly - userId comes from frontend session return deliveryOrderService.releaseDeliveryOrder(request) } + + @PostMapping("/DN") + @Throws(UnsupportedEncodingException::class, NoSuchMessageException::class, ParseException::class, Exception::class) + fun printDN(@Valid @RequestBody request: ExportDeliveryNoteRequest, response: HttpServletResponse){ + response.characterEncoding = "utf-8" + response.contentType = "application/pdf" + val out: OutputStream = response.outputStream + val pdf = deliveryOrderService.exportDeliveryNote(request) + val jasperPrint = pdf["report"] as JasperPrint + response.addHeader("filename", "${pdf["filename"]}.pdf") + out.write(JasperExportManager.exportReportToPdf(jasperPrint)) + } + + @GetMapping("print-DN") + fun printDN(@RequestBody request: PrintDeliveryNoteRequest) { + deliveryOrderService.printDeliveryNote(request) + } } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/deliveryOrder/web/models/ExportDeliveryNoteRequest.kt b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/web/models/ExportDeliveryNoteRequest.kt new file mode 100644 index 0000000..d5a3c7c --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/web/models/ExportDeliveryNoteRequest.kt @@ -0,0 +1,5 @@ +package com.ffii.fpsms.modules.deliveryOrder.web.models + +data class ExportDeliveryNoteRequest ( + val deliveryOrderIds: Long +) diff --git a/src/main/java/com/ffii/fpsms/modules/deliveryOrder/web/models/PrintDeliveryNoteRequest.kt b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/web/models/PrintDeliveryNoteRequest.kt new file mode 100644 index 0000000..9dec7d4 --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/web/models/PrintDeliveryNoteRequest.kt @@ -0,0 +1,7 @@ +package com.ffii.fpsms.modules.deliveryOrder.web.models + +data class PrintDeliveryNoteRequest( + val deliveryOrderId: Long, + val printerId: Long, + val printQty: Int?, +) \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/pickOrder/entity/RouterRepository.kt b/src/main/java/com/ffii/fpsms/modules/pickOrder/entity/RouterRepository.kt index ada52ba..4850458 100644 --- a/src/main/java/com/ffii/fpsms/modules/pickOrder/entity/RouterRepository.kt +++ b/src/main/java/com/ffii/fpsms/modules/pickOrder/entity/RouterRepository.kt @@ -22,4 +22,5 @@ interface RouterRepository : AbstractRepository { @Query("SELECT r FROM Router r WHERE r.route = :route AND r.deleted = false") fun findByRouteAndDeletedFalse(@Param("route") route: String): List + } \ No newline at end of file diff --git a/src/main/resources/DeliveryNote/DeliveryNotePDF.jrxml b/src/main/resources/DeliveryNote/DeliveryNotePDF.jrxml new file mode 100644 index 0000000..aedf069 --- /dev/null +++ b/src/main/resources/DeliveryNote/DeliveryNotePDF.jrxml @@ -0,0 +1,373 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <band height="28"> + <staticText> + <reportElement x="223" y="2" width="108" height="23" uuid="d88b3126-393b-4444-9a75-a73af8ae0797"> + <property name="com.jaspersoft.studio.unit.y" value="px"/> + <property name="com.jaspersoft.studio.unit.height" value="px"/> + </reportElement> + <textElement textAlignment="Center" verticalAlignment="Bottom"> + <font fontName="微軟正黑體" size="16"/> + </textElement> + <text><![CDATA[送貨單]]></text> + </staticText> + <staticText> + <reportElement x="430" y="10" width="40" height="18" uuid="ddea10c9-9e96-484b-b76b-bfff59950a98"> + <property name="com.jaspersoft.studio.unit.y" value="px"/> + <property name="com.jaspersoft.studio.unit.height" value="px"/> + </reportElement> + <textElement textAlignment="Center" verticalAlignment="Middle"> + <font fontName="微軟正黑體" size="12"/> + </textElement> + <text><![CDATA[頁數]]></text> + </staticText> + <textField> + <reportElement x="470" y="10" width="20" height="18" uuid="f4a20909-91b8-41bd-940f-140adced5b18"> + <property name="com.jaspersoft.studio.unit.height" value="px"/> + </reportElement> + <textElement textAlignment="Center" verticalAlignment="Middle"/> + <textFieldExpression><![CDATA[$V{PAGE_NUMBER}]]></textFieldExpression> + </textField> + <staticText> + <reportElement x="530" y="10" width="25" height="18" uuid="586df5fc-ea37-4896-b8df-2290890341a8"> + <property name="com.jaspersoft.studio.unit.y" value="px"/> + <property name="com.jaspersoft.studio.unit.height" value="px"/> + </reportElement> + <textElement textAlignment="Center" verticalAlignment="Middle"> + <font fontName="微軟正黑體" size="12"/> + </textElement> + <text><![CDATA[頁]]></text> + </staticText> + <textField evaluationTime="Report"> + <reportElement x="510" y="10" width="20" height="18" uuid="8718a2eb-22fd-4bce-ade2-1b5869800c55"> + <property name="com.jaspersoft.studio.unit.width" value="px"/> + <property name="com.jaspersoft.studio.unit.height" value="px"/> + </reportElement> + <textElement textAlignment="Center" verticalAlignment="Middle"/> + <textFieldExpression><![CDATA[$V{PAGE_NUMBER}]]></textFieldExpression> + </textField> + <staticText> + <reportElement x="490" y="10" width="20" height="18" uuid="460a4235-1ce6-47d8-9e34-952e5ac9a660"> + <property name="com.jaspersoft.studio.unit.y" value="px"/> + <property name="com.jaspersoft.studio.unit.height" value="px"/> + </reportElement> + <textElement textAlignment="Center" verticalAlignment="Justified"> + <font fontName="微軟正黑體" size="12"/> + </textElement> + <text><![CDATA[/]]></text> + </staticText> + </band> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +