|
|
|
@@ -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<String, Any> { |
|
|
|
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<String, Any>() |
|
|
|
val fields = mutableListOf<MutableMap<String, Any>>() |
|
|
|
|
|
|
|
for (info in cartonLabelInfo) { |
|
|
|
val field = mutableMapOf<String, Any>() |
|
|
|
} |
|
|
|
|
|
|
|
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<String, Any> { |
|
|
|
validateCartonReprintRange( |
|
|
|
fromCarton = request.fromCarton, |
|
|
|
toCarton = request.toCarton, |
|
|
|
totalCartonsOnShipment = request.totalCartonsOnShipment, |
|
|
|
) |
|
|
|
|
|
|
|
for (cartonNumber in 1..request.numOfCarton) { |
|
|
|
val field = mutableMapOf<String, Any>() |
|
|
|
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<String, Any>, |
|
|
|
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<String, Any>() |
|
|
|
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<DnCartonLabelSheetRow> { |
|
|
|
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 |
|
|
|
|