From dbfbf8534a8a0a0809b6b5358b757682f5a55908 Mon Sep 17 00:00:00 2001 From: "CANCERYS\\kw093" Date: Tue, 15 Sep 2026 12:31:08 +0800 Subject: [PATCH] fix stock leger balance part --- .../service/DoWorkbenchMainService.kt | 5 +- .../master/service/StockLedgerFixService.kt | 22 ++++--- .../service/PickExecutionIssueService.kt | 60 ++++++++++--------- .../stock/entity/StockLedgerRepository.kt | 1 + .../stock/service/StockInLineService.kt | 13 ++-- .../stock/service/StockOutLineService.kt | 20 ++----- .../service/StockOutLineWorkbenchService.kt | 5 +- .../stock/service/StockTakeRecordService.kt | 13 +++- 8 files changed, 78 insertions(+), 61 deletions(-) diff --git a/src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DoWorkbenchMainService.kt b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DoWorkbenchMainService.kt index dba9441..97d870f 100644 --- a/src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DoWorkbenchMainService.kt +++ b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DoWorkbenchMainService.kt @@ -2665,12 +2665,13 @@ return MessageResponse( } private fun resolveWorkbenchPreviousBalance( - itemId: Long, inventory: Inventory, onHandQtyBeforeUpdate: Double?, ): Double { if (onHandQtyBeforeUpdate != null) return onHandQtyBeforeUpdate - val latestLedger = stockLedgerRepository.findFirstByItemIdAndDeletedFalseOrderByDateDescIdDesc(itemId) + val latestLedger = inventory.id?.let { + stockLedgerRepository.findFirstByInventory_IdAndDeletedFalseOrderByDateDescIdDesc(it) + } return latestLedger?.balance ?: (inventory.onHandQty ?: BigDecimal.ZERO).toDouble() } diff --git a/src/main/java/com/ffii/fpsms/modules/master/service/StockLedgerFixService.kt b/src/main/java/com/ffii/fpsms/modules/master/service/StockLedgerFixService.kt index d780d4d..bea9364 100644 --- a/src/main/java/com/ffii/fpsms/modules/master/service/StockLedgerFixService.kt +++ b/src/main/java/com/ffii/fpsms/modules/master/service/StockLedgerFixService.kt @@ -482,8 +482,7 @@ open class StockLedgerFixService( } open fun inventoryScopeDetail(inventoryId: Long): StockLedgerFixScopeDetail { - val today = LocalDate.now() - val args = mapOf("inventoryId" to inventoryId, "today" to today) + val args = mapOf("inventoryId" to inventoryId, "today" to inventoryScopeToExclusive()) val head = jdbcDao.queryForList( """ SELECT @@ -556,10 +555,14 @@ open class StockLedgerFixService( ) } + /** + * Rebuild 2.1–2.6 for one inventory bucket through calendar today + * (`date < tomorrow`). Calendar day-fix still excludes today unless Allow today. + */ open fun fixInventoryScope(inventoryId: Long): StockLedgerFixRunResponse { if (inventoryId <= 0) throw IllegalArgumentException("inventoryId is required") if (!inFlight.add(runLock)) throw IllegalStateException("fix already running") - val today = LocalDate.now() + val toExclusive = inventoryScopeToExclusive() val t0 = System.nanoTime() try { val inv = jdbcDao.queryForList( @@ -572,26 +575,26 @@ open class StockLedgerFixService( "inventoryId" to inventoryId, "itemId" to (inv["itemId"] ?: 0), "uomId" to (inv["uomId"] ?: 0), - "today" to today, + "today" to toExclusive, ) val filledLotLineId = timed("inv 2.1") { fillLotLineIdScope(args, byInventory = true) } val filledUomId = timed("inv 2.2") { fillUomIdScope(args, byInventory = true) } val filledInventoryId = timed("inv 2.3") { fillInventoryIdScope(args, byInventory = true) } val filledLotQty = timed("inv 2.4") { fillLotQtyHistory(args, byInventory = true) } val filledBalance = timed("inv 2.5") { fillBalanceHistory(args) } - val lotIds = lotIdsForInventory(inventoryId, today) - val range = dateRangeForFilter("inventoryId = :inventoryId", mapOf("inventoryId" to inventoryId, "today" to today)) + val lotIds = lotIdsForInventory(inventoryId, toExclusive) + val range = dateRangeForFilter("inventoryId = :inventoryId", mapOf("inventoryId" to inventoryId, "today" to toExclusive)) val dayRows = if (range != null) { timed("inv 2.6") { upsertStockLotDayRange(lotIds, range.first, range.second) } } else 0 - val still = stillMissScope("inventoryId = :inventoryId", mapOf("inventoryId" to inventoryId, "today" to today)) + val still = stillMissScope("inventoryId = :inventoryId", mapOf("inventoryId" to inventoryId, "today" to toExclusive)) log.info( "stock-ledger-fix inventory {} lotLine={} uom={} inv={} lotQty={} bal={} dayRows={} total={}ms", inventoryId, filledLotLineId, filledUomId, filledInventoryId, filledLotQty, filledBalance, dayRows, (System.nanoTime() - t0) / 1_000_000, ) return StockLedgerFixRunResponse( - date = range?.first?.toString() ?: today.toString(), + date = range?.first?.toString() ?: LocalDate.now().toString(), filledLotLineId = filledLotLineId, filledUomId = filledUomId, filledInventoryId = filledInventoryId, @@ -1730,6 +1733,9 @@ open class StockLedgerFixService( ) } + /** Exclusive bound for inventory-scope inspect/fix: include calendar today. */ + private fun inventoryScopeToExclusive(): LocalDate = LocalDate.now().plusDays(1) + private fun fillBalanceHistory(args: Map): Int { return jdbcDao.executeUpdate( """ 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 7321893..a392e3a 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 @@ -10,6 +10,7 @@ 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 +import com.ffii.fpsms.modules.stock.entity.Inventory import com.ffii.fpsms.modules.stock.entity.InventoryRepository import com.ffii.fpsms.modules.stock.service.StockOutLineService @@ -46,6 +47,7 @@ import com.ffii.fpsms.modules.stock.web.model.BatchStockOutResult import com.ffii.fpsms.modules.stock.entity.StockLedger import com.ffii.fpsms.modules.stock.entity.StockLedgerRepository import com.ffii.fpsms.modules.stock.service.StockLedgerLotSnapshot +import com.ffii.fpsms.modules.stock.service.InventoryBucketResolver import com.ffii.fpsms.modules.deliveryOrder.entity.DeliveryOrderRepository import com.ffii.fpsms.modules.deliveryOrder.enums.DeliveryOrderStatus import com.ffii.fpsms.modules.stock.entity.SuggestPickLotRepository @@ -72,7 +74,8 @@ open class PickExecutionIssueService( private val stockLedgerRepository: StockLedgerRepository, private val deliveryOrderRepository: DeliveryOrderRepository, private val suggestedPickLotRepository: SuggestPickLotRepository, - private val doPickOrderRepository: DoPickOrderRepository + private val doPickOrderRepository: DoPickOrderRepository, + private val inventoryBucketResolver: InventoryBucketResolver, ) { // 配置属性:是否在批次拒绝时自动重新建议批次(默认为 false,不自动重新建议) @@ -858,13 +861,8 @@ private fun handleMissItemWithPartialPick(request: PickExecutionIssueRequest, ac println("Miss Qty: ${missQty}") val lotId = request.lotId ?: return - val itemId = request.itemId ?: return val inventoryLotLine = inventoryLotLineRepository.findById(lotId).orElse(null) - // ✅ 修复:在更新 lot line 之前获取 inventory 的 onHandQty - val inventoryBeforeUpdate = inventoryRepository.findByItemId(itemId).orElse(null) - val onHandQtyBeforeUpdate = (inventoryBeforeUpdate?.onHandQty ?: BigDecimal.ZERO).toDouble() - if (inventoryLotLine != null) { // ✅ 修改:更新 outQty 为 actualPickQty val currentOutQty = inventoryLotLine.outQty ?: BigDecimal.ZERO @@ -927,9 +925,7 @@ private fun handleMissItemOnly(request: PickExecutionIssueRequest, missQty: BigD val itemId = request.itemId ?: return val inventoryLotLine = inventoryLotLineRepository.findById(lotId).orElse(null) - // ✅ 修复:在更新 lot line 之前获取 inventory 的 onHandQty - val inventoryBeforeUpdate = inventoryRepository.findByItemId(itemId).orElse(null) - val onHandQtyBeforeUpdate = (inventoryBeforeUpdate?.onHandQty ?: BigDecimal.ZERO).toDouble() + val onHandQtyBeforeUpdate = bucketOnHandQty(itemId, inventoryLotLine) if (inventoryLotLine != null) { // ✅ 修改:释放 holdQty(减少 holdQty) @@ -1128,10 +1124,9 @@ private fun handleBothMissAndBadItem(request: PickExecutionIssueRequest, missQty val lotId = request.lotId ?: return val itemId = request.itemId ?: return + val inventoryLotLine = inventoryLotLineRepository.findById(lotId).orElse(null) - // ✅ 修复:在更新 lot line 之前获取 inventory 的 onHandQty - val inventoryBeforeUpdate = inventoryRepository.findByItemId(itemId).orElse(null) - val onHandQtyBeforeUpdate = (inventoryBeforeUpdate?.onHandQty ?: BigDecimal.ZERO).toDouble() + val onHandQtyBeforeUpdate = bucketOnHandQty(itemId, inventoryLotLine) // ✅ 修复:更新 stock_out_line,但不要累积 qty val stockOutLines = stockOutLineRepository.findByPickOrderLineIdAndInventoryLotLineIdAndDeletedFalse( @@ -1163,7 +1158,6 @@ private fun handleBothMissAndBadItem(request: PickExecutionIssueRequest, missQty } // ✅ 修复:更新 inventory_lot_line 的 outQty - val inventoryLotLine = inventoryLotLineRepository.findById(lotId).orElse(null) if (inventoryLotLine != null) { // ✅ 修复:计算新的 outQty,考虑之前的 outQty val currentOutQty = inventoryLotLine.outQty ?: BigDecimal.ZERO @@ -2324,23 +2318,19 @@ private fun createStockLedgerForStockOut( val item = stockOutLine.item ?: return val outQty = stockOutLine.qty?.toDouble() ?: 0.0 - - // ✅ 修复:如果传入了 balance,直接使用;否则重新查询 inventory + val inventory = resolveLedgerInventory(item.id!!, stockOutLine.inventoryLotLine) ?: return + val finalBalance = if (balance != null) { if (balance < 0) 0.0 else balance } else { - // 重新查询 inventory 以获取触发器更新后的最新 onHandQty - val inventory = inventoryRepository.findByItemId(item.id!!).orElse(null) ?: return - val currentOnHandQty = (inventory.onHandQty ?: BigDecimal.ZERO).toDouble() - val newBalance = currentOnHandQty - outQty + val previous = previousBalanceForInventory(inventory) + val newBalance = previous - outQty if (newBalance < 0) 0.0 else newBalance } // 使用传入的 ledgerType,如果没有则使用 stockOutLine.type val ledgerTypeToUse = ledgerType ?: stockOutLine.type ?: "Nor" - val inventory = inventoryRepository.findByItemId(item.id!!).orElse(null) ?: return - val ill = stockOutLine.inventoryLotLine val outQtyBd = BigDecimal.valueOf(outQty) val lotAfter = ill?.let { StockLedgerLotSnapshot.availableQty(it) } @@ -2736,8 +2726,11 @@ open fun submitIssueWithQty(request: SubmitIssueWithQtyRequest): MessageResponse ) } - // ✅ 修复:在创建和保存 stockOutLine 之前获取 inventory 的当前 onHandQty - val inventoryBeforeUpdate = inventoryRepository.findByItemId(request.itemId).orElse(null) + val lotLine = request.lotId.let { + inventoryLotLineRepository.findById(it).orElse(null) + } + val inventoryBeforeUpdate = inventoryBucketResolver.findInventoryBucket(request.itemId, lotLine) + ?: inventoryRepository.findByItemId(request.itemId).orElse(null) val onHandQtyBeforeUpdate = (inventoryBeforeUpdate?.onHandQty ?: BigDecimal.ZERO).toDouble() println("=== submitIssueWithQty: Before update ===") @@ -2746,9 +2739,6 @@ open fun submitIssueWithQty(request: SubmitIssueWithQtyRequest): MessageResponse println("OnHandQty Before Update: ${onHandQtyBeforeUpdate}") println("==========================================") - val lotLine = request.lotId.let { - inventoryLotLineRepository.findById(it).orElse(null) - } println("Lot line: id=${lotLine?.id}, lotNo=${lotLine?.inventoryLot?.lotNo}") val item = itemsRepository.findById(request.itemId).orElse(null) @@ -2835,7 +2825,7 @@ private fun createStockLedgerForStockOutWithBalance( // Use传入的 ledgerType,如果没有则使用 stockOutLine.type val ledgerTypeToUse = ledgerType ?: stockOutLine.type ?: "Nor" - val inventory = inventoryRepository.findByItemId(item.id!!).orElse(null) ?: return + val inventory = resolveLedgerInventory(item.id!!, stockOutLine.inventoryLotLine) ?: return val ill = stockOutLine.inventoryLotLine val outQtyBd = BigDecimal.valueOf(outQty) @@ -2947,4 +2937,20 @@ private fun resolveLotUomInfo(lotId: Long?): Pair { return Pair(uomCode, uomDesc) } + +private fun bucketOnHandQty(itemId: Long, lotLine: InventoryLotLine?): Double { + val inventory = resolveLedgerInventory(itemId, lotLine) + return (inventory?.onHandQty ?: BigDecimal.ZERO).toDouble() +} + +private fun resolveLedgerInventory(itemId: Long, lotLine: InventoryLotLine?): Inventory? = + inventoryBucketResolver.findInventoryBucket(itemId, lotLine) + ?: inventoryRepository.findByItemId(itemId).orElse(null) + +private fun previousBalanceForInventory(inventory: Inventory): Double { + val latest = inventory.id?.let { + stockLedgerRepository.findFirstByInventory_IdAndDeletedFalseOrderByDateDescIdDesc(it) + } + return latest?.balance ?: (inventory.onHandQty ?: BigDecimal.ZERO).toDouble() +} } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/stock/entity/StockLedgerRepository.kt b/src/main/java/com/ffii/fpsms/modules/stock/entity/StockLedgerRepository.kt index f5b3027..9335de6 100644 --- a/src/main/java/com/ffii/fpsms/modules/stock/entity/StockLedgerRepository.kt +++ b/src/main/java/com/ffii/fpsms/modules/stock/entity/StockLedgerRepository.kt @@ -60,6 +60,7 @@ interface StockLedgerRepository: AbstractRepository { """) fun findLatestByItemId(@Param("itemId") itemId: Long): List fun findFirstByItemIdAndDeletedFalseOrderByDateDescIdDesc(itemId: Long): StockLedger? +fun findFirstByInventory_IdAndDeletedFalseOrderByDateDescIdDesc(inventoryId: Long): StockLedger? @Query(""" SELECT sl FROM StockLedger sl diff --git a/src/main/java/com/ffii/fpsms/modules/stock/service/StockInLineService.kt b/src/main/java/com/ffii/fpsms/modules/stock/service/StockInLineService.kt index b143d78..c46be31 100644 --- a/src/main/java/com/ffii/fpsms/modules/stock/service/StockInLineService.kt +++ b/src/main/java/com/ffii/fpsms/modules/stock/service/StockInLineService.kt @@ -1074,7 +1074,7 @@ open class StockInLineService( if (putAwayDeltaStockQty > BigDecimal.ZERO) { val _tLedger = System.nanoTime() createStockLedgerForStockIn(stockInLine, putAwayDeltaStockQty.toDouble()) - _logStep("createStockLedgerForStockIn(includes findLatestByItemId)", _tLedger) + _logStep("createStockLedgerForStockIn(includes findLatestByInventoryId)", _tLedger) } val _tQtyCalc = System.nanoTime() @@ -1396,13 +1396,14 @@ open class StockInLineService( val inventory = inventoryBucketResolver.findInventoryBucket(item.id!!, stockInLine.inventoryLotLine) ?: return _logStep("inventoryBucketResolver.findInventoryBucket", _tInv) - // ✅ 修复:查询最新的 stock_ledger 记录,基于前一笔 balance 计算 + // Chain running balance on this inventory bucket only (not the whole item). val _tLatest = System.nanoTime() - val latestLedger = stockLedgerRepository.findFirstByItemIdAndDeletedFalseOrderByDateDescIdDesc(item.id!!) - _logStep("stockLedgerRepository.findLatestByItemId", _tLatest) + val latestLedger = inventory.id?.let { + stockLedgerRepository.findFirstByInventory_IdAndDeletedFalseOrderByDateDescIdDesc(it) + } + _logStep("stockLedgerRepository.findLatestByInventoryId", _tLatest) - // ✅ 修复:如果 latestLedger 为 null(第一次 stock in),previousBalance 应该是 0 - // 因为 inventory.onHandQty 已经被触发器更新为包含本次 stock in 的数量 + // First ledger on this bucket starts at 0: inventory.onHandQty already includes this stock-in. val previousBalance = latestLedger?.balance ?: 0.0 val newBalance = previousBalance + inQty diff --git a/src/main/java/com/ffii/fpsms/modules/stock/service/StockOutLineService.kt b/src/main/java/com/ffii/fpsms/modules/stock/service/StockOutLineService.kt index 73237b5..1901a76 100644 --- a/src/main/java/com/ffii/fpsms/modules/stock/service/StockOutLineService.kt +++ b/src/main/java/com/ffii/fpsms/modules/stock/service/StockOutLineService.kt @@ -1450,9 +1450,7 @@ open fun newBatchSubmit(request: QrPickBatchSubmitRequest): MessageResponse { val inventory = inventoryBucketResolver.findInventoryBucket(item.id!!, stockOutLine.inventoryLotLine) ?: return val outQty = stockOutLine.qty?.toDouble() ?: 0.0 - // Use latest ledger balance (same pattern as createStockLedgerForStockIn) so balance is correct when multiple actions run in one transaction - val latestLedger = stockLedgerRepository.findFirstByItemIdAndDeletedFalseOrderByDateDescIdDesc(item.id!!) - val previousBalance = latestLedger?.balance ?: (inventory.onHandQty ?: BigDecimal.ZERO).toDouble() + val previousBalance = resolvePreviousBalance(inventory = inventory) val newBalance = previousBalance - outQty val ill = stockOutLine.inventoryLotLine @@ -1726,7 +1724,6 @@ open fun newBatchSubmit(request: QrPickBatchSubmitRequest): MessageResponse { } val previousBalance = resolvePreviousBalance( - itemId = item.id!!, inventory = inventory, onHandQtyBeforeUpdate = onHandQtyBeforeUpdate ) @@ -1774,14 +1771,15 @@ open fun newBatchSubmit(request: QrPickBatchSubmitRequest): MessageResponse { } private fun resolvePreviousBalance( - itemId: Long, inventory: Inventory, onHandQtyBeforeUpdate: Double? = null ): Double { if (onHandQtyBeforeUpdate != null) { return onHandQtyBeforeUpdate } - val latestLedger = stockLedgerRepository.findFirstByItemIdAndDeletedFalseOrderByDateDescIdDesc(itemId) + val latestLedger = inventory.id?.let { + stockLedgerRepository.findFirstByInventory_IdAndDeletedFalseOrderByDateDescIdDesc(it) + } return latestLedger?.balance ?: (inventory.onHandQty ?: BigDecimal.ZERO).toDouble() } @@ -2137,10 +2135,7 @@ fun applyStockOutLineDelta( val item = savedSol.item ?: return savedSol val inventory = inventoryBucketResolver.findInventoryBucket(item.id!!, savedSol.inventoryLotLine) ?: return savedSol - val previousBalance = resolvePreviousBalance( - itemId = item.id!!, - inventory = inventory - ) + val previousBalance = resolvePreviousBalance(inventory = inventory) val outQty = deltaQty.toDouble() val newBalance = previousBalance - outQty @@ -2287,10 +2282,7 @@ open fun createStockOutBatch(request: BatchStockOutRequest): BatchStockOutResult ?: return@forEach val delta = BigDecimal.valueOf(sol.qty ?: 0.0) val prevBalance = runningLedgerBalanceByBucket[bucketKey] - ?: run { - val latestLedger = stockLedgerRepository.findFirstByItemIdAndDeletedFalseOrderByDateDescIdDesc(itemId) - latestLedger?.balance ?: (inv.onHandQty ?: BigDecimal.ZERO).toDouble() - } + ?: resolvePreviousBalance(inventory = inv) val newBalance = prevBalance - delta.toDouble() val ill = sol.inventoryLotLine diff --git a/src/main/java/com/ffii/fpsms/modules/stock/service/StockOutLineWorkbenchService.kt b/src/main/java/com/ffii/fpsms/modules/stock/service/StockOutLineWorkbenchService.kt index 6744f73..b2474f1 100644 --- a/src/main/java/com/ffii/fpsms/modules/stock/service/StockOutLineWorkbenchService.kt +++ b/src/main/java/com/ffii/fpsms/modules/stock/service/StockOutLineWorkbenchService.kt @@ -547,12 +547,13 @@ if (updated == 0) { } private fun resolveWorkbenchPreviousBalance( - itemId: Long, inventory: Inventory, onHandQtyBeforeUpdate: Double?, ): Double { if (onHandQtyBeforeUpdate != null) return onHandQtyBeforeUpdate - val latestLedger = stockLedgerRepository.findFirstByItemIdAndDeletedFalseOrderByDateDescIdDesc(itemId) + val latestLedger = inventory.id?.let { + stockLedgerRepository.findFirstByInventory_IdAndDeletedFalseOrderByDateDescIdDesc(it) + } return latestLedger?.balance ?: (inventory.onHandQty ?: BigDecimal.ZERO).toDouble() } diff --git a/src/main/java/com/ffii/fpsms/modules/stock/service/StockTakeRecordService.kt b/src/main/java/com/ffii/fpsms/modules/stock/service/StockTakeRecordService.kt index 86e8570..6c2cbb3 100644 --- a/src/main/java/com/ffii/fpsms/modules/stock/service/StockTakeRecordService.kt +++ b/src/main/java/com/ffii/fpsms/modules/stock/service/StockTakeRecordService.kt @@ -2482,7 +2482,7 @@ private fun applyVarianceAdjustment( ?: throw IllegalArgumentException("Item ID not found for stock take ledger") val ledgerKey = stockTakeLedgerBucketKey(itemIdForLedger, latestLine, inventory) val previousBalance = runtimeCache?.runningLedgerBalanceByBucket?.get(ledgerKey) - ?: (inventory.onHandQty ?: BigDecimal.ZERO).toDouble() + ?: previousBalanceForInventoryBucket(inventory) val newBalance = previousBalance - qtyToRemove.toDouble() // FP-MTMS Version Checklist | Functions Ref. No. 46 | v1.0.1 | 2026-08-05 @@ -2618,7 +2618,7 @@ private fun applyVarianceAdjustment( ?: throw IllegalArgumentException("Item ID not found for stock take ledger (in)") val ledgerKey = stockTakeLedgerBucketKey(itemIdForLedger, latestLine, inventory) val previousBalance = runtimeCache?.runningLedgerBalanceByBucket?.get(ledgerKey) - ?: (inventory.onHandQty ?: BigDecimal.ZERO).toDouble() + ?: previousBalanceForInventoryBucket(inventory) val newBalance = previousBalance + plusQty.toDouble() // latestLine.inQty already includes plusQty above — snapshot live lot line after move. @@ -2680,6 +2680,15 @@ private fun stockTakeLedgerBucketKey( return itemId to stockUomId } +private fun previousBalanceForInventoryBucket( + inventory: com.ffii.fpsms.modules.stock.entity.Inventory, +): Double { + val latest = inventory.id?.let { + stockLedgerRepository.findFirstByInventory_IdAndDeletedFalseOrderByDateDescIdDesc(it) + } + return latest?.balance ?: (inventory.onHandQty ?: BigDecimal.ZERO).toDouble() +} + private data class BatchAdjustmentCache( val inventoryLotLineByWarehouseLot: Map, InventoryLotLine>, val inventoryLotById: Map,