|
|
@@ -1383,6 +1383,7 @@ open class DeliveryOrderService( |
|
|
) |
|
|
) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** FP-MTMS Version Checklist | Functions Ref. No. 89 | v1.0.0 | 2026-10-28 */ |
|
|
private fun exportDeliveryNoteFromRecord( |
|
|
private fun exportDeliveryNoteFromRecord( |
|
|
request: ExportDeliveryNoteRequest, |
|
|
request: ExportDeliveryNoteRequest, |
|
|
deliveryNote: net.sf.jasperreports.engine.JasperReport, |
|
|
deliveryNote: net.sf.jasperreports.engine.JasperReport, |
|
|
@@ -1476,15 +1477,19 @@ open class DeliveryOrderService( |
|
|
val deliveryOrderCodeById = deliveryOrders.associate { it.id!! to (it.code ?: "") } |
|
|
val deliveryOrderCodeById = deliveryOrders.associate { it.id!! to (it.code ?: "") } |
|
|
val isExtraByDoId = deliveryOrders.associate { it.id!! to it.isExtra } |
|
|
val isExtraByDoId = deliveryOrders.associate { it.id!! to it.isExtra } |
|
|
val replenishPdfIndex = doReplenishmentService.buildReplenishPdfIndex(deliveryOrderIds) |
|
|
val replenishPdfIndex = doReplenishmentService.buildReplenishPdfIndex(deliveryOrderIds) |
|
|
|
|
|
|
|
|
|
|
|
val pickOrderLineIdByDeliveryLine = matchDeliveryNotePickOrderLines( |
|
|
|
|
|
exportLines = exportLines, |
|
|
|
|
|
pickOrderIdByDoId = pickOrderIdByDoId, |
|
|
|
|
|
pickOrderLines = pickOrderLines, |
|
|
|
|
|
replenishmentPickOrderLineIds = replenishPdfIndex.replenishmentPickOrderLineIds(), |
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
sortedLines.forEach { row -> |
|
|
sortedLines.forEach { row -> |
|
|
fields.add( |
|
|
fields.add( |
|
|
buildDeliveryNotePdfLineField( |
|
|
buildDeliveryNotePdfLineField( |
|
|
deliveryOrderId = row.deliveryOrderId, |
|
|
deliveryOrderId = row.deliveryOrderId, |
|
|
line = row.line, |
|
|
line = row.line, |
|
|
sequenceNumber = fields.size + 1, |
|
|
sequenceNumber = fields.size + 1, |
|
|
pickOrderIdByDoId = pickOrderIdByDoId, |
|
|
|
|
|
pickOrderLines = pickOrderLines, |
|
|
|
|
|
stockOutLinesByPickOrderLineId = stockOutLinesByPickOrderLineId, |
|
|
stockOutLinesByPickOrderLineId = stockOutLinesByPickOrderLineId, |
|
|
illById = illById, |
|
|
illById = illById, |
|
|
itemsById = itemsById, |
|
|
itemsById = itemsById, |
|
|
@@ -1493,6 +1498,7 @@ open class DeliveryOrderService( |
|
|
headerTicketNo = null, |
|
|
headerTicketNo = null, |
|
|
headerIsMerge = false, |
|
|
headerIsMerge = false, |
|
|
replenishPdfIndex = replenishPdfIndex, |
|
|
replenishPdfIndex = replenishPdfIndex, |
|
|
|
|
|
pickOrderLineIdByDeliveryLine = pickOrderLineIdByDeliveryLine, |
|
|
), |
|
|
), |
|
|
) |
|
|
) |
|
|
} |
|
|
} |
|
|
@@ -1553,17 +1559,47 @@ open class DeliveryOrderService( |
|
|
info.deliveryOrderLines.filter { it.deleted != true }.map { line -> DeliveryNoteExportLine(info.id, line) } |
|
|
info.deliveryOrderLines.filter { it.deleted != true }.map { line -> DeliveryNoteExportLine(info.id, line) } |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
fun resolvePickOrderLineIdForDeliveryNoteLine( |
|
|
|
|
|
deliveryOrderId: Long, |
|
|
|
|
|
itemId: Long?, |
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* FP-MTMS Version Checklist | Functions Ref. No. 89 | v1.0.0 | 2026-10-28 |
|
|
|
|
|
* Pair each delivery-order line with its own pick line. |
|
|
|
|
|
* Same item on one pick order is matched by qty first (4↔4, 1↔1). |
|
|
|
|
|
* Equal qty is paired in id order. Replenishment pick lines are left out; |
|
|
|
|
|
* those rows still print from do_replenishment.pickOrderLineId. |
|
|
|
|
|
*/ |
|
|
|
|
|
fun matchDeliveryNotePickOrderLines( |
|
|
|
|
|
exportLines: List<DeliveryNoteExportLine>, |
|
|
pickOrderIdByDoId: Map<Long, Long>, |
|
|
pickOrderIdByDoId: Map<Long, Long>, |
|
|
pickOrderLines: List<PickOrderLine>, |
|
|
pickOrderLines: List<PickOrderLine>, |
|
|
): 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 |
|
|
|
|
|
|
|
|
replenishmentPickOrderLineIds: Set<Long> = emptySet(), |
|
|
|
|
|
): Map<Pair<Long, Long>, Long> { |
|
|
|
|
|
val keys = exportLines.mapNotNull { row -> |
|
|
|
|
|
val itemId = row.line.itemId ?: return@mapNotNull null |
|
|
|
|
|
if (row.line.deleted == true) return@mapNotNull null |
|
|
|
|
|
DeliveryNoteLineMatchKey( |
|
|
|
|
|
deliveryOrderId = row.deliveryOrderId, |
|
|
|
|
|
lineId = row.line.id, |
|
|
|
|
|
itemId = itemId, |
|
|
|
|
|
qty = row.line.qty, |
|
|
|
|
|
) |
|
|
|
|
|
} |
|
|
|
|
|
val candidates = pickOrderLines.mapNotNull { pol -> |
|
|
|
|
|
val polId = pol.id ?: return@mapNotNull null |
|
|
|
|
|
val pickOrderId = pol.pickOrder?.id ?: return@mapNotNull null |
|
|
|
|
|
val itemId = pol.item?.id ?: return@mapNotNull null |
|
|
|
|
|
val qty = pol.qty ?: return@mapNotNull null |
|
|
|
|
|
DeliveryNotePickLineCandidate( |
|
|
|
|
|
id = polId, |
|
|
|
|
|
pickOrderId = pickOrderId, |
|
|
|
|
|
itemId = itemId, |
|
|
|
|
|
qty = qty, |
|
|
|
|
|
) |
|
|
|
|
|
} |
|
|
|
|
|
return DeliveryNotePickLineMatcher.match( |
|
|
|
|
|
lines = keys, |
|
|
|
|
|
pickOrderIdByDoId = pickOrderIdByDoId, |
|
|
|
|
|
pickLines = candidates, |
|
|
|
|
|
replenishmentPickOrderLineIds = replenishmentPickOrderLineIds, |
|
|
|
|
|
) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
fun parseTicketTypeLetter(ticketNo: String?): String? { |
|
|
fun parseTicketTypeLetter(ticketNo: String?): String? { |
|
|
@@ -1675,12 +1711,11 @@ open class DeliveryOrderService( |
|
|
return field |
|
|
return field |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** FP-MTMS Version Checklist | Functions Ref. No. 89 | v1.0.0 | 2026-10-28 */ |
|
|
fun buildDeliveryNotePdfLineField( |
|
|
fun buildDeliveryNotePdfLineField( |
|
|
deliveryOrderId: Long, |
|
|
deliveryOrderId: Long, |
|
|
line: DeliveryOrderLineInfo, |
|
|
line: DeliveryOrderLineInfo, |
|
|
sequenceNumber: Int, |
|
|
sequenceNumber: Int, |
|
|
pickOrderIdByDoId: Map<Long, Long>, |
|
|
|
|
|
pickOrderLines: List<PickOrderLine>, |
|
|
|
|
|
stockOutLinesByPickOrderLineId: Map<Long, List<StockOutLineInfo>>, |
|
|
stockOutLinesByPickOrderLineId: Map<Long, List<StockOutLineInfo>>, |
|
|
illById: Map<Long, InventoryLotLine>, |
|
|
illById: Map<Long, InventoryLotLine>, |
|
|
itemsById: Map<Long, Items>, |
|
|
itemsById: Map<Long, Items>, |
|
|
@@ -1689,6 +1724,7 @@ open class DeliveryOrderService( |
|
|
headerTicketNo: String? = null, |
|
|
headerTicketNo: String? = null, |
|
|
headerIsMerge: Boolean = false, |
|
|
headerIsMerge: Boolean = false, |
|
|
replenishPdfIndex: DoReplenishmentService.ReplenishPdfIndex = DoReplenishmentService.ReplenishPdfIndex.EMPTY, |
|
|
replenishPdfIndex: DoReplenishmentService.ReplenishPdfIndex = DoReplenishmentService.ReplenishPdfIndex.EMPTY, |
|
|
|
|
|
pickOrderLineIdByDeliveryLine: Map<Pair<Long, Long>, Long> = emptyMap(), |
|
|
): MutableMap<String, Any> { |
|
|
): MutableMap<String, Any> { |
|
|
val field = mutableMapOf<String, Any>() |
|
|
val field = mutableMapOf<String, Any>() |
|
|
val isExtra = isExtraDeliveryTicket( |
|
|
val isExtra = isExtraDeliveryTicket( |
|
|
@@ -1697,12 +1733,7 @@ open class DeliveryOrderService( |
|
|
headerIsMerge = headerIsMerge, |
|
|
headerIsMerge = headerIsMerge, |
|
|
) |
|
|
) |
|
|
|
|
|
|
|
|
val polId = resolvePickOrderLineIdForDeliveryNoteLine( |
|
|
|
|
|
deliveryOrderId = deliveryOrderId, |
|
|
|
|
|
itemId = line.itemId, |
|
|
|
|
|
pickOrderIdByDoId = pickOrderIdByDoId, |
|
|
|
|
|
pickOrderLines = pickOrderLines, |
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
val polId = pickOrderLineIdByDeliveryLine[deliveryOrderId to line.id] |
|
|
val isReplenish = replenishPdfIndex.matches(deliveryOrderId, line.itemId, polId) |
|
|
val isReplenish = replenishPdfIndex.matches(deliveryOrderId, line.itemId, polId) |
|
|
|
|
|
|
|
|
field["sequenceNumber"] = formatSequenceNumber(sequenceNumber, isExtra, isReplenish) |
|
|
field["sequenceNumber"] = formatSequenceNumber(sequenceNumber, isExtra, isReplenish) |
|
|
@@ -2806,3 +2837,52 @@ val inventoryLotLine = illId?.let { inventoryLotLineMap[it] } |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
internal data class DeliveryNoteLineMatchKey( |
|
|
|
|
|
val deliveryOrderId: Long, |
|
|
|
|
|
val lineId: Long, |
|
|
|
|
|
val itemId: Long, |
|
|
|
|
|
val qty: BigDecimal, |
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
internal data class DeliveryNotePickLineCandidate( |
|
|
|
|
|
val id: Long, |
|
|
|
|
|
val pickOrderId: Long, |
|
|
|
|
|
val itemId: Long, |
|
|
|
|
|
val qty: BigDecimal, |
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
internal object DeliveryNotePickLineMatcher { |
|
|
|
|
|
/** FP-MTMS Version Checklist | Functions Ref. No. 89 | v1.0.0 | 2026-10-28 */ |
|
|
|
|
|
fun match( |
|
|
|
|
|
lines: List<DeliveryNoteLineMatchKey>, |
|
|
|
|
|
pickOrderIdByDoId: Map<Long, Long>, |
|
|
|
|
|
pickLines: List<DeliveryNotePickLineCandidate>, |
|
|
|
|
|
replenishmentPickOrderLineIds: Set<Long> = emptySet(), |
|
|
|
|
|
): Map<Pair<Long, Long>, Long> { |
|
|
|
|
|
val result = mutableMapOf<Pair<Long, Long>, Long>() |
|
|
|
|
|
val rowsByDoAndItem = lines.groupBy { it.deliveryOrderId to it.itemId } |
|
|
|
|
|
for ((key, rows) in rowsByDoAndItem) { |
|
|
|
|
|
val (deliveryOrderId, itemId) = key |
|
|
|
|
|
val pickOrderId = pickOrderIdByDoId[deliveryOrderId] ?: continue |
|
|
|
|
|
val available = pickLines |
|
|
|
|
|
.filter { pol -> |
|
|
|
|
|
pol.pickOrderId == pickOrderId && |
|
|
|
|
|
pol.itemId == itemId && |
|
|
|
|
|
pol.id !in replenishmentPickOrderLineIds |
|
|
|
|
|
} |
|
|
|
|
|
.sortedBy { it.id } |
|
|
|
|
|
.toMutableList() |
|
|
|
|
|
for (row in rows.sortedBy { it.lineId }) { |
|
|
|
|
|
val qtyIndex = available.indexOfFirst { it.qty.compareTo(row.qty) == 0 } |
|
|
|
|
|
val chosen = when { |
|
|
|
|
|
qtyIndex >= 0 -> available.removeAt(qtyIndex) |
|
|
|
|
|
available.isNotEmpty() -> available.removeAt(0) |
|
|
|
|
|
else -> null |
|
|
|
|
|
} ?: continue |
|
|
|
|
|
result[row.deliveryOrderId to row.lineId] = chosen.id |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
return result |
|
|
|
|
|
} |
|
|
|
|
|
} |