@@ -67,6 +67,7 @@ open class PickExecutionIssueService(
@Transactional(rollbackFor = [Exception::class])
open fun recordPickExecutionIssue(request: PickExecutionIssueRequest): MessageResponse {
try {
// 1. 检查是否已经存在相同的 pick execution issue 记录
val existingIssues = pickExecutionIssueRepository.findByPickOrderLineIdAndLotIdAndDeletedFalse(
request.pickOrderLineId,
@@ -184,14 +185,31 @@ open class PickExecutionIssueService(
// 6. NEW: Update inventory_lot_line.issueQty
if (request.lotId != null && inventoryLotLine != null) {
val currentIssueQty = inventoryLotLine.issueQty ?: BigDecimal.ZERO
val newIssueQty = currentIssueQty.add(issueQty)
inventoryLotLine.issueQty = newIssueQty
inventoryLotLine.modified = LocalDateTime.now()
inventoryLotLine.modifiedBy = "system"
inventoryLotLineRepository.saveAndFlush(inventoryLotLine)
println("Updated inventory_lot_line ${request.lotId} issueQty: ${currentIssueQty} -> ${newIssueQty}")
// ✅ 修改:如果只有 missQty,不更新 issueQty
val actualPickQty = request.actualPickQty ?: BigDecimal.ZERO
val missQty = request.missQty ?: BigDecimal.ZERO
val badItemQty = request.badItemQty ?: BigDecimal.ZERO
val isMissItemOnly = actualPickQty == BigDecimal.ZERO
&& missQty > BigDecimal.ZERO
&& badItemQty == BigDecimal.ZERO
val hasMissItemWithPartialPick = missQty > BigDecimal.ZERO
&& actualPickQty > BigDecimal.ZERO
if (!isMissItemOnly && !hasMissItemWithPartialPick) {
// 只有非 miss item 的情况才更新 issueQty
val currentIssueQty = inventoryLotLine.issueQty ?: BigDecimal.ZERO
val newIssueQty = currentIssueQty.add(issueQty)
inventoryLotLine.issueQty = newIssueQty
inventoryLotLine.modified = LocalDateTime.now()
inventoryLotLine.modifiedBy = "system"
inventoryLotLineRepository.saveAndFlush(inventoryLotLine)
println("Updated inventory_lot_line ${request.lotId} issueQty: ${currentIssueQty} -> ${newIssueQty}")
} else {
println("Skipped updating issueQty for miss item (lot ${request.lotId})")
}
}
// 7. 获取相关数据用于后续处理
val actualPickQtyForProcessing = request.actualPickQty ?: BigDecimal.ZERO
@@ -446,7 +464,7 @@ private fun checkAndCompletePickOrder(consoCode: String) {
// 修复:处理有部分拣货但有 miss item 的情况
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = [Exception::class])
private fun handleMissItemWithPartialPick(request: PickExecutionIssueRequest, actualPickQty: BigDecimal, missQty: BigDecimal) {
println("=== HANDLING MISS ITEM WITH PARTIAL PICK (FIXED LOGIC ) ===")
println("=== HANDLING MISS ITEM WITH PARTIAL PICK (NEW LOGIC: DON'T REJECT LOT ) ===")
println("Actual Pick Qty: ${actualPickQty}")
println("Miss Qty: ${missQty}")
@@ -455,75 +473,73 @@ private fun handleMissItemWithPartialPick(request: PickExecutionIssueRequest, ac
val inventoryLotLine = inventoryLotLineRepository.findById(lotId).orElse(null)
if (inventoryLotLine != null) {
// 修复1:只处理已拣货的部分:更新 out Qty
// ✅ 修改:更新 outQty 为 actualPick Qty
val currentOutQty = inventoryLotLine.outQty ?: BigDecimal.ZERO
val newOutQty = currentOutQty.add(actualPickQty)
inventoryLotLine.outQty = newOutQty
// 修复2:Miss item 不减少 inQty,而是标记为 unavailable
// 因为 miss item 意味着这些物品实际上不存在或找不到
// 所以应该标记整个批次为 unavailable,而不是减少 inQty
// ✅ 修改:释放 holdQty(减少 requiredQty)
val currentHoldQty = inventoryLotLine.holdQty ?: BigDecimal.ZERO
val requiredQty = request.requiredQty ?: BigDecimal.ZERO
val newHoldQty = (currentHoldQty - requiredQty).coerceAtLeast(BigDecimal.ZERO)
inventoryLotLine.holdQty = newHoldQty
// 修复3:如果 missQty > 0,标记批次为 unavailable
if (missQty > BigDecimal.ZERO) {
inventoryLotLine.status = InventoryLotLineStatus.UNAVAILABLE
}
// ✅ 修改:不设置 status 为 UNAVAILABLE
// if (missQty > BigDecimal.ZERO) {
// inventoryLotLine.status = InventoryLotLineStatus.UNAVAILABLE
// }
inventoryLotLine.modified = LocalDateTime.now()
inventoryLotLine.modifiedBy = "system"
inventoryLotLineRepository.saveAndFlush(inventoryLotLine) // 使用 saveAndFlush
inventoryLotLineRepository.saveAndFlush(inventoryLotLine)
println("Miss item with partial pick: Updated lot ${lotId}")
println(" - Added to outQty: ${actualPickQty} (${currentOutQty} -> ${newOutQty})")
println(" - Set status to UNAVAILABLE due to missQty: ${missQty}")
println(" - Updated outQty: ${currentOutQty} -> ${newOutQty} (actualPickQty: ${actualPickQty})")
println(" - Released holdQty: ${currentHoldQty} -> ${newHoldQty} (released: ${requiredQty})")
println(" - Did NOT set status to UNAVAILABLE")
}
// 修复4:更新 inventory 表的 unavailableQty
// 对于 miss item,应该将 missQty 计入 unavailableQty
updateInventoryUnavailableQty(itemId, missQty)
// ✅ 修改:不更新 unavailableQty(因为不 reject lot)
// updateInventoryUnavailableQty(itemId, missQty) // 删除这行
// 修复5:更新 stock_out_line 状态为 rejected(因为还有 miss item)
// 修复:同时创建 stock_ledger 记录
// ✅ 修改:不 reject stock_out_line,根据 actualPickQty 设置状态
val stockOutLines = stockOutLineRepository.findByPickOrderLineIdAndInventoryLotLineIdAndDeletedFalse(
request.pickOrderLineId,
request.lotId ?: 0L
)
stockOutLines.forEach { stockOutLine ->
stockOutLine.status = "rejected"
val requiredQty = request.requiredQty?.toDouble() ?: 0.0
val actualPickQtyDouble = actualPickQty.toDouble()
// 更新 qty 为 actualPickQty
if (request.actualPickQty != null) {
stockOutLine.qty = request.actualPickQty.toDouble()
println("Updated stock out line ${stockOutLine.id} qty to: ${request.actualPickQty}")
// 设置状态:如果 actualPickQty >= requiredQty,则为 completed,否则为 partially_completed
val newStatus = if (actualPickQtyDouble >= requiredQty) {
"completed"
} else {
"partially_completed"
}
stockOutLine.status = newStatus
stockOutLine.qty = actualPickQtyDouble
stockOutLine.modified = LocalDateTime.now()
stockOutLine.modifiedBy = "system"
val savedStockOutLine = stockOutLineRepository.saveAndFlush(stockOutLine)
println("Updated stock out line ${stockOutLine.id} status to: rejected")
println("Updated stock out line ${stockOutLine.id} status to: ${newStatus} (NOT rejected) ")
// 修复:为实际拣货的部分创建 stock_ledger 记录(即使状态是 rejected)
// 因为这部分确实从库存中出库了
if (request.actualPickQty != null && request.actualPickQty > BigDecimal.ZERO) {
createStockLedgerForStockOut(savedStockOutLine, "Nor") // 实际拣货的部分
}
// 创建 stock_ledger 记录
createStockLedgerForStockOut(savedStockOutLine, "Nor")
}
// 重新建议拣货批次(针对 miss 的数量)
try {
resuggestPickOrder(request.pickOrderId)
println("Resuggested pick order for miss qty: ${missQty}")
} catch (e: Exception) {
println("Error during resuggest in handleMissItemWithPartialPick: ${e.message}")
}
// ✅ 修改:不重新建议拣货批次(因为 lot 仍然可用)
// resuggestPickOrder(request.pickOrderId) // 删除这行
println("Miss item with partial pick: Did NOT resuggest pick order (lot remains available)")
}
// 修复:Miss item 处理逻辑
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = [Exception::class])
private fun handleMissItemOnly(request: PickExecutionIssueRequest, missQty: BigDecimal) {
println("=== HANDLING MISS ITEM ONLY (FIXED LOGIC ) ===")
println("=== HANDLING MISS ITEM ONLY (NEW LOGIC: DON'T REJECT LOT ) ===")
println("Miss Qty: ${missQty}")
val lotId = request.lotId ?: return
@@ -531,41 +547,69 @@ private fun handleMissItemOnly(request: PickExecutionIssueRequest, missQty: BigD
val inventoryLotLine = inventoryLotLineRepository.findById(lotId).orElse(null)
if (inventoryLotLine != null) {
val currentInQty = inventoryLotLine.inQty ?: BigDecimal.ZERO
val currentOutQty = inventoryLotLine.outQty ?: BigDecimal.ZERO
val remainingQty = currentInQty.minus(currentOutQty)
// ✅ 修改:不设置 status 为 UNAVAILABLE
// ✅ 修改:释放 holdQty(减少 holdQty)
val currentHoldQty = inventoryLotLine.holdQty ?: BigDecimal.ZERO
val requiredQty = request.requiredQty ?: BigDecimal.ZERO
// 释放 holdQty:减少 requiredQty(因为已经处理完了)
val newHoldQty = (currentHoldQty - requiredQty).coerceAtLeast(BigDecimal.ZERO)
inventoryLotLine.holdQty = newHoldQty
// ✅ 修改:如果有 actualPickQty,更新 outQty
val actualPickQty = request.actualPickQty ?: BigDecimal.ZERO
if (actualPickQty > BigDecimal.ZERO) {
val currentOutQty = inventoryLotLine.outQty ?: BigDecimal.ZERO
val newOutQty = currentOutQty.add(actualPickQty)
inventoryLotLine.outQty = newOutQty
println(" - Updated outQty: ${currentOutQty} -> ${newOutQty} (actualPickQty: ${actualPickQty})")
}
inventoryLotLine.status = InventoryLotLineStatus.UNAVAILABLE
inventoryLotLine.modified = LocalDateTime.now()
inventoryLotLine.modifiedBy = "system"
inventoryLotLineRepository.save(inventoryLotLine)
inventoryLotLineRepository.saveAndFlush (inventoryLotLine)
println("Miss item only: Set lot ${lotId} status to UNAVAILABLE")
println(" - Remaining qty: ${remainingQty}")
println(" - Miss qty (user input): ${missQty}")
println(" - Unavailable qty (should be remaining qty): ${remainingQty}")
println("Miss item only: Updated lot ${lotId}")
println(" - Released holdQty: ${currentHoldQty} -> ${newHoldQty} (released: ${requiredQty})")
println(" - Did NOT set status to UNAVAILABLE")
}
val currentInQty = inventoryLotLine?.inQty ?: BigDecimal.ZERO
val currentOutQty = inventoryLotLine?.outQty ?: BigDecimal.ZERO
val remainingQty = currentInQty.minus(currentOutQty)
updateInventoryUnavailableQty(itemId, remainingQty)
// ✅ 修改:不更新 unavailableQty(因为不 reject lot)
// updateInventoryUnavailableQty(itemId, remainingQty) // 删除这行
updateStockOutLineStatus(request, "rejected")
// ✅ 修改:不 reject stock_out_line,根据 actualPickQty 设置状态
val stockOutLines = stockOutLineRepository.findByPickOrderLineIdAndInventoryLotLineIdAndDeletedFalse(
request.pickOrderLineId,
request.lotId ?: 0L
)
// 重新建议拣货批次
try {
resuggestPickOrder(request.pickOrderId)
println("Resuggested pick order to find alternative lots for missing qty: ${missQty}")
} catch (e: Exception) {
println("Error during resuggest in handleMissItemOnly: ${e.message}")
stockOutLines.forEach { stockOutLine ->
val requiredQty = request.requiredQty?.toDouble() ?: 0.0
val actualPickQtyDouble = request.actualPickQty?.toDouble() ?: 0.0
// 设置状态:如果 actualPickQty >= requiredQty,则为 completed,否则为 partially_completed
val newStatus = if (actualPickQtyDouble >= requiredQty) {
"completed"
} else {
"partially_completed"
}
stockOutLine.status = newStatus
stockOutLine.qty = actualPickQtyDouble
stockOutLine.modified = LocalDateTime.now()
stockOutLine.modifiedBy = "system"
val savedStockOutLine = stockOutLineRepository.saveAndFlush(stockOutLine)
println("Updated stock out line ${stockOutLine.id} status to: ${newStatus} (NOT rejected)")
// 创建 stock_ledger 记录(如果有 actualPickQty)
if (actualPickQtyDouble > 0) {
createStockLedgerForStockOut(savedStockOutLine, "Nor")
}
}
// ✅ 修改:不重新建议拣货批次(因为 lot 仍然可用)
// resuggestPickOrder(request.pickOrderId) // 删除这行
println("Miss item: Did NOT resuggest pick order (lot remains available)")
}
// 修复:Bad item 处理逻辑
@@ -729,6 +773,18 @@ private fun handleBothMissAndBadItem(request: PickExecutionIssueRequest, missQty
println("Updated stock out line ${stockOutLine.id} status to: ${status}")
}
try {
stockOutLineRepository.flush()
stockOutLineService.checkIsStockOutLineCompleted(request.pickOrderLineId)
println("✅ Checked pick order line ${request.pickOrderLineId} completion status after updating stock out line")
} catch (e: Exception) {
println("⚠️ Error checking pick order line completion: ${e.message}")
e.printStackTrace()
// 不中断主流程,只记录错误
}
}
// 修复:使用 REQUIRES_NEW 传播级别,避免事务冲突
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = [Exception::class])