|
|
|
@@ -1454,6 +1454,157 @@ open class PickOrderService( |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 強制完成 do_pick_order:僅將未完成的 pick_order / pick_order_line 標記為完成(不修改 stock_out_line 數量), |
|
|
|
* 並執行與正常完成相同的歸檔(do_pick_order → do_pick_order_record)與 delivery_order 狀態更新。 |
|
|
|
* 僅適用於仍存在于 do_pick_order 表中的單據。 |
|
|
|
*/ |
|
|
|
@Transactional(rollbackFor = [Exception::class]) |
|
|
|
open fun forceCompleteDoPickOrder(doPickOrderId: Long): MessageResponse { |
|
|
|
val dpo = doPickOrderRepository.findById(doPickOrderId).orElse(null) |
|
|
|
?: return MessageResponse( |
|
|
|
id = null, |
|
|
|
name = "Do pick order not found", |
|
|
|
code = "NOT_FOUND", |
|
|
|
type = "doPickOrder", |
|
|
|
message = "do_pick_order not found (may already be archived)", |
|
|
|
errorPosition = null, |
|
|
|
) |
|
|
|
if (dpo.ticketStatus == DoPickOrderStatus.completed) { |
|
|
|
return MessageResponse( |
|
|
|
id = dpo.id, |
|
|
|
name = dpo.ticketNo, |
|
|
|
code = "ALREADY_COMPLETED", |
|
|
|
type = "doPickOrder", |
|
|
|
message = "Do pick order already completed", |
|
|
|
errorPosition = null, |
|
|
|
) |
|
|
|
} |
|
|
|
if (dpo.ticketStatus !in listOf(DoPickOrderStatus.pending, DoPickOrderStatus.released)) { |
|
|
|
return MessageResponse( |
|
|
|
id = dpo.id, |
|
|
|
name = dpo.ticketNo, |
|
|
|
code = "INVALID_STATUS", |
|
|
|
type = "doPickOrder", |
|
|
|
message = "Invalid ticket status for force complete", |
|
|
|
errorPosition = null, |
|
|
|
) |
|
|
|
} |
|
|
|
|
|
|
|
val dpoLines = doPickOrderLineRepository.findByDoPickOrderIdAndDeletedFalse(doPickOrderId) |
|
|
|
val pickOrderIds = dpoLines.mapNotNull { it.pickOrderId }.distinct() |
|
|
|
if (pickOrderIds.isEmpty()) { |
|
|
|
return MessageResponse( |
|
|
|
id = dpo.id, |
|
|
|
name = dpo.ticketNo, |
|
|
|
code = "NO_PICK_ORDERS", |
|
|
|
type = "doPickOrder", |
|
|
|
message = "No pick orders linked to this do_pick_order", |
|
|
|
errorPosition = null, |
|
|
|
) |
|
|
|
} |
|
|
|
|
|
|
|
for (pickOrderId in pickOrderIds) { |
|
|
|
val pols = pickOrderLineRepository.findByPickOrderId(pickOrderId).filter { !it.deleted } |
|
|
|
pols.forEach { pol -> |
|
|
|
pol.status = PickOrderLineStatus.COMPLETED |
|
|
|
} |
|
|
|
if (pols.isNotEmpty()) { |
|
|
|
pickOrderLineRepository.saveAll(pols) |
|
|
|
} |
|
|
|
val pickOrder = pickOrderRepository.findById(pickOrderId).orElse(null) ?: continue |
|
|
|
pickOrder.status = PickOrderStatus.COMPLETED |
|
|
|
pickOrder.completeDate = LocalDateTime.now() |
|
|
|
pickOrderRepository.save(pickOrder) |
|
|
|
|
|
|
|
if (pickOrder.jobOrder != null) { |
|
|
|
val joPickOrders = joPickOrderRepository.findByPickOrderId(pickOrderId) |
|
|
|
joPickOrders.forEach { it.ticketCompleteTime = LocalDateTime.now() } |
|
|
|
if (joPickOrders.isNotEmpty()) { |
|
|
|
joPickOrderRepository.saveAll(joPickOrders) |
|
|
|
} |
|
|
|
val joPickOrderRecords = joPickOrderRecordRepository.findByPickOrderId(pickOrderId) |
|
|
|
joPickOrderRecords.forEach { it.ticketCompleteTime = LocalDateTime.now() } |
|
|
|
if (joPickOrderRecords.isNotEmpty()) { |
|
|
|
joPickOrderRecordRepository.saveAll(joPickOrderRecords) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
moveDoPickOrderToCompletedRecordAfterForce(dpo) |
|
|
|
|
|
|
|
return MessageResponse( |
|
|
|
id = dpo.id, |
|
|
|
code = "SUCCESS", |
|
|
|
name = dpo.ticketNo, |
|
|
|
type = "doPickOrder", |
|
|
|
message = "Force completed", |
|
|
|
errorPosition = null, |
|
|
|
entity = null, |
|
|
|
) |
|
|
|
} |
|
|
|
|
|
|
|
private fun moveDoPickOrderToCompletedRecordAfterForce(dpo: DoPickOrder) { |
|
|
|
val doOrderIdForDelivery = dpo.doOrderId |
|
|
|
val prefix = "DN" |
|
|
|
val midfix = CodeGenerator.DEFAULT_MIDFIX |
|
|
|
val latestCode = doPickOrderRecordRepository.findLatestDeliveryNoteCodeByPrefix("$prefix-$midfix") |
|
|
|
val deliveryNoteCode = CodeGenerator.generateNo(prefix = prefix, midfix = midfix, latestCode = latestCode) |
|
|
|
|
|
|
|
val dpoRecord = DoPickOrderRecord( |
|
|
|
recordId = dpo.id, |
|
|
|
storeId = dpo.storeId ?: "", |
|
|
|
ticketNo = dpo.ticketNo ?: "", |
|
|
|
ticketStatus = DoPickOrderStatus.completed, |
|
|
|
truckId = dpo.truckId, |
|
|
|
truckDepartureTime = dpo.truckDepartureTime, |
|
|
|
pickOrderId = dpo.pickOrderId, |
|
|
|
doOrderId = dpo.doOrderId, |
|
|
|
ticketReleaseTime = dpo.ticketReleaseTime, |
|
|
|
shopId = dpo.shopId, |
|
|
|
handlerName = dpo.handlerName, |
|
|
|
handledBy = dpo.handledBy, |
|
|
|
ticketCompleteDateTime = LocalDateTime.now(), |
|
|
|
truckLanceCode = dpo.truckLanceCode, |
|
|
|
shopCode = dpo.shopCode, |
|
|
|
shopName = dpo.shopName, |
|
|
|
requiredDeliveryDate = dpo.requiredDeliveryDate, |
|
|
|
pickOrderCode = dpo.pickOrderCode, |
|
|
|
deliveryOrderCode = dpo.deliveryOrderCode, |
|
|
|
deliveryNoteCode = deliveryNoteCode, |
|
|
|
loadingSequence = dpo.loadingSequence, |
|
|
|
) |
|
|
|
val savedHeader = doPickOrderRecordRepository.save(dpoRecord) |
|
|
|
|
|
|
|
val lines = doPickOrderLineRepository.findByDoPickOrderIdAndDeletedFalse(dpo.id!!) |
|
|
|
val lineRecords = lines.map { l: DoPickOrderLine -> |
|
|
|
DoPickOrderLineRecord().apply { |
|
|
|
this.recordId = l.id |
|
|
|
this.doPickOrderId = savedHeader.recordId |
|
|
|
this.pickOrderId = l.pickOrderId |
|
|
|
this.doOrderId = l.doOrderId |
|
|
|
this.pickOrderCode = l.pickOrderCode |
|
|
|
this.deliveryOrderCode = l.deliveryOrderCode |
|
|
|
this.status = l.status |
|
|
|
} |
|
|
|
} |
|
|
|
if (lineRecords.isNotEmpty()) { |
|
|
|
doPickOrderLineRecordRepository.saveAll(lineRecords) |
|
|
|
} |
|
|
|
if (lines.isNotEmpty()) { |
|
|
|
doPickOrderLineRepository.deleteAll(lines) |
|
|
|
} |
|
|
|
doPickOrderRepository.delete(dpo) |
|
|
|
|
|
|
|
doOrderIdForDelivery?.let { doId -> |
|
|
|
val deliveryOrder = deliveryOrderRepository.findByIdAndDeletedIsFalse(doId) |
|
|
|
if (deliveryOrder != null && deliveryOrder.status != DeliveryOrderStatus.COMPLETED) { |
|
|
|
deliveryOrder.status = DeliveryOrderStatus.COMPLETED |
|
|
|
deliveryOrderRepository.save(deliveryOrder) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
@Transactional(rollbackFor = [java.lang.Exception::class]) |
|
|
|
open fun checkAndCompletePickOrderByConsoCode(consoCode: String): MessageResponse { |
|
|
|
try { |
|
|
|
@@ -4100,72 +4251,73 @@ println("DEBUG sol polIds in linesResults: " + linesResults.mapNotNull { it["sto |
|
|
|
return null |
|
|
|
} |
|
|
|
|
|
|
|
open fun getCompletedDoPickOrders( |
|
|
|
userId: Long, |
|
|
|
private fun mapCompletedDoPickOrders( |
|
|
|
baseRecords: List<DoPickOrderRecord>, |
|
|
|
request: GetCompletedDoPickOrdersRequest |
|
|
|
): List<CompletedDoPickOrderResponse> { |
|
|
|
return try { |
|
|
|
val normalizedTargetDate = request.targetDate |
|
|
|
?.takeIf { it.isNotBlank() } |
|
|
|
?.replace("-", "") |
|
|
|
println("request.targetDate: $request.targetDate") |
|
|
|
println("request.shopName: $request.shopName") |
|
|
|
println("request.deliveryNoteCode: $request.deliveryNoteCode") |
|
|
|
val completedRecords = doPickOrderRecordRepository |
|
|
|
.findByHandledByAndTicketStatusAndDeletedFalse(userId, DoPickOrderStatus.completed) |
|
|
|
.filter { record -> |
|
|
|
val matchTargetDate = normalizedTargetDate.isNullOrBlank() || |
|
|
|
record.ticketCompleteDateTime |
|
|
|
?.format(DateTimeFormatter.ofPattern("yyyyMMdd")) |
|
|
|
?.contains(normalizedTargetDate, ignoreCase = true) == true |
|
|
|
val matchShop = request.shopName.isNullOrBlank() || |
|
|
|
record.shopName?.contains(request.shopName, ignoreCase = true) == true |
|
|
|
val matchDeliveryNoteCode = request.deliveryNoteCode.isNullOrBlank() || |
|
|
|
record.deliveryNoteCode?.contains(request.deliveryNoteCode, ignoreCase = true) == true |
|
|
|
matchTargetDate && matchShop && matchDeliveryNoteCode |
|
|
|
} |
|
|
|
.filter { (it.recordId ?: 0L) > 0 } |
|
|
|
|
|
|
|
?.takeIf { it.isNotBlank() } |
|
|
|
?.replace("-", "") |
|
|
|
println("request.targetDate: ${request.targetDate}") |
|
|
|
println("request.shopName: ${request.shopName}") |
|
|
|
println("request.deliveryNoteCode: ${request.deliveryNoteCode}") |
|
|
|
val completedRecords = baseRecords |
|
|
|
.filter { record -> |
|
|
|
val matchTargetDate = normalizedTargetDate.isNullOrBlank() || |
|
|
|
record.ticketCompleteDateTime |
|
|
|
?.format(DateTimeFormatter.ofPattern("yyyyMMdd")) |
|
|
|
?.contains(normalizedTargetDate, ignoreCase = true) == true |
|
|
|
val matchShop = request.shopName.isNullOrBlank() || |
|
|
|
record.shopName?.contains(request.shopName, ignoreCase = true) == true |
|
|
|
val matchDeliveryNoteCode = request.deliveryNoteCode.isNullOrBlank() || |
|
|
|
record.deliveryNoteCode?.contains(request.deliveryNoteCode, ignoreCase = true) == true |
|
|
|
val matchTruck = request.truckLanceCode.isNullOrBlank() || |
|
|
|
record.truckLanceCode?.contains(request.truckLanceCode!!, ignoreCase = true) == true |
|
|
|
matchTargetDate && matchShop && matchDeliveryNoteCode && matchTruck |
|
|
|
} |
|
|
|
.filter { (it.recordId ?: 0L) > 0 } |
|
|
|
|
|
|
|
if (completedRecords.isEmpty()) { |
|
|
|
return emptyList() |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
val recordIds = completedRecords.mapNotNull { it.recordId }.distinct() |
|
|
|
val lineRecords = doPickOrderLineRecordRepository.findByDoPickOrderIdInAndDeletedFalse(recordIds) |
|
|
|
val lineRecordsByRecordId = lineRecords.groupBy { it.doPickOrderId } |
|
|
|
|
|
|
|
|
|
|
|
val filteredRecords = completedRecords.filter { record -> |
|
|
|
val lines = lineRecordsByRecordId[record.recordId] ?: emptyList() |
|
|
|
|
|
|
|
|
|
|
|
val matchTargetDate = normalizedTargetDate.isNullOrBlank() || |
|
|
|
record.ticketCompleteDateTime |
|
|
|
?.format(DateTimeFormatter.ofPattern("yyyyMMdd")) |
|
|
|
?.contains(normalizedTargetDate, ignoreCase = true) == true |
|
|
|
matchTargetDate |
|
|
|
}.sortedByDescending { it.ticketCompleteDateTime } |
|
|
|
|
|
|
|
|
|
|
|
if (filteredRecords.isEmpty()) { |
|
|
|
return emptyList() |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
val allPickOrderIds = lineRecords.mapNotNull { it.pickOrderId }.distinct() |
|
|
|
val pickOrdersById = pickOrderRepository.findAllById(allPickOrderIds).associateBy { it.id!! } |
|
|
|
|
|
|
|
|
|
|
|
val allDeliveryOrderIds = lineRecords.mapNotNull { it.doOrderId }.distinct() |
|
|
|
val deliveryOrdersById = deliveryOrderRepository.findAllById(allDeliveryOrderIds).associateBy { it.id!! } |
|
|
|
|
|
|
|
|
|
|
|
filteredRecords.map { record -> |
|
|
|
val lines = lineRecordsByRecordId[record.recordId] ?: emptyList() |
|
|
|
val pickOrderIds = lines.mapNotNull { it.pickOrderId }.distinct() |
|
|
|
val pickOrderCodes = lines.mapNotNull { it.pickOrderCode }.distinct() |
|
|
|
val deliveryOrderIds = lines.mapNotNull { it.doOrderId }.distinct() |
|
|
|
val deliveryNos = lines.mapNotNull { it.deliveryOrderCode }.distinct() |
|
|
|
|
|
|
|
|
|
|
|
val numberOfCartons = pickOrderIds.sumOf { id -> |
|
|
|
pickOrdersById[id]?.pickOrderLines?.count { !it.deleted } ?: 0 |
|
|
|
} |
|
|
|
val completedDateStr = record.ticketCompleteDateTime |
|
|
|
?.format(DateTimeFormatter.ofPattern("yyyyMMdd")) |
|
|
|
?.format(DateTimeFormatter.ofPattern("yyyyMMdd")) |
|
|
|
|
|
|
|
val representativePickOrder = pickOrderIds.firstOrNull()?.let { pickOrdersById[it] } |
|
|
|
val representativeDelivery = deliveryOrderIds.firstOrNull()?.let { deliveryOrdersById[it] } |
|
|
|
@@ -4183,7 +4335,7 @@ println("DEBUG sol polIds in linesResults: " + linesResults.mapNotNull { it["sto |
|
|
|
shopName = record.shopName, |
|
|
|
truckLanceCode = record.truckLanceCode, |
|
|
|
departureTime = record.truckDepartureTime, |
|
|
|
deliveryNoteCode=record.deliveryNoteCode, |
|
|
|
deliveryNoteCode = record.deliveryNoteCode, |
|
|
|
pickOrderIds = pickOrderIds, |
|
|
|
pickOrderCodes = pickOrderCodes, |
|
|
|
deliveryOrderIds = deliveryOrderIds, |
|
|
|
@@ -4203,12 +4355,29 @@ println("DEBUG sol polIds in linesResults: " + linesResults.mapNotNull { it["sto |
|
|
|
) |
|
|
|
} |
|
|
|
} catch (e: Exception) { |
|
|
|
println("❌ Error in getCompletedDoPickOrders: ${e.message}") |
|
|
|
println("❌ Error in mapCompletedDoPickOrders: ${e.message}") |
|
|
|
e.printStackTrace() |
|
|
|
emptyList() |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
open fun getCompletedDoPickOrders( |
|
|
|
userId: Long, |
|
|
|
request: GetCompletedDoPickOrdersRequest |
|
|
|
): List<CompletedDoPickOrderResponse> { |
|
|
|
val baseRecords = doPickOrderRecordRepository |
|
|
|
.findByHandledByAndTicketStatusAndDeletedFalse(userId, DoPickOrderStatus.completed) |
|
|
|
return mapCompletedDoPickOrders(baseRecords, request) |
|
|
|
} |
|
|
|
|
|
|
|
open fun getCompletedDoPickOrdersAll( |
|
|
|
request: GetCompletedDoPickOrdersRequest |
|
|
|
): List<CompletedDoPickOrderResponse> { |
|
|
|
val baseRecords = doPickOrderRecordRepository |
|
|
|
.findByTicketStatusAndDeletedFalse(DoPickOrderStatus.completed) |
|
|
|
return mapCompletedDoPickOrders(baseRecords, request) |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private fun numToBigDecimal(n: Number?): BigDecimal { |
|
|
|
return when (n) { |
|
|
|
|