|
|
@@ -40,7 +40,7 @@ import com.ffii.fpsms.modules.bag.service.BagService |
|
|
import com.ffii.fpsms.modules.common.SecurityUtils |
|
|
import com.ffii.fpsms.modules.common.SecurityUtils |
|
|
import com.ffii.fpsms.modules.stock.entity.StockLedgerRepository |
|
|
import com.ffii.fpsms.modules.stock.entity.StockLedgerRepository |
|
|
import com.ffii.fpsms.modules.stock.entity.InventoryRepository |
|
|
import com.ffii.fpsms.modules.stock.entity.InventoryRepository |
|
|
|
|
|
|
|
|
|
|
|
import com.ffii.fpsms.modules.pickOrder.entity.PickExecutionIssueRepository |
|
|
import java.time.LocalTime |
|
|
import java.time.LocalTime |
|
|
@Service |
|
|
@Service |
|
|
open class StockOutLineService( |
|
|
open class StockOutLineService( |
|
|
@@ -63,7 +63,8 @@ private val doPickOrderLineRecordRepository: DoPickOrderLineRecordRepository, |
|
|
private val inventoryLotLineService: InventoryLotLineService, |
|
|
private val inventoryLotLineService: InventoryLotLineService, |
|
|
private val bagService: BagService, |
|
|
private val bagService: BagService, |
|
|
private val stockLedgerRepository: StockLedgerRepository, |
|
|
private val stockLedgerRepository: StockLedgerRepository, |
|
|
private val inventoryRepository: InventoryRepository |
|
|
|
|
|
|
|
|
private val inventoryRepository: InventoryRepository, |
|
|
|
|
|
private val pickExecutionIssueRepository: PickExecutionIssueRepository |
|
|
): AbstractBaseEntityService<StockOutLine, Long, StockOutLIneRepository>(jdbcDao, stockOutLineRepository) { |
|
|
): AbstractBaseEntityService<StockOutLine, Long, StockOutLIneRepository>(jdbcDao, stockOutLineRepository) { |
|
|
@Throws(IOException::class) |
|
|
@Throws(IOException::class) |
|
|
@Transactional |
|
|
@Transactional |
|
|
@@ -347,29 +348,51 @@ private fun getStockOutIdFromPickOrderLine(pickOrderLineId: Long): Long { |
|
|
allStockOutLines.forEach { sol -> |
|
|
allStockOutLines.forEach { sol -> |
|
|
println(" StockOutLine ${sol.id}: status=${sol.status}, qty=${sol.qty}") |
|
|
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 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) { |
|
|
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 { |
|
|
} else { |
|
|
false |
|
|
false |
|
|
} |
|
|
} |
|
|
|