diff --git a/src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DeliveryOrderService.kt b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DeliveryOrderService.kt index 264f0b6..efd51fd 100644 --- a/src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DeliveryOrderService.kt +++ b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DeliveryOrderService.kt @@ -39,7 +39,7 @@ import com.ffii.fpsms.modules.master.service.PrinterService import java.time.format.DateTimeFormatter import com.ffii.fpsms.modules.pickOrder.entity.TruckRepository import java.time.LocalTime -import com.ffii.fpsms.modules.stock.service.SuggestedPickLotService + import com.ffii.fpsms.modules.stock.entity.InventoryLotLineRepository import com.ffii.fpsms.modules.stock.entity.StockOutRepository import com.ffii.fpsms.modules.stock.entity.StockOutLIneRepository @@ -69,6 +69,7 @@ import com.ffii.fpsms.modules.pickOrder.entity.Router import net.sf.jasperreports.engine.JasperPrintManager import net.sf.jasperreports.engine.JRPrintPage import com.ffii.fpsms.modules.stock.entity.SuggestPickLotRepository +import com.ffii.fpsms.modules.stock.service.SuggestedPickLotService // ✅ 添加这行 @Service open class DeliveryOrderService( diff --git a/src/main/java/com/ffii/fpsms/modules/jobOrder/service/JoPickOrderService.kt b/src/main/java/com/ffii/fpsms/modules/jobOrder/service/JoPickOrderService.kt index aad660f..9cd15ea 100644 --- a/src/main/java/com/ffii/fpsms/modules/jobOrder/service/JoPickOrderService.kt +++ b/src/main/java/com/ffii/fpsms/modules/jobOrder/service/JoPickOrderService.kt @@ -23,6 +23,8 @@ import com.ffii.fpsms.modules.pickOrder.entity.PickExecutionIssue import com.ffii.fpsms.modules.pickOrder.entity.PickExecutionIssueRepository import java.math.BigDecimal import com.ffii.fpsms.modules.pickOrder.entity.HandleStatus +import com.ffii.fpsms.modules.pickOrder.entity.IssueCategory + @Service open class JoPickOrderService( private val joPickOrderRepository: JoPickOrderRepository, @@ -1129,36 +1131,38 @@ open fun recordSecondScanIssue(request: SecondScanIssueRequest): MessageResponse // ✅ 生成 issueNo val issueNo = generateIssueNoForJo() + // ✅ Create pick execution issue with complete data // ✅ Create pick execution issue with complete data val pickExecutionIssue = PickExecutionIssue( - issueNo = issueNo, - type = request.type ?: "match", - pickOrderId = request.pickOrderId, - pickOrderCode = pickOrderCode ?: "P-${request.pickOrderId}", - pickOrderCreateDate = pickOrderCreateDate, // ✅ 现在已经定义了 - pickExecutionDate = LocalDate.now(), - pickOrderLineId = pickOrderLineId, - itemId = request.itemId, - itemCode = itemCode, - itemDescription = itemName, - lotId = lotId, - lotNo = lotNo, - storeLocation = storeLocation, - requiredQty = requiredQty, - actualPickQty = request.qty.toBigDecimal(), - missQty = request.missQty.toBigDecimal(), // ✅ 使用实际的 missQty - badItemQty = request.badItemQty.toBigDecimal(), // ✅ 使用实际的 badItemQty - issueRemark = request.reason, - pickerName = pickerName, - handleStatus = HandleStatus.pending, - handleDate = null, - handledBy = null, - created = LocalDateTime.now(), - createdBy = request.createdBy.toString(), - version = 0, - modified = LocalDateTime.now(), - modifiedBy = null, - deleted = false + id = null, // ✅ 1 + pickOrderId = request.pickOrderId, // ✅ 2 + pickOrderCode = pickOrderCode ?: "P-${request.pickOrderId}", // ✅ 3 + pickOrderCreateDate = pickOrderCreateDate, // ✅ 4 + pickExecutionDate = LocalDate.now(), // ✅ 5 + pickOrderLineId = pickOrderLineId, // ✅ 6 + issueNo = issueNo, // ✅ 7 + issueCategory = IssueCategory.match_issue, // ✅ 8 - 因为这是 JO 的 match 问题 + itemId = request.itemId, // ✅ 9 + itemCode = itemCode, // ✅ 10 + itemDescription = itemName, // ✅ 11 + lotId = lotId, // ✅ 12 + lotNo = lotNo, // ✅ 13 + storeLocation = storeLocation, // ✅ 14 + requiredQty = requiredQty, // ✅ 15 + actualPickQty = request.qty.toBigDecimal(), // ✅ 16 + missQty = request.missQty.toBigDecimal(), // ✅ 17 + badItemQty = request.badItemQty.toBigDecimal(), // ✅ 18 + issueRemark = request.reason, // ✅ 19 + pickerName = pickerName, // ✅ 20 + handleStatus = HandleStatus.pending, // ✅ 21 + handleDate = null, // ✅ 22 + handledBy = null, // ✅ 23 + created = LocalDateTime.now(), // ✅ 24 + createdBy = request.createdBy.toString(), // ✅ 25 + version = 0, // ✅ 26 + modified = LocalDateTime.now(), // ✅ 27 + modifiedBy = null, // ✅ 28 + deleted = false // ✅ 29 ) pickExecutionIssueRepository.save(pickExecutionIssue) diff --git a/src/main/java/com/ffii/fpsms/modules/pickOrder/entity/PickExecutionIssue.kt b/src/main/java/com/ffii/fpsms/modules/pickOrder/entity/PickExecutionIssue.kt index a71b4f4..b0de9bf 100644 --- a/src/main/java/com/ffii/fpsms/modules/pickOrder/entity/PickExecutionIssue.kt +++ b/src/main/java/com/ffii/fpsms/modules/pickOrder/entity/PickExecutionIssue.kt @@ -25,14 +25,19 @@ class PickExecutionIssue( @Column(name = "pick_execution_date") val pickExecutionDate: LocalDate? = null, + @Column(name = "Jo_pick_order_id") +val joPickOrderId: Long? = null, + +@Column(name = "Do_pick_order_id") +val doPickOrderId: Long? = null, @Column(name = "pick_order_line_id", nullable = false) val pickOrderLineId: Long, @Column(name = "issue_no", length = 50) val issueNo: String? = null, - // ✅ 新增字段:type - @Column(name = "type", length = 50) - val type: String? = null, + @Enumerated(EnumType.STRING) + @Column(name = "issue_category") + val issueCategory: IssueCategory = IssueCategory.lot_issue, @Column(name = "item_id", nullable = false) val itemId: Long, @@ -110,7 +115,7 @@ class PickExecutionIssue( itemDescription = null, lotId = null, issueNo = null, - type = null, + issueCategory = IssueCategory.lot_issue, lotNo = null, storeLocation = null, requiredQty = null, @@ -135,4 +140,9 @@ enum class HandleStatus { pending, sort_and_repair, dispose +} +enum class IssueCategory { + lot_issue, // 正常 Issue Button 提交 + resuggest_issue, // Resuggest 失败 + match_issue // Matching 问题 } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/pickOrder/service/PickExecutionIssueService.kt b/src/main/java/com/ffii/fpsms/modules/pickOrder/service/PickExecutionIssueService.kt index 85ecaf6..9d82ebe 100644 --- a/src/main/java/com/ffii/fpsms/modules/pickOrder/service/PickExecutionIssueService.kt +++ b/src/main/java/com/ffii/fpsms/modules/pickOrder/service/PickExecutionIssueService.kt @@ -5,6 +5,8 @@ import com.ffii.fpsms.modules.master.web.models.MessageResponse import com.ffii.fpsms.modules.pickOrder.entity.PickExecutionIssue import com.ffii.fpsms.modules.pickOrder.entity.PickExecutionIssueRepository import com.ffii.fpsms.modules.stock.entity.StockOutLine +import com.ffii.fpsms.modules.pickOrder.entity.IssueCategory // ✅ 添加这行 +import com.ffii.fpsms.modules.pickOrder.entity.HandleStatus import com.ffii.fpsms.modules.stock.entity.StockOutLIneRepository import com.ffii.fpsms.modules.stock.entity.InventoryLotLine import com.ffii.fpsms.modules.stock.entity.InventoryLotLineRepository @@ -51,7 +53,9 @@ open class PickExecutionIssueService( // 2. 创建 pick execution issue 记录 val pickExecutionIssue = PickExecutionIssue( issueNo = generateIssueNo(), - type = request.type, + issueCategory = IssueCategory.valueOf( + request.issueCategory ?: "lot_issue" + ), pickOrderId = request.pickOrderId, pickOrderCode = request.pickOrderCode, pickOrderCreateDate = request.pickOrderCreateDate, 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 da0c4ee..1e98404 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 @@ -1843,43 +1843,43 @@ open fun autoAssignAndReleasePickOrderByStoreAndTicket(storeId: String, ticketNo val pickOrderIdsStr = pickOrderIds.joinToString(",") val sql = """ - SELECT - -- Pick Order Information - po.id as pickOrderId, - po.code as pickOrderCode, + SELECT + -- Pick Order Information + po.id as pickOrderId, + po.code as pickOrderCode, po.consoCode as pickOrderConsoCode, - po.targetDate as pickOrderTargetDate, - po.type as pickOrderType, - po.status as pickOrderStatus, - po.assignTo as pickOrderAssignTo, - - -- Pick Order Line Information - pol.id as pickOrderLineId, - pol.qty as pickOrderLineRequiredQty, + po.targetDate as pickOrderTargetDate, + po.type as pickOrderType, + po.status as pickOrderStatus, + po.assignTo as pickOrderAssignTo, + + -- Pick Order Line Information + pol.id as pickOrderLineId, + pol.qty as pickOrderLineRequiredQty, pol.status as pickOrderLineStatus, - - -- Item Information - i.id as itemId, - i.code as itemCode, - i.name as itemName, - uc.code as uomCode, - uc.udfudesc as uomDesc, - - -- ✅ Calculate total picked quantity from stock_out_line table - COALESCE(( - SELECT SUM(sol_picked.qty) - FROM fpsmsdb.stock_out_line sol_picked - WHERE sol_picked.pickOrderLineId = pol.id - AND sol_picked.deleted = false - AND sol_picked.status IN ('completed', 'COMPLETE', 'partially_completed','rejected') - ), 0) as totalPickedQty, - - -- ✅ Calculate available quantity from inventory - COALESCE(( - SELECT inv.onHandQty - inv.onHoldQty - inv.unavailableQty - FROM fpsmsdb.inventory inv - WHERE inv.itemId = i.id - AND inv.deleted = false + + -- Item Information + i.id as itemId, + i.code as itemCode, + i.name as itemName, + uc.code as uomCode, + uc.udfudesc as uomDesc, + + -- ✅ Calculate total picked quantity from stock_out_line table + COALESCE(( + SELECT SUM(sol_picked.qty) + FROM fpsmsdb.stock_out_line sol_picked + WHERE sol_picked.pickOrderLineId = pol.id + AND sol_picked.deleted = false + AND sol_picked.status IN ('completed', 'COMPLETE', 'partially_completed','rejected') + ), 0) as totalPickedQty, + + -- ✅ Calculate available quantity from inventory + COALESCE(( + SELECT inv.onHandQty - inv.onHoldQty - inv.unavailableQty + FROM fpsmsdb.inventory inv + WHERE inv.itemId = i.id + AND inv.deleted = false ), 0) as availableQty, -- ✅ Check if all stock out lines for this pick order line are completed @@ -1899,15 +1899,15 @@ open fun autoAssignAndReleasePickOrderByStoreAndTicket(storeId: String, ticketNo ) THEN true ELSE false END as allLotsCompleted - - FROM fpsmsdb.pick_order po - JOIN fpsmsdb.pick_order_line pol ON pol.poId = po.id - JOIN fpsmsdb.items i ON i.id = pol.itemId - LEFT JOIN fpsmsdb.uom_conversion uc ON uc.id = pol.uomId - WHERE po.deleted = false - AND po.id IN ($pickOrderIdsStr) - AND pol.deleted = false - AND po.status = 'RELEASED' + + FROM fpsmsdb.pick_order po + JOIN fpsmsdb.pick_order_line pol ON pol.poId = po.id + JOIN fpsmsdb.items i ON i.id = pol.itemId + LEFT JOIN fpsmsdb.uom_conversion uc ON uc.id = pol.uomId + WHERE po.deleted = false + AND po.id IN ($pickOrderIdsStr) + AND pol.deleted = false + AND po.status = 'RELEASED' AND po.type NOT IN ('do', 'job') -- ✅ 排除 do 和 job 类型 -- ✅ Only include lines that have incomplete stock out lines AND ( @@ -1925,11 +1925,11 @@ open fun autoAssignAndReleasePickOrderByStoreAndTicket(storeId: String, ticketNo AND sol_incomplete.status NOT IN ('completed', 'COMPLETE') ) ) - - ORDER BY - po.code ASC, - i.code ASC - """.trimIndent() + + ORDER BY + po.code ASC, + i.code ASC + """.trimIndent() println("🔍 Executing optimized SQL: $sql") diff --git a/src/main/java/com/ffii/fpsms/modules/pickOrder/web/models/PickExecutionIssueRequest.kt b/src/main/java/com/ffii/fpsms/modules/pickOrder/web/models/PickExecutionIssueRequest.kt index c5927ff..539073e 100644 --- a/src/main/java/com/ffii/fpsms/modules/pickOrder/web/models/PickExecutionIssueRequest.kt +++ b/src/main/java/com/ffii/fpsms/modules/pickOrder/web/models/PickExecutionIssueRequest.kt @@ -16,7 +16,7 @@ data class PickExecutionIssueRequest( val lotId: Long? = null, val lotNo: String? = null, val issueNo: String? = null, - val type: String? = null, + val issueCategory: String = "lot_issue", val storeLocation: String? = null, val requiredQty: BigDecimal? = null, val actualPickQty: BigDecimal? = null, 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 51ef450..feb5383 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 @@ -2,8 +2,13 @@ package com.ffii.fpsms.modules.stock.service import com.ffii.fpsms.modules.master.service.ItemUomService import com.ffii.fpsms.modules.pickOrder.entity.PickOrder + import com.ffii.fpsms.modules.pickOrder.entity.PickOrderLine import com.ffii.fpsms.modules.pickOrder.entity.PickOrderLineRepository +import com.ffii.fpsms.modules.pickOrder.entity.PickExecutionIssue // ✅ 添加 +import com.ffii.fpsms.modules.pickOrder.entity.IssueCategory +import com.ffii.fpsms.modules.pickOrder.entity.HandleStatus // ✅ 添加 +import com.ffii.fpsms.modules.pickOrder.entity.PickExecutionIssueRepository import com.ffii.fpsms.modules.stock.entity.* import com.ffii.fpsms.modules.stock.entity.enum.InventoryLotLineStatus import com.ffii.fpsms.modules.stock.entity.projection.InventoryLotLineInfo @@ -15,6 +20,8 @@ import com.ffii.fpsms.modules.stock.web.model.SuggestedPickLotResponse import org.springframework.stereotype.Service import java.math.BigDecimal import java.time.LocalDate +import java.time.LocalDateTime // ✅ 添加 +import java.time.format.DateTimeFormatter // ✅ 添加 import kotlin.jvm.optionals.getOrDefault import kotlin.jvm.optionals.getOrNull import org.springframework.transaction.annotation.Transactional @@ -29,6 +36,7 @@ import com.ffii.fpsms.modules.stock.entity.FailInventoryLotLineRepository import com.ffii.fpsms.modules.stock.entity.StockOutRepository import com.ffii.fpsms.modules.master.entity.ItemsRepository import com.ffii.fpsms.modules.pickOrder.enums.PickOrderLineStatus +import com.ffii.fpsms.modules.stock.entity.projection.StockOutLineInfo // ✅ 添加这行 @Service open class SuggestedPickLotService( val suggestedPickLotRepository: SuggestPickLotRepository, @@ -37,6 +45,7 @@ open class SuggestedPickLotService( val pickOrderLineRepository: PickOrderLineRepository, val inventoryLotLineService: InventoryLotLineService, val itemUomService: ItemUomService, + val pickExecutionIssueRepository: PickExecutionIssueRepository, // ✅ 添加逗号 val pickOrderRepository: PickOrderRepository, val inventoryRepository: InventoryRepository, val failInventoryLotLineRepository: FailInventoryLotLineRepository, @@ -363,38 +372,49 @@ open class SuggestedPickLotService( val allSuggestions = suggestedPickLotRepository.findAllByPickOrderLineIdIn(allPickOrderLineIds) println("Found ${allSuggestions.size} existing suggestions") - // ✅ FIX: Separate suggestions to keep (those with rejected stock out lines) and delete - val suggestionsToKeep = allSuggestions.filter { suggestion -> - val pickOrderLineId = suggestion.pickOrderLine?.id - val suggestedLotLineId = suggestion.suggestedLotLine?.id - - println("Checking suggestion ${suggestion.id}: pickOrderLineId=$pickOrderLineId, suggestedLotLineId=$suggestedLotLineId") - - if (pickOrderLineId != null && suggestedLotLineId != null) { - val stockOutLines = stockOutLIneRepository.findByPickOrderLineIdAndInventoryLotLineIdAndDeletedFalse( - pickOrderLineId, - suggestedLotLineId - ) - val hasRejectedStockOutLine = stockOutLines.any { it.status == "rejected" } - println(" Stock out lines: ${stockOutLines.map { "${it.id}(status=${it.status}, qty=${it.qty})" }}") - println(" Has rejected stock out line: $hasRejectedStockOutLine") - hasRejectedStockOutLine - } else { - println(" Missing pickOrderLineId or suggestedLotLineId") - false - } - } + - val suggestionsToDelete = allSuggestions.filter { suggestion -> - !suggestionsToKeep.contains(suggestion) - } + // ✅ 删除第 376-395 行的旧代码,替换为: +// ✅ FIX: Separate suggestions to keep (those WITHOUT rejected stock out lines) and delete +val suggestionsToKeep = allSuggestions.filter { suggestion -> + val pickOrderLineId = suggestion.pickOrderLine?.id + val suggestedLotLineId = suggestion.suggestedLotLine?.id + + if (pickOrderLineId != null && suggestedLotLineId != null) { + val stockOutLines = stockOutLIneRepository.findByPickOrderLineIdAndInventoryLotLineIdAndDeletedFalse( + pickOrderLineId, + suggestedLotLineId + ) + // ✅ 保留没有 rejected stock out lines 的 suggestions + !stockOutLines.any { it.status == "rejected" } + } else { + true // 保留有问题的 suggestions 用于调试 + } +} + +// ✅ 只删除有 rejected stock out lines 的 suggestions +val suggestionsToDelete = allSuggestions.filter { suggestion -> + val pickOrderLineId = suggestion.pickOrderLine?.id + val suggestedLotLineId = suggestion.suggestedLotLine?.id + + if (pickOrderLineId != null && suggestedLotLineId != null) { + val stockOutLines = stockOutLIneRepository.findByPickOrderLineIdAndInventoryLotLineIdAndDeletedFalse( + pickOrderLineId, + suggestedLotLineId + ) + stockOutLines.any { it.status == "rejected" } // ✅ 只删除 rejected 的 + } else { + false + } +} + println("Suggestions to keep (with rejected stock out lines): ${suggestionsToKeep.size}") println("Suggestions to delete: ${suggestionsToDelete.size}") // ✅ FIX: Clear holdQty ONLY for lots that have rejected stock out lines - val rejectedLotIds = suggestionsToKeep.mapNotNull { it.suggestedLotLine?.id }.distinct() - println("Rejected lot IDs: $rejectedLotIds") + val rejectedLotIds = suggestionsToDelete.mapNotNull { it.suggestedLotLine?.id }.distinct() + println("Rejected lot IDs (clearing holdQty only): $rejectedLotIds") rejectedLotIds.forEach { lotId -> val lot = inventoryLotLineRepository.findById(lotId).orElse(null) @@ -407,29 +427,13 @@ open class SuggestedPickLotService( } // ✅ NEW: Reduce holdQty for lots that are no longer suggested - val deletedSuggestions = suggestionsToDelete - val deletedLotIds = deletedSuggestions.mapNotNull { it.suggestedLotLine?.id }.distinct() - println("Deleted lot IDs: $deletedLotIds") - - deletedLotIds.forEach { lotId -> - val lot = inventoryLotLineRepository.findById(lotId).orElse(null) - lot?.let { - val originalHoldQty = it.holdQty ?: BigDecimal.ZERO - val deletedQty = deletedSuggestions - .filter { it.suggestedLotLine?.id == lotId } - .sumOf { it.qty ?: BigDecimal.ZERO } - - val newHoldQty = originalHoldQty.minus(deletedQty) - it.holdQty = if (newHoldQty < BigDecimal.ZERO) BigDecimal.ZERO else newHoldQty - inventoryLotLineRepository.save(it) - println("Reduced holdQty for deleted lot ${lot.id}: $originalHoldQty - $deletedQty = ${it.holdQty}") - } - } - // ✅ FIX: Delete only the suggestions that should be deleted - if (suggestionsToDelete.isNotEmpty()) { - suggestedPickLotRepository.deleteAll(suggestionsToDelete) - println("Deleted ${suggestionsToDelete.size} suggestions") - } + + // ✅ 不删除任何 suggestions - 保留 rejected suggestions 用于显示 +// if (suggestionsToDelete.isNotEmpty()) { +// suggestedPickLotRepository.deleteAll(suggestionsToDelete) +// println("Deleted ${suggestionsToDelete.size} suggestions") +// } +println("Keeping all suggestions (including rejected ones for display)") // ✅ NEW: Build holdQtyMap with existing holdQty from other pick orders val existingHoldQtyMap = mutableMapOf() @@ -462,61 +466,102 @@ open class SuggestedPickLotService( println("Final existing holdQtyMap: $existingHoldQtyMap") // ✅ FIX: Create new suggestions for all pick orders to resuggest -allPickOrdersToResuggest.forEach { pickOrderToResuggest -> - println("=== Creating new suggestions for pick order: ${pickOrderToResuggest.code} ===") - - val request = SuggestedPickLotForPolRequest( - pickOrderLines = pickOrderToResuggest.pickOrderLines, - holdQtyMap = existingHoldQtyMap.toMutableMap() // ✅ Use existing holdQty - ) - val response = suggestionForPickOrderLines(request) - - println("Generated ${response.suggestedList.size} new suggestions") - response.suggestedList.forEach { suggestion -> - println(" - Suggestion: lotId=${suggestion.suggestedLotLine?.id}, qty=${suggestion.qty}") + allPickOrdersToResuggest.forEach { pickOrderToResuggest -> + // ✅ 只获取有 rejected stock out lines 的 pick order lines + val problematicPickOrderLines = pickOrderToResuggest.pickOrderLines.filter { pol -> + val stockOutLines = stockOutLIneRepository.findAllByPickOrderLineIdAndDeletedFalse(pol.id!!) + stockOutLines.any { it.status == "rejected" } } - - // ✅ FIX: Save the suggestions to the database - if (response.suggestedList.isNotEmpty()) { - val savedSuggestions = suggestedPickLotRepository.saveAllAndFlush(response.suggestedList) - println("Saved ${savedSuggestions.size} new suggestions for pick order: ${pickOrderToResuggest.code}") - savedSuggestions.forEach { suggestion -> - if (suggestion.suggestedLotLine != null) { - val stockOutLine = createStockOutLineForSuggestion(suggestion, pickOrderToResuggest) - if (stockOutLine != null) { - println("✅ Created stock out line ${stockOutLine.id} for suggestion ${suggestion.id}") + + if (problematicPickOrderLines.isNotEmpty()) { + println("=== Creating new suggestions for pick order: ${pickOrderToResuggest.code} ===") + + // ✅ 调用 suggestionForPickOrderLines 生成新的 suggestions + val request = SuggestedPickLotForPolRequest( + pickOrderLines = problematicPickOrderLines, + holdQtyMap = existingHoldQtyMap.toMutableMap() + ) + val response = suggestionForPickOrderLines(request) + + println("Generated ${response.suggestedList.size} new suggestions") + response.suggestedList.forEach { suggestion -> + println(" - Suggestion: lotId=${suggestion.suggestedLotLine?.id}, qty=${suggestion.qty}") + } + + if (response.suggestedList.isNotEmpty()) { + println("Saving ${response.suggestedList.size} new suggestions") + + // ✅ 保存所有新生成的 suggestions + val savedSuggestions = suggestedPickLotRepository.saveAllAndFlush(response.suggestedList) + println("Saved ${savedSuggestions.size} new suggestions") + + // ✅ 为每个新 suggestion 创建 stock out line 或 issue + savedSuggestions.forEach { suggestion -> + if (suggestion.suggestedLotLine != null) { + val stockOutLine = createStockOutLineForSuggestion(suggestion, pickOrderToResuggest) + if (stockOutLine != null) { + println("✅ Created stock out line ${stockOutLine.id} for suggestion ${suggestion.id}") + } } else { - println("❌ Failed to create stock out line for suggestion ${suggestion.id}") + // ✅ 如果 lot 是 null,表示没有可用的 lot,创建 resuggest_issue + println("❌ No available lot for pick order line ${suggestion.pickOrderLine?.id}, creating resuggest_issue") + + val pickOrderLine = suggestion.pickOrderLine + if (pickOrderLine != null) { + // 获取这个 line 的 rejected stock out lines + val rejectedStockOutLines = stockOutLIneRepository + .findAllByPickOrderLineIdAndDeletedFalse(pickOrderLine.id!!) + .filter { it.status == "rejected" } + + rejectedStockOutLines.forEach { rejectedLine -> + createResuggestFailureIssue( + pickOrder = pickOrderToResuggest, + pickOrderLine = pickOrderLine, + rejectedStockOutLine = rejectedLine + ) + } + } } } - } - // ✅ FIX: Update holdQty for the lots that were suggested - CUMULATIVE - response.holdQtyMap.forEach { (lotId, newHoldQty) -> - if (lotId != null && newHoldQty != null && newHoldQty > BigDecimal.ZERO) { + + // ✅ 更新 holdQty + response.holdQtyMap.forEach { (lotId, newHoldQty) -> + if (lotId != null && newHoldQty != null && newHoldQty > BigDecimal.ZERO) { val lot = inventoryLotLineRepository.findById(lotId).orElse(null) lot?.let { - val currentHoldQty = it.holdQty ?: BigDecimal.ZERO - val existingHoldQty = existingHoldQtyMap[lotId] ?: BigDecimal.ZERO - - // ✅ FIX: Calculate the additional holdQty needed - val additionalHoldQty = newHoldQty.minus(existingHoldQty) - val finalHoldQty = currentHoldQty.plus(additionalHoldQty) - - it.holdQty = finalHoldQty - inventoryLotLineRepository.save(it) - - // ✅ Update the existingHoldQtyMap for next iteration - existingHoldQtyMap[lotId] = newHoldQty - - println("Updated holdQty for lot $lotId: $currentHoldQty + $additionalHoldQty = $finalHoldQty") + val currentHoldQty = it.holdQty ?: BigDecimal.ZERO + val existingHoldQty = existingHoldQtyMap[lotId] ?: BigDecimal.ZERO + val additionalHoldQty = newHoldQty.minus(existingHoldQty) + val finalHoldQty = currentHoldQty.plus(additionalHoldQty) + it.holdQty = finalHoldQty + inventoryLotLineRepository.save(it) + existingHoldQtyMap[lotId] = newHoldQty + println("Updated holdQty for lot $lotId: $currentHoldQty + $additionalHoldQty = $finalHoldQty") + } + } + } + } else { + // ✅ 如果完全没有生成任何 suggestions + println("No suggestions generated at all for pick order: ${pickOrderToResuggest.code}") + + problematicPickOrderLines.forEach { pickOrderLine -> + val rejectedStockOutLines = stockOutLIneRepository + .findAllByPickOrderLineIdAndDeletedFalse(pickOrderLine.id!!) + .filter { it.status == "rejected" } + + if (rejectedStockOutLines.isNotEmpty()) { + rejectedStockOutLines.forEach { rejectedLine -> + createResuggestFailureIssue( + pickOrder = pickOrderToResuggest, + pickOrderLine = pickOrderLine, + rejectedStockOutLine = rejectedLine + ) + } + } + } + } } } - } - } else { - println("No suggestions generated for pick order: ${pickOrderToResuggest.code}") - } -} - // ✅ FIX: Update inventory table for each pick order allPickOrdersToResuggest.forEach { pickOrderToUpdate -> println("=== Updating inventory table for pick order: ${pickOrderToUpdate.code} ===") @@ -546,6 +591,88 @@ allPickOrdersToResuggest.forEach { pickOrderToResuggest -> ) } } + + + private fun createResuggestFailureIssue( + pickOrder: PickOrder, + pickOrderLine: PickOrderLine, + rejectedStockOutLine: StockOutLineInfo // ✅ 使用 StockOutLineInfo + ) { + try { + val item = pickOrderLine.item + + // ✅ 从 StockOutLineInfo 获取 inventoryLotLineId + val inventoryLotLineId = rejectedStockOutLine.inventoryLotLineId + val inventoryLotLine = if (inventoryLotLineId != null) { + inventoryLotLineRepository.findById(inventoryLotLineId).orElse(null) + } else { + null + } + + val issue = PickExecutionIssue( + id = null, + pickOrderId = pickOrder.id!!, + pickOrderCode = pickOrder.code!!, + pickOrderCreateDate = pickOrder.created?.toLocalDate(), + pickExecutionDate = LocalDate.now(), + pickOrderLineId = pickOrderLine.id!!, + issueNo = generateIssueNo(), + joPickOrderId=pickOrder.jobOrder?.id, + doPickOrderId=pickOrder.deliveryOrder?.id, + issueCategory = IssueCategory.resuggest_issue, + itemId = item?.id!!, + itemCode = item.code, + itemDescription = item.name, + lotId = inventoryLotLine?.id, + lotNo = inventoryLotLine?.inventoryLot?.lotNo, + storeLocation = inventoryLotLine?.warehouse?.name, + requiredQty = pickOrderLine.qty, + actualPickQty = rejectedStockOutLine.qty ?: BigDecimal.ZERO, // ✅ 直接使用,不需要 toBigDecimal() + missQty = (pickOrderLine.qty ?: BigDecimal.ZERO).minus(rejectedStockOutLine.qty ?: BigDecimal.ZERO), // ✅ 直接使用 + badItemQty = BigDecimal.ZERO, + issueRemark = "Resuggest failed: No alternative lots available for rejected lot ${inventoryLotLine?.inventoryLot?.lotNo}", + pickerName = null, + handleStatus = HandleStatus.pending, + handleDate = null, + handledBy = null, + created = LocalDateTime.now(), + createdBy = "system", + version = 0, + modified = LocalDateTime.now(), + modifiedBy = "system", + deleted = false + ) + + pickExecutionIssueRepository.save(issue) + println("✅ Created resuggest_issue: ${issue.issueNo} for pick order ${pickOrder.code}") + + } catch (e: Exception) { + println("❌ Error creating resuggest_issue: ${e.message}") + e.printStackTrace() + } + } + private fun generateIssueNo(): String { + val now = LocalDateTime.now() + val yearMonth = now.format(DateTimeFormatter.ofPattern("yyMM")) + + // 查询当月最新的 issueNo + val latestIssueNo = pickExecutionIssueRepository.findLatestIssueNoByYearMonth(yearMonth) + + // 计算下一个序列号 + val nextSequence = if (latestIssueNo != null) { + val parts = latestIssueNo.split("-") + if (parts.size == 3) { + val currentSequence = parts[2].toIntOrNull() ?: 0 + currentSequence + 1 + } else { + 1 + } + } else { + 1 + } + + return "SKO-${yearMonth}-${nextSequence.toString().padStart(3, '0')}" + } private fun findAllSuggestionsForPickOrders(pickOrderIds: List): List { val allPickOrderLines = mutableListOf() diff --git a/src/main/resources/db/changelog/changes/20251009_02_enson/03_altertable_enson.sql b/src/main/resources/db/changelog/changes/20251009_02_enson/03_altertable_enson.sql new file mode 100644 index 0000000..fcccfa8 --- /dev/null +++ b/src/main/resources/db/changelog/changes/20251009_02_enson/03_altertable_enson.sql @@ -0,0 +1,19 @@ +--liquibase formatted sql + +--changeset [enson]:update_pick_execution_issue_add_columns_and_modify_status + +-- 1. 添加新列 issueNo +ALTER TABLE pick_execution_issue +ADD COLUMN issue_category ENUM( + 'lot_issue', + 'resuggest_issue', + 'match_issue' + +) NULL DEFAULT 'lot_issue' AFTER type; + +ALTER TABLE pick_execution_issue +ADD COLUMN Do_pick_order_id INT NULL AFTER issue_category, +ADD COLUMN Jo_pick_order_id INT NULL AFTER Do_pick_order_id; + +ALTER TABLE pick_execution_issue +DROP COLUMN type;