|
|
@@ -89,6 +89,11 @@ import java.io.FileNotFoundException |
|
|
import java.util.Locale |
|
|
import java.util.Locale |
|
|
import com.ffii.fpsms.modules.deliveryOrder.service.DeliveryOrderService |
|
|
import com.ffii.fpsms.modules.deliveryOrder.service.DeliveryOrderService |
|
|
import org.springframework.jdbc.core.JdbcTemplate |
|
|
import org.springframework.jdbc.core.JdbcTemplate |
|
|
|
|
|
import org.springframework.transaction.PlatformTransactionManager |
|
|
|
|
|
import org.springframework.transaction.TransactionDefinition |
|
|
|
|
|
import org.springframework.transaction.support.TransactionSynchronization |
|
|
|
|
|
import org.springframework.transaction.support.TransactionSynchronizationManager |
|
|
|
|
|
import org.springframework.transaction.support.TransactionTemplate |
|
|
/** |
|
|
/** |
|
|
* DO workbench: pick execution and related operations (v1: [scanPick]). |
|
|
* DO workbench: pick execution and related operations (v1: [scanPick]). |
|
|
*/ |
|
|
*/ |
|
|
@@ -122,6 +127,7 @@ open class DoWorkbenchMainService( |
|
|
private val joPickOrderRepository: JoPickOrderRepository, |
|
|
private val joPickOrderRepository: JoPickOrderRepository, |
|
|
private val printerService: PrinterService, |
|
|
private val printerService: PrinterService, |
|
|
private val itemsRepository: ItemsRepository, |
|
|
private val itemsRepository: ItemsRepository, |
|
|
|
|
|
private val transactionManager: PlatformTransactionManager, |
|
|
) { |
|
|
) { |
|
|
@PersistenceContext |
|
|
@PersistenceContext |
|
|
private lateinit var entityManager: EntityManager |
|
|
private lateinit var entityManager: EntityManager |
|
|
@@ -589,83 +595,30 @@ val saveSolMs = lapMs() |
|
|
|
|
|
|
|
|
val pickOrderId = pol.pickOrder?.id |
|
|
val pickOrderId = pol.pickOrder?.id |
|
|
val poType = pol.pickOrder?.type |
|
|
val poType = pol.pickOrder?.type |
|
|
var rebuildMs = 0L |
|
|
|
|
|
var ensureMs = 0L |
|
|
|
|
|
var polPartialMs = 0L |
|
|
|
|
|
var postMs = 0L |
|
|
|
|
|
val effectiveExcludeWarehouseCodes = when (poType) { |
|
|
val effectiveExcludeWarehouseCodes = when (poType) { |
|
|
PickOrderType.JOB_ORDER -> request.excludeWarehouseCodes ?: emptyList() |
|
|
PickOrderType.JOB_ORDER -> request.excludeWarehouseCodes ?: emptyList() |
|
|
else -> request.excludeWarehouseCodes // null → DO 走 default;有傳則整份取代 default |
|
|
else -> request.excludeWarehouseCodes // null → DO 走 default;有傳則整份取代 default |
|
|
} |
|
|
} |
|
|
if (pickOrderId != null) { |
|
|
|
|
|
if (hasExplicitQty) { |
|
|
|
|
|
rebuildMs = measureTimeMillis { |
|
|
|
|
|
if (explicitRemainder > BigDecimal.ZERO) { |
|
|
|
|
|
suggestedPickLotWorkbenchService.setNoHoldSuggestionsForPickOrderLineNextSingleLot( |
|
|
|
|
|
pickOrderLineId = polId, |
|
|
|
|
|
targetQty = explicitRemainder, |
|
|
|
|
|
storeId = request.storeId, |
|
|
|
|
|
excludeInventoryLotLineId = scannedIll.id, |
|
|
|
|
|
excludeWarehouseCodes = effectiveExcludeWarehouseCodes, |
|
|
|
|
|
) |
|
|
|
|
|
} else { |
|
|
|
|
|
suggestedPickLotWorkbenchService.setNoHoldSuggestionsForPickOrderLineExactQty( |
|
|
|
|
|
pickOrderLineId = polId, |
|
|
|
|
|
targetQty = BigDecimal.ZERO, |
|
|
|
|
|
excludeWarehouseCodes = effectiveExcludeWarehouseCodes, |
|
|
|
|
|
) |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
ensureMs = measureTimeMillis { |
|
|
|
|
|
if (explicitRemainder > BigDecimal.ZERO) { |
|
|
|
|
|
stockOutLineWorkbenchService.ensureStockOutLinesForPickOrderLineNoHold(polId, request.userId) |
|
|
|
|
|
} else { |
|
|
|
|
|
val allSolEntities = |
|
|
|
|
|
stockOutLIneRepository.findAllByPickOrderLineIdInAndDeletedFalse(listOf(polId)) |
|
|
|
|
|
val toClose = allSolEntities.filter { it.id != sol.id && !isWorkbenchSolEndStatus(it.status) } |
|
|
|
|
|
if (toClose.isNotEmpty()) { |
|
|
|
|
|
toClose.forEach { s -> |
|
|
|
|
|
s.status = StockOutLineStatus.COMPLETE.status |
|
|
|
|
|
if (s.startTime == null) s.startTime = LocalDateTime.now() |
|
|
|
|
|
s.endTime = LocalDateTime.now() |
|
|
|
|
|
} |
|
|
|
|
|
stockOutLIneRepository.saveAll(toClose) |
|
|
|
|
|
stockOutLIneRepository.flush() |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} else { |
|
|
|
|
|
rebuildMs = measureTimeMillis { |
|
|
|
|
|
suggestedPickLotWorkbenchService.rebuildNoHoldSuggestionsForPickOrderLine( |
|
|
|
|
|
pickOrderLineId = polId, |
|
|
|
|
|
storeId = request.storeId, |
|
|
|
|
|
excludeWarehouseCodes = effectiveExcludeWarehouseCodes, |
|
|
|
|
|
) |
|
|
|
|
|
} |
|
|
|
|
|
ensureMs = measureTimeMillis { |
|
|
|
|
|
stockOutLineWorkbenchService.ensureStockOutLinesForPickOrderLineNoHold(polId, request.userId) |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (effectiveLotExhaustedSplit) { |
|
|
|
|
|
polPartialMs = measureTimeMillis { |
|
|
|
|
|
val polEntity = pickOrderLineRepository.findById(polId).orElse(null) |
|
|
|
|
|
if (polEntity != null) { |
|
|
|
|
|
polEntity.status = PickOrderLineStatus.PARTIALLY_COMPLETE |
|
|
|
|
|
pickOrderLineRepository.save(polEntity) |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
val ledgerMs = measureTimeMillis { createWorkbenchPickLedger(sol, effectiveDelta) } |
|
|
|
|
|
registerAfterCommit { |
|
|
|
|
|
runInNewTransaction { |
|
|
|
|
|
runWorkbenchPickDeferredFollowUps( |
|
|
|
|
|
solId = sol.id!!, |
|
|
|
|
|
polId = polId, |
|
|
|
|
|
pickOrderId = pickOrderId, |
|
|
|
|
|
itemId = itemId, |
|
|
|
|
|
userId = request.userId, |
|
|
|
|
|
hasExplicitQty = hasExplicitQty, |
|
|
|
|
|
explicitRemainder = explicitRemainder, |
|
|
|
|
|
scannedIllId = scannedIll.id, |
|
|
|
|
|
requestStoreId = request.storeId, |
|
|
|
|
|
effectiveExcludeWarehouseCodes = effectiveExcludeWarehouseCodes, |
|
|
|
|
|
effectiveLotExhaustedSplit = effectiveLotExhaustedSplit, |
|
|
|
|
|
effectiveDelta = effectiveDelta, |
|
|
|
|
|
) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
updateJoPickOrderHandledByIfJobOrder( |
|
|
|
|
|
pickOrderId = pickOrderId, |
|
|
|
|
|
itemId = itemId, |
|
|
|
|
|
userId = request.userId, |
|
|
|
|
|
) |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
postMs = measureTimeMillis { postWorkbenchPickSideEffects(sol, effectiveDelta) } |
|
|
|
|
|
|
|
|
|
|
|
val mapFetchT0 = System.nanoTime() |
|
|
val mapFetchT0 = System.nanoTime() |
|
|
val mapped = stockOutLIneRepository.findStockOutLineInfoById(sol.id!!) |
|
|
val mapped = stockOutLIneRepository.findStockOutLineInfoById(sol.id!!) |
|
|
val mapFetchMs = (System.nanoTime() - mapFetchT0) / 1_000_000 |
|
|
val mapFetchMs = (System.nanoTime() - mapFetchT0) / 1_000_000 |
|
|
@@ -673,15 +626,12 @@ val mapFetchMs = (System.nanoTime() - mapFetchT0) / 1_000_000 |
|
|
val totalMs = (System.nanoTime() - wall0) / 1_000_000 |
|
|
val totalMs = (System.nanoTime() - wall0) / 1_000_000 |
|
|
/* |
|
|
/* |
|
|
log.info( |
|
|
log.info( |
|
|
"workbench scanPick timing (ms): total={} prep={} outbound={} saveSol={} rebuildSpl={} ensureSol={} polPartial={} postEffects={} mapFetch={} lotSplit={} solId={} polId={} poId={}", |
|
|
|
|
|
|
|
|
"workbench scanPick timing (ms): total={} prep={} outbound={} saveSol={} ledger={} mapFetch={} lotSplit={} solId={} polId={} poId={}", |
|
|
totalMs, |
|
|
totalMs, |
|
|
prepMs, |
|
|
prepMs, |
|
|
outboundMs, |
|
|
outboundMs, |
|
|
saveSolMs, |
|
|
saveSolMs, |
|
|
rebuildMs, |
|
|
|
|
|
ensureMs, |
|
|
|
|
|
polPartialMs, |
|
|
|
|
|
postMs, |
|
|
|
|
|
|
|
|
ledgerMs, |
|
|
mapFetchMs, |
|
|
mapFetchMs, |
|
|
effectiveLotExhaustedSplit, |
|
|
effectiveLotExhaustedSplit, |
|
|
sol.id, |
|
|
sol.id, |
|
|
@@ -1952,16 +1902,152 @@ return MessageResponse( |
|
|
} |
|
|
} |
|
|
checkWorkbenchPickOrderLineCompleted(polId, solsForPol) |
|
|
checkWorkbenchPickOrderLineCompleted(polId, solsForPol) |
|
|
val pickOrder = pol.pickOrder ?: return |
|
|
val pickOrder = pol.pickOrder ?: return |
|
|
val poId = pickOrder.id ?: return |
|
|
|
|
|
val allLines = pickOrderLineRepository.findAllByPickOrderIdAndDeletedFalse(poId) |
|
|
|
|
|
val allCompleted = allLines.isNotEmpty() && allLines.all { it.status == PickOrderLineStatus.COMPLETED } |
|
|
|
|
|
|
|
|
val poId = pol.pickOrder?.id ?: return |
|
|
|
|
|
val freshPo = pickOrderRepository.findById(poId).orElse(null) ?: return |
|
|
|
|
|
val total = freshPo.totalLines ?: 0 |
|
|
|
|
|
val completed = freshPo.submittedLines ?: 0 |
|
|
|
|
|
val allCompleted = total > 0 && completed >= total |
|
|
if (allCompleted) { |
|
|
if (allCompleted) { |
|
|
completePickOrderWithRetry(poId) |
|
|
completePickOrderWithRetry(poId) |
|
|
tryCompleteDeliveryOrderPickOrderTicketCompleted(poId) |
|
|
tryCompleteDeliveryOrderPickOrderTicketCompleted(poId) |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
private fun registerAfterCommit(action: () -> Unit) { |
|
|
|
|
|
if (!TransactionSynchronizationManager.isSynchronizationActive()) { |
|
|
|
|
|
action() |
|
|
|
|
|
return |
|
|
|
|
|
} |
|
|
|
|
|
TransactionSynchronizationManager.registerSynchronization( |
|
|
|
|
|
object : TransactionSynchronization { |
|
|
|
|
|
override fun afterCommit() { |
|
|
|
|
|
action() |
|
|
|
|
|
} |
|
|
|
|
|
}, |
|
|
|
|
|
) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private fun runInNewTransaction(action: () -> Unit) { |
|
|
|
|
|
val txTemplate = TransactionTemplate(transactionManager).apply { |
|
|
|
|
|
propagationBehavior = TransactionDefinition.PROPAGATION_REQUIRES_NEW |
|
|
|
|
|
} |
|
|
|
|
|
txTemplate.executeWithoutResult { |
|
|
|
|
|
action() |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
private fun postWorkbenchPickSideEffects(savedStockOutLine: StockOutLine, deltaQty: BigDecimal) { |
|
|
|
|
|
|
|
|
private fun runWorkbenchPickDeferredFollowUps( |
|
|
|
|
|
solId: Long, |
|
|
|
|
|
polId: Long, |
|
|
|
|
|
pickOrderId: Long?, |
|
|
|
|
|
itemId: Long, |
|
|
|
|
|
userId: Long, |
|
|
|
|
|
hasExplicitQty: Boolean, |
|
|
|
|
|
explicitRemainder: BigDecimal, |
|
|
|
|
|
scannedIllId: Long?, |
|
|
|
|
|
requestStoreId: String?, |
|
|
|
|
|
effectiveExcludeWarehouseCodes: List<String>?, |
|
|
|
|
|
effectiveLotExhaustedSplit: Boolean, |
|
|
|
|
|
effectiveDelta: BigDecimal, |
|
|
|
|
|
) { |
|
|
|
|
|
val deferredStart = System.nanoTime() |
|
|
|
|
|
var rebuildMs = 0L |
|
|
|
|
|
var ensureMs = 0L |
|
|
|
|
|
var polPartialMs = 0L |
|
|
|
|
|
var joHandledByMs = 0L |
|
|
|
|
|
var postMs = 0L |
|
|
|
|
|
try { |
|
|
|
|
|
if (pickOrderId != null) { |
|
|
|
|
|
if (hasExplicitQty) { |
|
|
|
|
|
rebuildMs = measureTimeMillis { |
|
|
|
|
|
if (explicitRemainder > BigDecimal.ZERO) { |
|
|
|
|
|
suggestedPickLotWorkbenchService.setNoHoldSuggestionsForPickOrderLineNextSingleLot( |
|
|
|
|
|
pickOrderLineId = polId, |
|
|
|
|
|
targetQty = explicitRemainder, |
|
|
|
|
|
storeId = requestStoreId, |
|
|
|
|
|
excludeInventoryLotLineId = scannedIllId, |
|
|
|
|
|
excludeWarehouseCodes = effectiveExcludeWarehouseCodes, |
|
|
|
|
|
) |
|
|
|
|
|
} else { |
|
|
|
|
|
suggestedPickLotWorkbenchService.setNoHoldSuggestionsForPickOrderLineExactQty( |
|
|
|
|
|
pickOrderLineId = polId, |
|
|
|
|
|
targetQty = BigDecimal.ZERO, |
|
|
|
|
|
excludeWarehouseCodes = effectiveExcludeWarehouseCodes, |
|
|
|
|
|
) |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
ensureMs = measureTimeMillis { |
|
|
|
|
|
if (explicitRemainder > BigDecimal.ZERO) { |
|
|
|
|
|
stockOutLineWorkbenchService.ensureStockOutLinesForPickOrderLineNoHold(polId, userId) |
|
|
|
|
|
} else { |
|
|
|
|
|
val allSolEntities = |
|
|
|
|
|
stockOutLIneRepository.findAllByPickOrderLineIdInAndDeletedFalse(listOf(polId)) |
|
|
|
|
|
val toClose = allSolEntities.filter { it.id != solId && !isWorkbenchSolEndStatus(it.status) } |
|
|
|
|
|
if (toClose.isNotEmpty()) { |
|
|
|
|
|
toClose.forEach { s -> |
|
|
|
|
|
s.status = StockOutLineStatus.COMPLETE.status |
|
|
|
|
|
if (s.startTime == null) s.startTime = LocalDateTime.now() |
|
|
|
|
|
s.endTime = LocalDateTime.now() |
|
|
|
|
|
} |
|
|
|
|
|
stockOutLIneRepository.saveAll(toClose) |
|
|
|
|
|
stockOutLIneRepository.flush() |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} else { |
|
|
|
|
|
rebuildMs = measureTimeMillis { |
|
|
|
|
|
suggestedPickLotWorkbenchService.rebuildNoHoldSuggestionsForPickOrderLine( |
|
|
|
|
|
pickOrderLineId = polId, |
|
|
|
|
|
storeId = requestStoreId, |
|
|
|
|
|
excludeWarehouseCodes = effectiveExcludeWarehouseCodes, |
|
|
|
|
|
) |
|
|
|
|
|
} |
|
|
|
|
|
ensureMs = measureTimeMillis { |
|
|
|
|
|
stockOutLineWorkbenchService.ensureStockOutLinesForPickOrderLineNoHold(polId, userId) |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (effectiveLotExhaustedSplit) { |
|
|
|
|
|
polPartialMs = measureTimeMillis { |
|
|
|
|
|
val polEntity = pickOrderLineRepository.findById(polId).orElse(null) |
|
|
|
|
|
if (polEntity != null) { |
|
|
|
|
|
polEntity.status = PickOrderLineStatus.PARTIALLY_COMPLETE |
|
|
|
|
|
pickOrderLineRepository.save(polEntity) |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
joHandledByMs = measureTimeMillis { |
|
|
|
|
|
updateJoPickOrderHandledByIfJobOrder( |
|
|
|
|
|
pickOrderId = pickOrderId, |
|
|
|
|
|
itemId = itemId, |
|
|
|
|
|
userId = userId, |
|
|
|
|
|
) |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
val savedSol = stockOutLIneRepository.findById(solId).orElse(null) |
|
|
|
|
|
if (savedSol != null) { |
|
|
|
|
|
postMs = measureTimeMillis { |
|
|
|
|
|
postWorkbenchPickSideEffects(savedSol, effectiveDelta, createLedger = false) |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} catch (e: Exception) { |
|
|
|
|
|
log.error("WORKBENCH_DEFERRED_PICK_FAILED solId={} polId={} msg={}", solId, polId, e.message, e) |
|
|
|
|
|
} finally { |
|
|
|
|
|
val totalMs = (System.nanoTime() - deferredStart) / 1_000_000 |
|
|
|
|
|
log.info( |
|
|
|
|
|
"WORKBENCH_DEFERRED_PICK_TRACE solId={} polId={} totalMs={} rebuildMs={} ensureMs={} polPartialMs={} joHandledByMs={} postMs={}", |
|
|
|
|
|
solId, |
|
|
|
|
|
polId, |
|
|
|
|
|
totalMs, |
|
|
|
|
|
rebuildMs, |
|
|
|
|
|
ensureMs, |
|
|
|
|
|
polPartialMs, |
|
|
|
|
|
joHandledByMs, |
|
|
|
|
|
postMs, |
|
|
|
|
|
) |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
private fun postWorkbenchPickSideEffects(savedStockOutLine: StockOutLine, deltaQty: BigDecimal, createLedger: Boolean = true) { |
|
|
if (deltaQty <= BigDecimal.ZERO) return |
|
|
if (deltaQty <= BigDecimal.ZERO) return |
|
|
val wall0 = System.nanoTime() |
|
|
val wall0 = System.nanoTime() |
|
|
var ledgerMs = 0L |
|
|
var ledgerMs = 0L |
|
|
@@ -1971,7 +2057,9 @@ return MessageResponse( |
|
|
var poLinesFetchMs = 0L |
|
|
var poLinesFetchMs = 0L |
|
|
var poCompleteAndDoMs = 0L |
|
|
var poCompleteAndDoMs = 0L |
|
|
|
|
|
|
|
|
ledgerMs = measureTimeMillis { createWorkbenchPickLedger(savedStockOutLine, deltaQty) } |
|
|
|
|
|
|
|
|
if (createLedger) { |
|
|
|
|
|
ledgerMs = measureTimeMillis { createWorkbenchPickLedger(savedStockOutLine, deltaQty) } |
|
|
|
|
|
} |
|
|
try { |
|
|
try { |
|
|
val bagT0 = System.nanoTime() |
|
|
val bagT0 = System.nanoTime() |
|
|
val solItem = savedStockOutLine.item |
|
|
val solItem = savedStockOutLine.item |
|
|
@@ -2018,15 +2106,19 @@ return MessageResponse( |
|
|
polCompleteMs = measureTimeMillis { tryCompletePickOrderLineWorkbench(polId, solsForPol) } |
|
|
polCompleteMs = measureTimeMillis { tryCompletePickOrderLineWorkbench(polId, solsForPol) } |
|
|
} |
|
|
} |
|
|
polCheckMs = measureTimeMillis { checkWorkbenchPickOrderLineCompleted(polId, solsForPol) } |
|
|
polCheckMs = measureTimeMillis { checkWorkbenchPickOrderLineCompleted(polId, solsForPol) } |
|
|
val pickOrder = pol.pickOrder |
|
|
|
|
|
if (pickOrder != null && pickOrder.id != null) { |
|
|
|
|
|
val poId = pickOrder.id!! |
|
|
|
|
|
|
|
|
val poId = pol.pickOrder?.id ?: return |
|
|
|
|
|
val freshPo = pickOrderRepository.findById(poId).orElse(null) ?: return |
|
|
|
|
|
if (freshPo != null && freshPo.id != null) { |
|
|
|
|
|
val poId = freshPo.id!! |
|
|
val t0 = System.nanoTime() |
|
|
val t0 = System.nanoTime() |
|
|
val allLines = pickOrderLineRepository.findAllByPickOrderIdAndDeletedFalse(poId) |
|
|
|
|
|
|
|
|
//val allLines = pickOrderLineRepository.findAllByPickOrderIdAndDeletedFalse(poId) |
|
|
poLinesFetchMs = (System.nanoTime() - t0) / 1_000_000 |
|
|
poLinesFetchMs = (System.nanoTime() - t0) / 1_000_000 |
|
|
// Split rows use pick_order_line.status = partially_completed until fully picked; do not complete header PO until every line is completed. |
|
|
// Split rows use pick_order_line.status = partially_completed until fully picked; do not complete header PO until every line is completed. |
|
|
val allCompleted = allLines.all { it.status == PickOrderLineStatus.COMPLETED } |
|
|
|
|
|
if (allCompleted && allLines.isNotEmpty()) { |
|
|
|
|
|
|
|
|
// val allCompleted = allLines.all { it.status == PickOrderLineStatus.COMPLETED } |
|
|
|
|
|
val total = freshPo.totalLines ?: 0 |
|
|
|
|
|
val completed = freshPo.submittedLines ?: 0 |
|
|
|
|
|
val allCompleted = total > 0 && completed >= total |
|
|
|
|
|
if (allCompleted) { |
|
|
poCompleteAndDoMs = measureTimeMillis { |
|
|
poCompleteAndDoMs = measureTimeMillis { |
|
|
// Use reload+retry to avoid optimistic lock when other flows update the same PO. |
|
|
// Use reload+retry to avoid optimistic lock when other flows update the same PO. |
|
|
completePickOrderWithRetry(poId) |
|
|
completePickOrderWithRetry(poId) |
|
|
@@ -2100,13 +2192,18 @@ return MessageResponse( |
|
|
* Skips [checkAndCompletePickOrderByConsoCode] if POL was already completed (avoids full conso scan every pick). |
|
|
* Skips [checkAndCompletePickOrderByConsoCode] if POL was already completed (avoids full conso scan every pick). |
|
|
*/ |
|
|
*/ |
|
|
private fun tryCompletePickOrderLineWorkbench(pickOrderLineId: Long, sols: List<StockOutLineInfo>) { |
|
|
private fun tryCompletePickOrderLineWorkbench(pickOrderLineId: Long, sols: List<StockOutLineInfo>) { |
|
|
val polEntity = pickOrderLineRepository.findById(pickOrderLineId).orElse(null) ?: return |
|
|
|
|
|
if (polEntity.status == PickOrderLineStatus.COMPLETED) return |
|
|
|
|
|
if (sols.isEmpty()) return |
|
|
if (sols.isEmpty()) return |
|
|
val allEnded = sols.all { isWorkbenchSolEndStatus(it.status) } |
|
|
val allEnded = sols.all { isWorkbenchSolEndStatus(it.status) } |
|
|
if (!allEnded) return |
|
|
if (!allEnded) return |
|
|
polEntity.status = PickOrderLineStatus.COMPLETED |
|
|
|
|
|
pickOrderLineRepository.save(polEntity) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
val polEntity = pickOrderLineRepository.findById(pickOrderLineId).orElse(null) ?: return |
|
|
|
|
|
val poId = polEntity.pickOrder?.id ?: return |
|
|
|
|
|
|
|
|
|
|
|
// Atomic gate: only one concurrent request can transition this POL to COMPLETED. |
|
|
|
|
|
val changed = pickOrderLineRepository.markCompletedIfNotCompleted(pickOrderLineId) |
|
|
|
|
|
if (changed > 0) { |
|
|
|
|
|
pickOrderRepository.incrementSubmittedLines(poId) |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
|