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 87159d3..0ee4173 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 @@ -1157,26 +1157,39 @@ open class DeliveryOrderService( val truckNo = doPickOrder.truckLanceCode ?: "" val selectedPickOrder = pickOrderRepository.findById(pickOrderId).orElse(null) - val allLines = deliveryNoteInfo.flatMap { info -> - info.deliveryOrderLines.map { line -> line } - } + val deliveryOrders = deliveryOrderRepository.findAllById(deliveryOrderIds) + val deliveryOrderCodeById = deliveryOrders.associate { it.id!! to (it.code ?: "") } + val isExtraByDoId = deliveryOrders.associate { it.id!! to it.isExtra } - val sortedLines = allLines.sortedBy { line -> - line.itemId?.let { itemId -> + val exportLines = deliveryNoteExportLines(deliveryNoteInfo) + val sortedLines = exportLines.sortedBy { row -> + row.line.itemId?.let { itemId -> getWarehouseOrderByItemId(itemId) } ?: Int.MAX_VALUE } - val uniqueItemIds = sortedLines.mapNotNull { it.itemId }.distinct() + val uniqueItemIds = sortedLines.mapNotNull { it.line.itemId }.distinct() val itemsMap = if (uniqueItemIds.isNotEmpty()) { itemsRepository.findAllById(uniqueItemIds).associateBy { it.id } } else { emptyMap() } - sortedLines.forEach { line -> + sortedLines.forEach { row -> + val line = row.line val field = mutableMapOf() - - field["sequenceNumber"] = (fields.size + 1).toString() + val sequenceNumber = fields.size + 1 + + field["sequenceNumber"] = formatSequenceNumber( + sequenceNumber, + isExtraDeliveryTicket( + lineTicketNo = null, + deliveryOrderIsExtra = isExtraByDoId[row.deliveryOrderId] == true, + headerIsMerge = false, + ), + ) + field["deliveryOrderCode"] = formatDoCodeWithUnderlineLast4( + deliveryOrderCodeById[row.deliveryOrderId].orEmpty(), + ) field["itemNo"] = line.itemNo field["itemName"] = line.itemName ?: "" field["uom"] = line.uom ?: "" @@ -1221,13 +1234,14 @@ open class DeliveryOrderService( params["numOfCarton"] = "" } - params["shopName"] = doPickOrder.shopName ?: deliveryNoteInfo[0].shopName ?: "" + params["shopName"] = formatShopNameForDeliveryNote( + doPickOrder.shopName ?: deliveryNoteInfo[0].shopName ?: "", + ) params["shopAddress"] = deliveryNoteInfo[0].shopAddress ?: "" params["deliveryDate"] = deliveryNoteInfo[0].estimatedArrivalDate?.format(DateTimeFormatter.ISO_LOCAL_DATE) ?: "" params["truckNo"] = truckNo - params["ShopPurchaseOrderNo"] = doPickOrder.deliveryOrderCode ?: deliveryNoteInfo.joinToString(", ") { it.code } - params["FGPickOrderNo"] = doPickOrder.pickOrderCode ?: selectedPickOrder?.code ?: "" + params["deliveryOrderCodeAll"] = formatDoCodesPlain(deliveryNoteInfo.map { it.code }) params["loadingSequence"] = doPickOrder.loadingSequence?.let { "裝載順序:$it" } ?: "" @@ -1326,6 +1340,10 @@ open class DeliveryOrderService( } else { emptyMap() } + + val deliveryOrders = deliveryOrderRepository.findAllById(deliveryOrderIds) + val deliveryOrderCodeById = deliveryOrders.associate { it.id!! to (it.code ?: "") } + val isExtraByDoId = deliveryOrders.associate { it.id!! to it.isExtra } sortedLines.forEach { row -> fields.add( @@ -1338,6 +1356,10 @@ open class DeliveryOrderService( stockOutLinesByPickOrderLineId = stockOutLinesByPickOrderLineId, illById = illById, itemsById = itemsById, + deliveryOrderCodeById = deliveryOrderCodeById, + isExtraByDoId = isExtraByDoId, + headerTicketNo = null, + headerIsMerge = false, ), ) } @@ -1353,14 +1375,14 @@ open class DeliveryOrderService( params["numOfCarton"] = "" } - params["shopName"] = doPickOrderRecord.shopName ?: deliveryNoteInfo[0].shopName ?: "" + params["shopName"] = formatShopNameForDeliveryNote( + doPickOrderRecord.shopName ?: deliveryNoteInfo[0].shopName ?: "", + ) params["shopAddress"] = deliveryNoteInfo[0].shopAddress ?: "" params["deliveryDate"] = deliveryNoteInfo[0].estimatedArrivalDate?.format(DateTimeFormatter.ISO_LOCAL_DATE) ?: "" params["truckNo"] = truckNo - params["ShopPurchaseOrderNo"] = - doPickOrderRecord.deliveryOrderCode ?: deliveryNoteInfo.joinToString(", ") { it.code } - params["FGPickOrderNo"] = doPickOrderRecord.pickOrderCode ?: selectedPickOrder?.code ?: "" + params["deliveryOrderCodeAll"] = formatDoCodesPlain(deliveryNoteInfo.map { it.code }) params["loadingSequence"] = doPickOrderRecord.loadingSequence?.let { "裝載順序:$it" } ?: "" @@ -1394,6 +1416,61 @@ open class DeliveryOrderService( ?.id } + fun parseTicketTypeLetter(ticketNo: String?): String? { + if (ticketNo.isNullOrBlank()) return null + val parts = ticketNo.split("-") + return if (parts.size >= 2) parts[1] else null + } + + fun isExtraDeliveryTicket( + lineTicketNo: String?, + deliveryOrderIsExtra: Boolean, + headerIsMerge: Boolean, + ): Boolean { + if (headerIsMerge) return deliveryOrderIsExtra + if (lineTicketNo != null) return parseTicketTypeLetter(lineTicketNo) == "E" + return deliveryOrderIsExtra + } + + private fun escapeXmlForJasperStyled(text: String): String = + text.replace("&", "&").replace("<", "<").replace(">", ">") + + fun formatDoCodeWithUnderlineLast4(code: String): String { + if (code.isBlank()) return "" + return if (code.length <= 4) { + "" + } else { + val prefix = escapeXmlForJasperStyled(code.dropLast(4)) + val suffix = escapeXmlForJasperStyled(code.takeLast(4)) + "$prefix" + } + } + + fun formatShopNameForDeliveryNote(raw: String): String { + if (raw.isBlank()) return "" + val trimmed = raw.trim() + val (shopCodePart, restPart) = trimmed.split(" - ", limit = 2).let { parts -> + (parts.getOrNull(0)?.trim().orEmpty()) to (parts.getOrNull(1)?.trim().orEmpty()) + } + return if (shopCodePart.isNotBlank() && restPart.isNotBlank()) { + val styledCode = + "" + "$styledCode - ${escapeXmlForJasperStyled(restPart)}" + } else { + escapeXmlForJasperStyled(trimmed) + } + } + + fun formatDoCodesStyled(codes: Collection): String = + codes.filter { it.isNotBlank() }.distinct().sorted().joinToString(", ") { formatDoCodeWithUnderlineLast4(it) } + + fun formatDoCodesPlain(codes: Collection): String = + codes.filter { it.isNotBlank() }.distinct().sorted() + .joinToString(", ") { escapeXmlForJasperStyled(it) } + + fun formatSequenceNumber(sequenceNumber: Int, isExtra: Boolean): String = + if (isExtra) "$sequenceNumber(加單)" else sequenceNumber.toString() + fun buildDeliveryNotePdfLineField( deliveryOrderId: Long, line: DeliveryOrderLineInfo, @@ -1403,9 +1480,21 @@ open class DeliveryOrderService( stockOutLinesByPickOrderLineId: Map>, illById: Map, itemsById: Map, + deliveryOrderCodeById: Map = emptyMap(), + isExtraByDoId: Map = emptyMap(), + headerTicketNo: String? = null, + headerIsMerge: Boolean = false, ): MutableMap { val field = mutableMapOf() - field["sequenceNumber"] = sequenceNumber.toString() + val isExtra = isExtraDeliveryTicket( + lineTicketNo = headerTicketNo, + deliveryOrderIsExtra = isExtraByDoId[deliveryOrderId] == true, + headerIsMerge = headerIsMerge, + ) + field["sequenceNumber"] = formatSequenceNumber(sequenceNumber, isExtra) + field["deliveryOrderCode"] = formatDoCodeWithUnderlineLast4( + deliveryOrderCodeById[deliveryOrderId].orEmpty(), + ) field["itemNo"] = line.itemNo field["itemName"] = line.itemName ?: "" field["uom"] = line.uom ?: "" 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 081db78..680fea7 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 @@ -2679,6 +2679,21 @@ return MessageResponse( ) } + private fun buildDeliveryOrderCodeAll(deliveryNoteCode: String): String { + val sql = """ + SELECT DISTINCT do.code AS code + FROM delivery_order_pick_order dop + JOIN pick_order po ON po.deliveryOrderPickOrderId = dop.id AND po.deleted = 0 + JOIN delivery_order do ON do.id = po.doId + WHERE dop.deliveryNoteCode = :deliveryNoteCode + AND dop.deleted = 0 + ORDER BY do.code + """.trimIndent() + val codes = jdbcDao.queryForList(sql, mapOf("deliveryNoteCode" to deliveryNoteCode)) + .mapNotNull { row -> row["code"]?.toString() } + return deliveryOrderService.formatDoCodesPlain(codes) + } + open fun exportDeliveryNoteWorkbench(request: ExportDeliveryNoteRequest): Map { val DELIVERYNOTE_PDF = "DeliveryNote/DeliveryNotePDF.jrxml" val resource = ClassPathResource(DELIVERYNOTE_PDF) @@ -2744,6 +2759,12 @@ return MessageResponse( emptyMap() } + val deliveryOrders = deliveryOrderRepository.findAllById(ctx.deliveryOrderIds) + val deliveryOrderCodeById = deliveryOrders.associate { it.id!! to (it.code ?: "") } + val isExtraByDoId = deliveryOrders.associate { it.id!! to it.isExtra } + val headerIsMerge = ctx.header.ticketNo?.startsWith("TI-M-") == true + val headerTicketNo = ctx.header.ticketNo + sortedLines.forEach { row -> fields.add( deliveryOrderService.buildDeliveryNotePdfLineField( @@ -2755,6 +2776,10 @@ return MessageResponse( stockOutLinesByPickOrderLineId = stockOutLinesByPickOrderLineId, illById = illById, itemsById = itemsById, + deliveryOrderCodeById = deliveryOrderCodeById, + isExtraByDoId = isExtraByDoId, + headerTicketNo = headerTicketNo, + headerIsMerge = headerIsMerge, ), ) } @@ -2765,14 +2790,17 @@ return MessageResponse( params["deliveryNoteCodeTitle"] = "送貨單編號:" params["deliveryNoteCode"] = ctx.header.deliveryNoteCode ?: "" params["numOfCarton"] = request.numOfCarton.toString().takeUnless { it == "0" } ?: "" - params["shopName"] = ctx.header.shopName ?: deliveryNoteInfo[0].shopName ?: "" + params["shopName"] = deliveryOrderService.formatShopNameForDeliveryNote( + ctx.header.shopName ?: deliveryNoteInfo[0].shopName ?: "", + ) params["shopAddress"] = deliveryNoteInfo[0].shopAddress ?: "" params["deliveryDate"] = ctx.header.requiredDeliveryDate?.format(DateTimeFormatter.ISO_LOCAL_DATE) ?: deliveryNoteInfo[0].estimatedArrivalDate?.format(DateTimeFormatter.ISO_LOCAL_DATE) ?: "" params["truckNo"] = ctx.header.truckLanceCode ?: "" - params["ShopPurchaseOrderNo"] = deliveryNoteInfo.joinToString(", ") { it.code } - params["FGPickOrderNo"] = ctx.pickOrderCodes.joinToString(", ") + params["deliveryOrderCodeAll"] = ctx.header.deliveryNoteCode?.let(::buildDeliveryOrderCodeAll) + ?.takeIf { it.isNotBlank() } + ?: deliveryOrderService.formatDoCodesPlain(deliveryNoteInfo.map { it.code }) params["loadingSequence"] = "" return mapOf( diff --git a/src/main/resources/DeliveryNote/DeliveryNotePDF.jrxml b/src/main/resources/DeliveryNote/DeliveryNotePDF.jrxml index c3ef3bc..90a0b24 100644 --- a/src/main/resources/DeliveryNote/DeliveryNotePDF.jrxml +++ b/src/main/resources/DeliveryNote/DeliveryNotePDF.jrxml @@ -1,404 +1,404 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - <band/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <band/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +