From 46b5b9f8c836efc64cba747dbae54672f2c4285e Mon Sep 17 00:00:00 2001 From: "CANCERYS\\kw093" Date: Wed, 27 May 2026 20:55:01 +0800 Subject: [PATCH] do PDF fix --- .../service/DeliveryOrderService.kt | 220 ++++++++++++------ .../service/DoWorkbenchMainService.kt | 81 +++---- .../web/DeliveryOrderController.kt | 2 +- .../web/DoWorkbenchController.kt | 19 ++ 4 files changed, 193 insertions(+), 129 deletions(-) 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 128c38c..8c1b94e 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 @@ -86,6 +86,9 @@ import org.springframework.data.domain.Page import org.springframework.data.domain.Pageable import com.ffii.fpsms.modules.deliveryOrder.entity.models.DeliveryOrderInfoLite import com.ffii.fpsms.modules.deliveryOrder.entity.models.DeliveryOrderInfoLiteDto +import com.ffii.fpsms.modules.deliveryOrder.entity.models.DeliveryOrderLineInfo +import com.ffii.fpsms.modules.master.entity.Items +import com.ffii.fpsms.modules.pickOrder.entity.PickOrderLine import com.ffii.fpsms.modules.stock.entity.InventoryLotLine import com.ffii.fpsms.modules.stock.entity.projection.StockOutLineInfo import java.util.Locale @@ -1242,18 +1245,18 @@ open class DeliveryOrderService( val truckNo = doPickOrderRecord.truckLanceCode ?: "" val selectedPickOrder = pickOrderRepository.findById(pickOrderId).orElse(null) - val allLines = deliveryNoteInfo.flatMap { info -> - info.deliveryOrderLines.map { line -> line } - } - + val pickOrderIdByDoId = doPickOrderLineRecords.mapNotNull { rec -> + val doId = rec.doOrderId + val poId = rec.pickOrderId + if (doId != null && poId != null) doId to poId else null + }.toMap() + + val exportLines = deliveryNoteExportLines(deliveryNoteInfo) + val pickOrderLines = pickOrderIds.flatMap { pid -> pickOrderLineRepository.findAllByPickOrderId(pid) } - - val pickOrderLineIdsByItemId = pickOrderLines - .groupBy { it.item?.id } - .mapValues { (_, lines) -> lines.mapNotNull { it.id } } - + val allPickOrderLineIds = pickOrderLines.mapNotNull { it.id } val stockOutLinesByPickOrderLineId: Map> = if (allPickOrderLineIds.isNotEmpty()) { @@ -1269,7 +1272,7 @@ open class DeliveryOrderService( ?.replace(" ", "") ?: "" - val uniqueItemIdsForSort = allLines.mapNotNull { it.itemId }.distinct() + val uniqueItemIdsForSort = exportLines.mapNotNull { it.line.itemId }.distinct() val itemsById = if (uniqueItemIdsForSort.isNotEmpty()) { itemsRepository.findAllById(uniqueItemIdsForSort).associateBy { it.id!! } } else { @@ -1277,17 +1280,17 @@ open class DeliveryOrderService( } val sortedLines = when (doStoreFloorKey) { - "2F" -> allLines.sortedWith( + "2F" -> exportLines.sortedWith( compareBy( - { line -> itemsById[line.itemId]?.item_Order ?: Int.MAX_VALUE }, - { line -> line.itemNo }, + { row -> itemsById[row.line.itemId]?.item_Order ?: Int.MAX_VALUE }, + { row -> row.line.itemNo }, ), ) - "4F" -> allLines.sortedBy { line -> - line.itemId?.let { getWarehouseOrderByItemId(it) } ?: Int.MAX_VALUE + "4F" -> exportLines.sortedBy { row -> + row.line.itemId?.let { getWarehouseOrderByItemId(it) } ?: Int.MAX_VALUE } - else -> allLines.sortedBy { line -> - line.itemId?.let { getWarehouseOrderByItemId(it) } ?: Int.MAX_VALUE + else -> exportLines.sortedBy { row -> + row.line.itemId?.let { getWarehouseOrderByItemId(it) } ?: Int.MAX_VALUE } } @@ -1300,61 +1303,19 @@ open class DeliveryOrderService( emptyMap() } - val itemsMap = itemsById - - sortedLines.forEach { line -> - val field = mutableMapOf() - - field["sequenceNumber"] = (fields.size + 1).toString() - field["itemNo"] = line.itemNo - field["itemName"] = line.itemName ?: "" - field["uom"] = line.uom ?: "" - - val actualPickQty = if (line.itemId != null) { - val pickOrderLineIdsForItem = pickOrderLineIdsByItemId[line.itemId] ?: emptyList() - val totalQty = pickOrderLineIdsForItem.sumOf { polId -> - stockOutLinesByPickOrderLineId[polId].orEmpty().sumOf { it.qty } - } - totalQty.toString() - } else { - line.qty.toString() - } - field["qty"] = actualPickQty - - field["shortName"] = line.uomShortDesc ?: "" - - val route = line.itemId?.let { itemId -> - routeFromStockOutsForItem( - itemId, - pickOrderLineIdsByItemId, - stockOutLinesByPickOrderLineId, - illById, - ).takeIf { it != "-" } ?: getWarehouseCodeByItemId(itemId) - } ?: "-" - field["route"] = route - - val lotNo = line.itemId?.let { itemId -> - val pickOrderLineIdsForItem = pickOrderLineIdsByItemId[itemId] ?: emptyList() - val lotNumbers = pickOrderLineIdsForItem.flatMap { polId -> - stockOutLinesByPickOrderLineId[polId].orEmpty().mapNotNull { it.lotNo } - }.distinct().joinToString(", ") - - lotNumbers.ifBlank { "沒有庫存" } - } ?: "沒有庫存" - - field["lotNo"] = lotNo - - val signOff = line.itemId?.let { itemId -> - val item = itemsMap[itemId] - if (item?.isEgg == true) { - "簽署: __________" - } else { - "" - } - } ?: "" - field["signOff"] = signOff - - fields.add(field) + sortedLines.forEach { row -> + fields.add( + buildDeliveryNotePdfLineField( + deliveryOrderId = row.deliveryOrderId, + line = row.line, + sequenceNumber = fields.size + 1, + pickOrderIdByDoId = pickOrderIdByDoId, + pickOrderLines = pickOrderLines, + stockOutLinesByPickOrderLineId = stockOutLinesByPickOrderLineId, + illById = illById, + itemsById = itemsById, + ), + ) } params["dnTitle"] = "送貨單" @@ -1386,15 +1347,110 @@ open class DeliveryOrderService( ) } - fun routeFromStockOutsForItem( - itemId: Long, - pickOrderLineIdsByItemId: Map>, + data class DeliveryNoteExportLine( + val deliveryOrderId: Long, + val line: DeliveryOrderLineInfo, + ) + + fun deliveryNoteExportLines(deliveryNoteInfo: List): List = + deliveryNoteInfo.flatMap { info -> + info.deliveryOrderLines.map { line -> DeliveryNoteExportLine(info.id, line) } + } + + fun resolvePickOrderLineIdForDeliveryNoteLine( + deliveryOrderId: Long, + itemId: Long?, + pickOrderIdByDoId: Map, + pickOrderLines: List, + ): Long? { + if (itemId == null) return null + val pickOrderId = pickOrderIdByDoId[deliveryOrderId] ?: return null + return pickOrderLines + .firstOrNull { pol -> pol.pickOrder?.id == pickOrderId && pol.item?.id == itemId } + ?.id + } + + fun buildDeliveryNotePdfLineField( + deliveryOrderId: Long, + line: DeliveryOrderLineInfo, + sequenceNumber: Int, + pickOrderIdByDoId: Map, + pickOrderLines: List, + stockOutLinesByPickOrderLineId: Map>, + illById: Map, + itemsById: Map, + ): MutableMap { + val field = mutableMapOf() + field["sequenceNumber"] = sequenceNumber.toString() + field["itemNo"] = line.itemNo + field["itemName"] = line.itemName ?: "" + field["uom"] = line.uom ?: "" + field["shortName"] = line.uomShortDesc ?: "" + + val polId = resolvePickOrderLineIdForDeliveryNoteLine( + deliveryOrderId = deliveryOrderId, + itemId = line.itemId, + pickOrderIdByDoId = pickOrderIdByDoId, + pickOrderLines = pickOrderLines, + ) + val polIdsForRow = listOfNotNull(polId) + + field["qty"] = if (polId != null) { + sumActualPickQtyForPickOrderLineIds(polIdsForRow, stockOutLinesByPickOrderLineId) + } else { + line.qty.toString() + } + + val itemId = line.itemId + field["route"] = when { + itemId != null && polId != null -> { + routeFromStockOutsForPickOrderLineIds(polIdsForRow, stockOutLinesByPickOrderLineId, illById) + .takeIf { it != "-" } ?: getWarehouseCodeByItemId(itemId) ?: "-" + } + itemId != null -> getWarehouseCodeByItemId(itemId) ?: "-" + else -> "-" + } + + field["lotNo"] = if (itemId != null && polId != null) { + lotNumbersForPickOrderLineIds(polIdsForRow, stockOutLinesByPickOrderLineId).ifBlank { "沒有庫存" } + } else { + "沒有庫存" + } + + field["signOff"] = if (itemId != null && itemsById[itemId]?.isEgg == true) { + "簽署: __________" + } else { + "" + } + + return field + } + + fun sumActualPickQtyForPickOrderLineIds( + pickOrderLineIds: List, + stockOutLinesByPickOrderLineId: Map>, + ): String { + if (pickOrderLineIds.isEmpty()) return "0" + return pickOrderLineIds.sumOf { polId -> + stockOutLinesByPickOrderLineId[polId].orEmpty().sumOf { it.qty } + }.toString() + } + + fun lotNumbersForPickOrderLineIds( + pickOrderLineIds: List, + stockOutLinesByPickOrderLineId: Map>, + ): String = + pickOrderLineIds.flatMap { polId -> + stockOutLinesByPickOrderLineId[polId].orEmpty().mapNotNull { it.lotNo } + }.distinct().joinToString(", ") + + fun routeFromStockOutsForPickOrderLineIds( + pickOrderLineIds: List, stockOutLinesByPickOrderLineId: Map>, illById: Map, ): String { - val polIds = pickOrderLineIdsByItemId[itemId] ?: return "-" val codes = linkedSetOf() - for (polId in polIds) { + for (polId in pickOrderLineIds) { for (sol in stockOutLinesByPickOrderLineId[polId].orEmpty()) { val illId = sol.inventoryLotLineId ?: continue val ill = illById[illId] ?: continue @@ -1403,6 +1459,16 @@ open class DeliveryOrderService( } return if (codes.isEmpty()) "-" else codes.joinToString(", ") } + + fun routeFromStockOutsForItem( + itemId: Long, + pickOrderLineIdsByItemId: Map>, + stockOutLinesByPickOrderLineId: Map>, + illById: Map, + ): String { + val polIds = pickOrderLineIdsByItemId[itemId] ?: return "-" + return routeFromStockOutsForPickOrderLineIds(polIds, stockOutLinesByPickOrderLineId, illById) + } //Print Delivery Note @Transactional open fun printDeliveryNote(request: PrintDeliveryNoteRequest) { diff --git a/src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DoWorkbenchMainService.kt b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DoWorkbenchMainService.kt index 72d084b..d0dd4b6 100644 --- a/src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DoWorkbenchMainService.kt +++ b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DoWorkbenchMainService.kt @@ -2638,6 +2638,7 @@ return MessageResponse( val pickOrderIds: List, val pickOrderCodes: List, val deliveryOrderIds: List, + val pickOrderIdByDoId: Map, ) private fun getWorkbenchPrintContext(deliveryOrderPickOrderId: Long): WorkbenchPrintContext { @@ -2660,7 +2661,12 @@ return MessageResponse( val pickOrderIds = rows.mapNotNull { (it["id"] as? Number)?.toLong() }.distinct() val pickOrderCodes = rows.mapNotNull { it["code"]?.toString()?.takeIf { s -> s.isNotBlank() } }.distinct() - val deliveryOrderIds = rows.mapNotNull { (it["doId"] as? Number)?.toLong() }.distinct() + val pickOrderIdByDoId = rows.mapNotNull { row -> + val doId = (row["doId"] as? Number)?.toLong() + val poId = (row["id"] as? Number)?.toLong() + if (doId != null && poId != null) doId to poId else null + }.toMap() + val deliveryOrderIds = pickOrderIdByDoId.keys.distinct() if (deliveryOrderIds.isEmpty()) { throw IllegalStateException("DeliveryOrderPickOrder $deliveryOrderPickOrderId has no associated delivery orders") } @@ -2670,10 +2676,11 @@ return MessageResponse( pickOrderIds = pickOrderIds, pickOrderCodes = pickOrderCodes, deliveryOrderIds = deliveryOrderIds, + pickOrderIdByDoId = pickOrderIdByDoId, ) } - private fun exportDeliveryNoteWorkbench(request: ExportDeliveryNoteRequest): Map { + open fun exportDeliveryNoteWorkbench(request: ExportDeliveryNoteRequest): Map { val DELIVERYNOTE_PDF = "DeliveryNote/DeliveryNotePDF.jrxml" val resource = ClassPathResource(DELIVERYNOTE_PDF) if (!resource.exists()) { @@ -2693,13 +2700,11 @@ return MessageResponse( throw NoSuchElementException("Delivery orders not found for IDs: ${ctx.deliveryOrderIds}") } - val allLines = deliveryNoteInfo.flatMap { info -> info.deliveryOrderLines.map { it } } + val exportLines = deliveryOrderService.deliveryNoteExportLines(deliveryNoteInfo) + val pickOrderLines = ctx.pickOrderIds.flatMap { pid -> pickOrderLineRepository.findAllByPickOrderId(pid) } - val pickOrderLineIdsByItemId = pickOrderLines - .groupBy { it.item?.id } - .mapValues { (_, lines) -> lines.mapNotNull { it.id } } val allPickOrderLineIds = pickOrderLines.mapNotNull { it.id } val stockOutLinesByPickOrderLineId: Map> = if (allPickOrderLineIds.isNotEmpty()) { @@ -2714,21 +2719,21 @@ return MessageResponse( ?.replace("/", "") ?.replace(" ", "") ?: "" - val uniqueItemIdsForSort = allLines.mapNotNull { it.itemId }.distinct() + val uniqueItemIdsForSort = exportLines.mapNotNull { it.line.itemId }.distinct() val itemsById = if (uniqueItemIdsForSort.isNotEmpty()) { itemsRepository.findAllById(uniqueItemIdsForSort).associateBy { it.id!! } } else { emptyMap() } val sortedLines = when (doStoreFloorKey) { - "2F" -> allLines.sortedWith( + "2F" -> exportLines.sortedWith( compareBy( - { line -> itemsById[line.itemId]?.item_Order ?: Int.MAX_VALUE }, - { line -> line.itemNo }, + { row -> itemsById[row.line.itemId]?.item_Order ?: Int.MAX_VALUE }, + { row -> row.line.itemNo }, ), ) - else -> allLines.sortedBy { line -> - line.itemId?.let { deliveryOrderService.getWarehouseOrderByItemId(it) } ?: Int.MAX_VALUE + else -> exportLines.sortedBy { row -> + row.line.itemId?.let { deliveryOrderService.getWarehouseOrderByItemId(it) } ?: Int.MAX_VALUE } } val allIllIds = stockOutLinesByPickOrderLineId.values @@ -2740,45 +2745,19 @@ return MessageResponse( emptyMap() } - sortedLines.forEach { line -> - val field = mutableMapOf() - field["sequenceNumber"] = (fields.size + 1).toString() - field["itemNo"] = line.itemNo - field["itemName"] = line.itemName ?: "" - field["uom"] = line.uom ?: "" - val actualPickQty = if (line.itemId != null) { - val pickOrderLineIdsForItem = pickOrderLineIdsByItemId[line.itemId] ?: emptyList() - val totalQty = pickOrderLineIdsForItem.sumOf { polId -> - stockOutLinesByPickOrderLineId[polId].orEmpty().sumOf { it.qty } - } - totalQty.toString() - } else { - line.qty.toString() - } - field["qty"] = actualPickQty - field["shortName"] = line.uomShortDesc ?: "" - val route = line.itemId?.let { itemId -> - deliveryOrderService.routeFromStockOutsForItem( - itemId, - pickOrderLineIdsByItemId, - stockOutLinesByPickOrderLineId, - illById, - ).takeIf { it != "-" } ?: deliveryOrderService.getWarehouseCodeByItemId(itemId) - } ?: "-" - field["route"] = route - val lotNo = line.itemId?.let { itemId -> - val pickOrderLineIdsForItem = pickOrderLineIdsByItemId[itemId] ?: emptyList() - val lotNumbers = pickOrderLineIdsForItem.flatMap { polId -> - stockOutLinesByPickOrderLineId[polId].orEmpty().mapNotNull { it.lotNo } - }.distinct().joinToString(", ") - lotNumbers.ifBlank { "沒有庫存" } - } ?: "沒有庫存" - field["lotNo"] = lotNo - val signOff = line.itemId?.let { itemId -> - if (itemsById[itemId]?.isEgg == true) "簽署: __________" else "" - } ?: "" - field["signOff"] = signOff - fields.add(field) + sortedLines.forEach { row -> + fields.add( + deliveryOrderService.buildDeliveryNotePdfLineField( + deliveryOrderId = row.deliveryOrderId, + line = row.line, + sequenceNumber = fields.size + 1, + pickOrderIdByDoId = ctx.pickOrderIdByDoId, + pickOrderLines = pickOrderLines, + stockOutLinesByPickOrderLineId = stockOutLinesByPickOrderLineId, + illById = illById, + itemsById = itemsById, + ), + ) } params["dnTitle"] = "送貨單" 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 f803111..f57fbfe 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 @@ -94,7 +94,7 @@ class DeliveryOrderController( } /** - * DO 輕量搜尋 v2:車線關鍵字正規化(`車線-X`/`x`/`車線-` 前綴併入未指派)、 + * DO 輕量搜索 v2:車線關鍵字正規化(`車線-X`/`x`/`車線-` 前綴併入未指派)、 * 允許供應商條件下分批掃描,避免單次載入過大;請求體同 [searchDoLite]。 */ @PostMapping("/search-do-lite-v2") diff --git a/src/main/java/com/ffii/fpsms/modules/deliveryOrder/web/DoWorkbenchController.kt b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/web/DoWorkbenchController.kt index ffcdcae..dc6b487 100644 --- a/src/main/java/com/ffii/fpsms/modules/deliveryOrder/web/DoWorkbenchController.kt +++ b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/web/DoWorkbenchController.kt @@ -24,6 +24,11 @@ import org.springframework.http.HttpStatus import org.springframework.http.MediaType import org.springframework.http.ResponseEntity import java.time.format.DateTimeFormatter +import jakarta.servlet.http.HttpServletResponse +import jakarta.validation.Valid +import net.sf.jasperreports.engine.JasperExportManager +import net.sf.jasperreports.engine.JasperPrint +import java.io.OutputStream @RestController @RequestMapping("/doPickOrder/workbench") class DoWorkbenchController( @@ -237,6 +242,20 @@ class DoWorkbenchController( doWorkbenchMainService.printDeliveryNoteWorkbench(request) } + @PostMapping("/DN") + fun downloadWorkbenchDN( + @Valid @RequestBody request: ExportDeliveryNoteRequest, + response: HttpServletResponse, + ) { + response.characterEncoding = "utf-8" + response.contentType = "application/pdf" + val out: OutputStream = response.outputStream + val pdf = doWorkbenchMainService.exportDeliveryNoteWorkbench(request) + val jasperPrint = pdf["report"] as JasperPrint + response.addHeader("filename", "${pdf["filename"]}.pdf") + out.write(JasperExportManager.exportReportToPdf(jasperPrint)) + } + @GetMapping("/print-DNLabels") fun printWorkbenchDNLabels(@ModelAttribute request: PrintDNLabelsRequest) { doWorkbenchMainService.printDNLabelsWorkbench(request)