| @@ -366,6 +366,91 @@ open class DeliveryOrderService( | |||
| } | |||
| } | |||
| /** | |||
| * 僅回傳依店鋪/ETA 從 truck 排程**推算後** `truckLanceCode` 為 null 或空白的送貨單(畫面上對應「車線-X」)。 | |||
| * 與 [searchDoLiteByPage] 帶一般車線關鍵字分開,避免 `車線-X` 在 truck 表無 shopId 時走舊邏輯漏單。 | |||
| */ | |||
| open fun searchDoLiteUnassignedTruckByPage( | |||
| code: String?, | |||
| shopName: String?, | |||
| status: String?, | |||
| estimatedArrivalDate: LocalDateTime?, | |||
| pageNum: Int?, | |||
| pageSize: Int?, | |||
| ): RecordsRes<DeliveryOrderInfoLiteDto> { | |||
| val page = (pageNum ?: 1) - 1 | |||
| val size = pageSize ?: 10 | |||
| val statusEnum = status?.let { s -> DeliveryOrderStatus.entries.find { it.value == s } } | |||
| val etaStart = estimatedArrivalDate | |||
| val etaEnd = estimatedArrivalDate?.plusDays(1) | |||
| val allowedSupplierCodes = listOf("P06B", "P07", "P06D") | |||
| val allResult = deliveryOrderRepository.searchDoLitePageWithSupplierCodes( | |||
| code = code?.ifBlank { null }, | |||
| shopName = shopName?.ifBlank { null }, | |||
| status = statusEnum, | |||
| etaStart = etaStart, | |||
| etaEnd = etaEnd, | |||
| allowedSupplierCodes = allowedSupplierCodes, | |||
| pageable = PageRequest.of(0, 100_000), | |||
| ) | |||
| val deliveryOrderIds = allResult.content.mapNotNull { it.id } | |||
| val deliveryOrdersMap = deliveryOrderRepository.findAllById(deliveryOrderIds).associateBy { it.id } | |||
| val processedRecords = allResult.content.map { info -> | |||
| val deliveryOrder = deliveryOrdersMap[info.id] | |||
| val supplierCode = deliveryOrder?.supplier?.code | |||
| val preferredFloor = when (supplierCode) { | |||
| "P06B" -> "4F" | |||
| "P07", "P06D" -> "2F" | |||
| else -> "2F" | |||
| } | |||
| val shop = deliveryOrder?.shop | |||
| val shopId = shop?.id | |||
| val infoEta = info.estimatedArrivalDate | |||
| val calculatedTruckLanceCode = | |||
| if (deliveryOrder != null && shopId != null && infoEta != null) { | |||
| val targetDate = infoEta.toLocalDate() | |||
| val dayAbbr = getDayOfWeekAbbr(targetDate) | |||
| val trucks = truckRepository.findByShopIdAndStoreIdAndDayOfWeek(shopId, preferredFloor, dayAbbr) | |||
| val matchedTruck = if (trucks.isEmpty()) { | |||
| truckRepository.findByShopIdAndDeletedFalse(shopId) | |||
| .filter { it.storeId == preferredFloor } | |||
| .minByOrNull { it.departureTime ?: LocalTime.of(23, 59, 59) } | |||
| } else { | |||
| trucks.minByOrNull { it.departureTime ?: LocalTime.of(23, 59, 59) } | |||
| } | |||
| matchedTruck?.truckLanceCode | |||
| } else { | |||
| null | |||
| } | |||
| DeliveryOrderInfoLiteDto( | |||
| id = info.id, | |||
| code = info.code, | |||
| orderDate = info.orderDate, | |||
| estimatedArrivalDate = info.estimatedArrivalDate, | |||
| status = info.status, | |||
| shopName = info.shopName, | |||
| supplierName = info.supplierName, | |||
| shopAddress = info.shopAddress, | |||
| truckLanceCode = calculatedTruckLanceCode, | |||
| ) | |||
| }.filter { dto -> dto.truckLanceCode.isNullOrBlank() } | |||
| val totalCount = processedRecords.size | |||
| val startIndex = page * size | |||
| val endIndex = minOf(startIndex + size, processedRecords.size) | |||
| val paginatedRecords = if (startIndex < processedRecords.size) { | |||
| processedRecords.subList(startIndex, endIndex) | |||
| } else { | |||
| emptyList() | |||
| } | |||
| return RecordsRes(paginatedRecords, totalCount) | |||
| } | |||
| open fun findByM18DataLogId(m18DataLogId: Long): DeliveryOrder? { | |||
| @@ -472,8 +472,8 @@ class DoReleaseCoordinatorService( | |||
| """.trimIndent() | |||
| println(" DEBUG: SQL length: ${sql.length} characters") | |||
| println(" DEBUG: SQL first 500 chars: ${sql.take(500)}") | |||
| // println(" DEBUG: SQL length: ${sql.length} characters") | |||
| // println(" DEBUG: SQL first 500 chars: ${sql.take(500)}") | |||
| val results = jdbcDao.queryForList(sql) | |||
| println(" DEBUG: Results type: ${results.javaClass.name}") | |||
| @@ -68,6 +68,22 @@ class DeliveryOrderController( | |||
| truckLanceCode = request.truckLanceCode | |||
| ) | |||
| } | |||
| /** | |||
| * 僅回傳推算車線為 null/空白之送貨單(對應 UI 車線-X)。請求體與 search-do-lite 相同,**忽略** [SearchDeliveryOrderInfoRequest.truckLanceCode]。 | |||
| */ | |||
| @PostMapping("/search-do-lite-unassigned-truck") | |||
| fun searchDoLiteUnassignedTruck(@RequestBody request: SearchDeliveryOrderInfoRequest): RecordsRes<DeliveryOrderInfoLiteDto> { | |||
| return deliveryOrderService.searchDoLiteUnassignedTruckByPage( | |||
| code = request.code, | |||
| shopName = request.shopName, | |||
| status = request.status, | |||
| estimatedArrivalDate = request.estimatedArrivalDate, | |||
| pageNum = request.pageNum, | |||
| pageSize = request.pageSize, | |||
| ) | |||
| } | |||
| @GetMapping("/list") | |||
| fun getDoList(): List<DeliveryOrderInfo> { | |||
| return deliveryOrderService.getDoList(); | |||
| @@ -4385,7 +4385,7 @@ open fun getAllPickOrderLotsWithDetailsHierarchical(userId: Long): Map<String, A | |||
| val newIll = resolveNewInventoryLotLine(req, polItemId) | |||
| ?: return MessageResponse( | |||
| id = null, name = "New lot line not found", code = "ERROR", type = "pickorder", | |||
| message = "Cannot resolve new inventory lot line", errorPosition = null | |||
| message = "This lot is not yet putaway", errorPosition = null | |||
| ) | |||
| val targetUnavailable = newIll.status == InventoryLotLineStatus.UNAVAILABLE | |||
| @@ -92,8 +92,11 @@ open class InventoryLotLineService( | |||
| outQty: BigDecimal?, | |||
| holdQty: BigDecimal? | |||
| ): InventoryLotLineStatus { | |||
| val remainingQty = | |||
| (inQty ?: BigDecimal.ZERO) - (outQty ?: BigDecimal.ZERO) - (holdQty ?: BigDecimal.ZERO) | |||
| // val remainingQty = | |||
| // (inQty ?: BigDecimal.ZERO) - (outQty ?: BigDecimal.ZERO) - (holdQty ?: BigDecimal.ZERO) | |||
| val remainingQty = | |||
| (inQty ?: BigDecimal.ZERO) - (outQty ?: BigDecimal.ZERO) | |||
| val status = previousStatus | |||
| val qtyStatus = | |||
| if (remainingQty > BigDecimal.ZERO) InventoryLotLineStatus.AVAILABLE else InventoryLotLineStatus.UNAVAILABLE | |||
| @@ -328,7 +328,7 @@ private fun getStockOutIdFromPickOrderLine(pickOrderLineId: Long): Long { | |||
| """.trimIndent() | |||
| val result = jdbcDao.queryForList(sql, mapOf("pickOrderLineId" to pickOrderLineId)) | |||
| println("SQL result: $result") | |||
| // println("SQL result: $result") | |||
| if (result.isEmpty()) { | |||
| throw IllegalArgumentException("No StockOut found for pickOrderLineId: $pickOrderLineId. Check if pick order line exists and has associated stock out.") | |||
| @@ -338,7 +338,7 @@ private fun getStockOutIdFromPickOrderLine(pickOrderLineId: Long): Long { | |||
| val consoPickOrderCode = result[0]["consoPickOrderCode"] as? String | |||
| val consoCode = result[0]["consoCode"] as? String | |||
| println("Found stockOutId: $stockOutId, consoPickOrderCode: $consoPickOrderCode, consoCode: $consoCode") | |||
| //println("Found stockOutId: $stockOutId, consoPickOrderCode: $consoPickOrderCode, consoCode: $consoCode") | |||
| if (stockOutId == null) { | |||
| throw IllegalArgumentException("StockOut ID is null for pickOrderLineId: $pickOrderLineId. ConsoCode: $consoCode, ConsoPickOrderCode: $consoPickOrderCode") | |||
| @@ -382,11 +382,7 @@ private fun getStockOutIdFromPickOrderLine(pickOrderLineId: Long): Long { | |||
| val allStockOutLines = stockOutLineRepository | |||
| .findAllByPickOrderLineIdAndDeletedFalse(pickOrderLineId) | |||
| println("=== checkIsStockOutLineCompleted for pickOrderLineId: $pickOrderLineId ===") | |||
| println("Total stock out lines: ${allStockOutLines.size}") | |||
| allStockOutLines.forEach { sol -> | |||
| println(" StockOutLine ${sol.id}: status=${sol.status}, qty=${sol.qty}") | |||
| } | |||
| // 计算当前行的需求数量 | |||
| val pickOrderLine = pickOrderLineRepository.findById(pickOrderLineId).orElse(null) | |||
| @@ -413,11 +409,11 @@ private fun getStockOutIdFromPickOrderLine(pickOrderLineId: Long): Long { | |||
| acc + (issue.issueQty ?: BigDecimal.ZERO) | |||
| } | |||
| } catch (e: Exception) { | |||
| println("⚠️ Error fetching issues for pickOrderLineId $pickOrderLineId: ${e.message}") | |||
| println(" Error fetching issues for pickOrderLineId $pickOrderLineId: ${e.message}") | |||
| BigDecimal.ZERO | |||
| } | |||
| println(" totalPickedQty = $totalPickedQty, totalIssueQty = $totalIssueQty, requiredQty = $requiredQty") | |||
| // println(" totalPickedQty = $totalPickedQty, totalIssueQty = $totalIssueQty, requiredQty = $requiredQty") | |||
| val unfinishedLine = allStockOutLines.filter { | |||
| val rawStatus = it.status?.trim() | |||