diff --git a/src/main/java/com/ffii/fpsms/modules/pickOrder/service/PickOrderService.kt b/src/main/java/com/ffii/fpsms/modules/pickOrder/service/PickOrderService.kt index 7b682bc..9146fa0 100644 --- a/src/main/java/com/ffii/fpsms/modules/pickOrder/service/PickOrderService.kt +++ b/src/main/java/com/ffii/fpsms/modules/pickOrder/service/PickOrderService.kt @@ -3729,6 +3729,7 @@ open fun getAllPickOrderLotsWithDetailsHierarchical(userId: Long): Map(jdbcDao, stockOutLineRepository) { @Throws(IOException::class) @Transactional @@ -347,29 +348,51 @@ private fun getStockOutIdFromPickOrderLine(pickOrderLineId: Long): Long { allStockOutLines.forEach { sol -> println(" StockOutLine ${sol.id}: status=${sol.status}, qty=${sol.qty}") } - + + // 计算当前行的需求数量 + val pickOrderLine = pickOrderLineRepository.findById(pickOrderLineId).orElse(null) + val requiredQty = pickOrderLine?.qty ?: BigDecimal.ZERO + + // 1) 计算所有拣货的累计数量(包括 completed / partially_completed / COMPLETE / PARTIALLY_COMPLETE) + val totalPickedQty = allStockOutLines + .filter { sol -> + val solStatus = sol.status?.trim()?.lowercase() + solStatus == "completed" || + solStatus == "partially_completed" || + solStatus == StockOutLineStatus.COMPLETE.status.lowercase() || + solStatus == StockOutLineStatus.PARTIALLY_COMPLETE.status.lowercase() + } + .fold(BigDecimal.ZERO) { acc, sol -> + val qtyValue = sol.qty ?: 0.0 + acc + BigDecimal(qtyValue.toString()) + } + + // 2) 计算该行所有相关 issue 的数量(miss / bad / expiry 等) + val totalIssueQty = try { + val issues = pickExecutionIssueRepository.findByPickOrderLineIdAndDeletedFalse(pickOrderLineId) + issues.fold(BigDecimal.ZERO) { acc, issue -> + acc + (issue.issueQty ?: BigDecimal.ZERO) + } + } catch (e: Exception) { + println("⚠️ Error fetching issues for pickOrderLineId $pickOrderLineId: ${e.message}") + BigDecimal.ZERO + } + + println(" totalPickedQty = $totalPickedQty, totalIssueQty = $totalIssueQty, requiredQty = $requiredQty") + val unfinishedLine = allStockOutLines.filter { - val status = it.status?.trim()?.lowercase() - val isComplete = status == StockOutLineStatus.COMPLETE.status.lowercase() - val isRejected = status == StockOutLineStatus.REJECTED.status.lowercase() - val isPartiallyComplete = status == StockOutLineStatus.PARTIALLY_COMPLETE.status.lowercase() + val rawStatus = it.status?.trim() + val status = rawStatus?.lowercase() + // 兼容多种写法:'COMPLETED'、'completed'、枚举值等 + val isComplete = status == "completed" || status == StockOutLineStatus.COMPLETE.status.lowercase() + val isRejected = status == "rejected" || status == StockOutLineStatus.REJECTED.status.lowercase() + val isPartiallyComplete = + status == "partially_completed" || status == StockOutLineStatus.PARTIALLY_COMPLETE.status.lowercase() - // Check if partially_completed should be considered as finished - // (if total picked qty meets required qty) + // 如果「拣货数量 + issue 数量」已经覆盖需求,就把 partially_completed 视为已完成 val shouldConsiderPartiallyCompleteAsFinished = if (isPartiallyComplete) { - // ✅ 修复:从 repository 获取 pickOrderLine,避免懒加载问题 - val pickOrderLine = pickOrderLineRepository.findById(pickOrderLineId).orElse(null) - if (pickOrderLine != null && pickOrderLine.qty != null) { - val totalPickedQty = allStockOutLines - .filter { sol -> - val solStatus = sol.status?.trim()?.lowercase() - solStatus == "completed" || solStatus == "partially_completed" - } - .fold(BigDecimal.ZERO) { acc, sol -> - val qtyValue = sol.qty ?: 0.0 - acc + BigDecimal(qtyValue.toString()) - } - totalPickedQty >= pickOrderLine.qty + if (requiredQty > BigDecimal.ZERO) { + (totalPickedQty + totalIssueQty) >= requiredQty } else { false } diff --git a/src/main/java/com/ffii/fpsms/modules/stock/service/SuggestedPickLotService.kt b/src/main/java/com/ffii/fpsms/modules/stock/service/SuggestedPickLotService.kt index c8a084b..4c1656c 100644 --- a/src/main/java/com/ffii/fpsms/modules/stock/service/SuggestedPickLotService.kt +++ b/src/main/java/com/ffii/fpsms/modules/stock/service/SuggestedPickLotService.kt @@ -118,7 +118,20 @@ open class SuggestedPickLotService( val salesUnit = line.item?.id?.let { itemUomService.findSalesUnitByItemId(it) } val lotLines = availableInventoryLotLines[line.item?.id].orEmpty() val ratio = one // (salesUnit?.ratioN ?: one).divide(salesUnit?.ratioD ?: one, 10, RoundingMode.HALF_UP) - + val pickOrder = line.pickOrder + val isDoPickOrder = pickOrder?.type?.value == "do" || pickOrder?.type?.value == "delivery_order" + + val doPreferredFloor: String? = if (isDoPickOrder) { + val supplierCode = pickOrder?.deliveryOrder?.supplier?.code + when (supplierCode) { + "P06B" -> "4F" + "P07", "P06D" -> "2F" + else -> null // 其他供应商不限定 2F/4F + } + } else { + null + } + // FIX: Calculate remaining quantity needed (not the full required quantity) val stockOutLines = stockOutLIneRepository.findAllByPickOrderLineIdAndDeletedFalse(line.id!!) val totalPickedQty = stockOutLines @@ -153,6 +166,13 @@ open class SuggestedPickLotService( if (warehouseStoreId == "3F" && isDeliveryOrderPick) { return@forEachIndexed } + if (doPreferredFloor != null && + warehouseStoreId != "1F" && + warehouseStoreId != doPreferredFloor + ) { + // 例:首选 2F,但是 lot 在 4F -> 跳过 + return@forEachIndexed + } // 修复:计算可用数量,转换为销售单位 val availableQtyInBaseUnits = calculateRemainingQtyForInfo(lotLine) val holdQtyInBaseUnits = holdQtyMap[lotLine.id] ?: zero