From 9ff66b0d884fe19340b0cdba03b4701b49e7ff56 Mon Sep 17 00:00:00 2001 From: "CANCERYS\\kw093" Date: Tue, 14 Apr 2026 21:13:16 +0800 Subject: [PATCH] fix put away slow --- .../stock/entity/StockLedgerRepository.kt | 1 + .../stock/service/StockInLineService.kt | 100 ++++++++++++++---- 2 files changed, 78 insertions(+), 23 deletions(-) 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 e48a921..9ac5eac 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 @@ -57,4 +57,5 @@ interface StockLedgerRepository: AbstractRepository { ORDER BY sl.date DESC, sl.id DESC """) fun findLatestByItemId(@Param("itemId") itemId: Long): List +fun findFirstByItemIdAndDeletedFalseOrderByDateDescIdDesc(itemId: Long): StockLedger? } \ No newline at end of file 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 ddfd673..bac3b58 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 @@ -868,6 +868,13 @@ open class StockInLineService( @Throws(IOException::class) @Transactional open fun update(request: SaveStockInLineRequest): MessageResponse { + val _t0 = System.nanoTime() + fun _msSince(t: Long): Double = (System.nanoTime() - t) / 1_000_000.0 + fun _logStep(name: String, t: Long) { + logger.info("[SIL.update timing] $name took ${String.format("%.3f", _msSince(t))} ms") + } + + val _tFind = System.nanoTime() val stockInLine = if (request.id != null) stockInLineRepository.findById(request.id!!).orElseThrow() else return MessageResponse( id = null, @@ -877,6 +884,7 @@ open class StockInLineService( message = "stock in line id is null", errorPosition = null, ) + _logStep("stockInLineRepository.findById", _tFind) // val allStockInLine = stockInLineRepository.findAllStockInLineInfoByStockInIdAndDeletedFalse(stockInLine!!.stockIn!!.id!!) if (stockInLine.expiryDate != null && request.expiryDate == null) { @@ -887,6 +895,7 @@ open class StockInLineService( // TODO: check all status to prevent reverting progress due to multiple users access to the same po? // return list of stock in line, update data grid with the list + val _tApplyFields = System.nanoTime() stockInLine.apply { this.productionDate = request.productionDate?.atStartOfDay() ?: this.productionDate this.productLotNo = request.productLotNo ?: this.productLotNo @@ -941,15 +950,23 @@ open class StockInLineService( this.expiryDate = stockInLine.expiryDate ?: request.expiryDate this.remarks = stockInLine.remarks ?: request.remarks } + _logStep("apply_fields", _tApplyFields) if (request.status == StockInLineStatus.RECEIVED.status) { + val _tPutAwayTotal = System.nanoTime() // Putaway var savedInventoryLotLine: InventoryLotLine? = null // savedInventoryLotLine = saveInventoryLotLineWhenStockIn(request = request, stockInLine = stockInLine) + val _tSaveLotLines = System.nanoTime() val savedInventoryLotLines = saveInventoryLotLineWhenStockIn(request = request, stockInLine = stockInLine) + _logStep("saveInventoryLotLineWhenStockIn", _tSaveLotLines) + + val _tFindAllLotLines = System.nanoTime() val inventoryLotLines = stockInLine.inventoryLot?.let { it.id?.let { _id -> inventoryLotLineRepository.findAllByInventoryLotId(_id) } } ?: listOf() + _logStep("inventoryLotLineRepository.findAllByInventoryLotId", _tFindAllLotLines) // ✅ 每次上架都寫一筆 stock_ledger(inQty = 本次上架的庫存數量) + val _tDeltaCompute = System.nanoTime() val putAwayDeltaStockQty = (request.inventoryLotLines ?: listOf()).sumOf { line -> val stockItemUom = itemUomRepository.findBaseUnitByItemIdAndStockUnitIsTrueAndDeletedIsFalse( itemId = request.itemId @@ -967,10 +984,14 @@ open class StockInLineService( } convertedBaseQty } + _logStep("compute_putAwayDeltaStockQty(+uom lookups per line)", _tDeltaCompute) if (putAwayDeltaStockQty > BigDecimal.ZERO) { + val _tLedger = System.nanoTime() createStockLedgerForStockIn(stockInLine, putAwayDeltaStockQty.toDouble()) + _logStep("createStockLedgerForStockIn(includes findLatestByItemId)", _tLedger) } + val _tQtyCalc = System.nanoTime() val putAwayStockQty = inventoryLotLines.sumOf { it.inQty ?: BigDecimal.ZERO } // PO-origin: acceptedQty is already STOCK qty; Non-PO: keep legacy rule (acceptQty is purchase qty) @@ -986,8 +1007,10 @@ open class StockInLineService( } (request.acceptQty ?: request.acceptedQty)?.times(ratio) ?: BigDecimal.ZERO } + _logStep("compute_putAwayStockQty_and_requiredStockQty(+uom lookups)", _tQtyCalc) if (putAwayStockQty >= requiredStockQty) { + val _tStatus = System.nanoTime() stockInLine.apply { val isWipJobOrder = stockInLine.jobOrder?.bom?.description == "WIP" this.status = if (isWipJobOrder) { @@ -1009,7 +1032,9 @@ open class StockInLineService( } jobOrderRepository.save(jo!!) } + _logStep("update_status_and_jobOrder_save", _tStatus) } + _logStep("putaway_total", _tPutAwayTotal) } else if (request.status == StockInLineStatus.PENDING.status || request.status == StockInLineStatus.ESCALATED.status) { var escLogId : Long? = 0L; // Escalation @@ -1065,17 +1090,27 @@ open class StockInLineService( saveQcResultWhenStockIn(request, stockInLine, escLogId) } + val _tSaveFlush = System.nanoTime() val savedStockInLine = saveAndFlush(stockInLine) + _logStep("saveAndFlush(stockInLine)", _tSaveFlush) // check if all line completed if (savedStockInLine.purchaseOrderLine != null) { val pol = savedStockInLine.purchaseOrderLine if (pol != null) { + val _tPol = System.nanoTime() updatePurchaseOrderLineStatus(pol) + _logStep("updatePurchaseOrderLineStatus", _tPol) + val _tPo = System.nanoTime() updatePurchaseOrderStatus(pol.purchaseOrder!!) + _logStep("updatePurchaseOrderStatus", _tPo) } } + val _tLineInfo = System.nanoTime() val lineInfo = stockInLineRepository.findStockInLineInfoByIdAndDeletedFalse(savedStockInLine.id!!) + _logStep("findStockInLineInfoByIdAndDeletedFalse", _tLineInfo) + + logger.info("[SIL.update timing] TOTAL took ${String.format("%.3f", (System.nanoTime() - _t0) / 1_000_000.0)} ms") return MessageResponse( id = savedStockInLine.id, @@ -1263,32 +1298,51 @@ open class StockInLineService( @Transactional private fun createStockLedgerForStockIn(stockInLine: StockInLine, inQty: Double) { + val _t0 = System.nanoTime() + fun _msSince(t: Long): Double = (System.nanoTime() - t) / 1_000_000.0 + fun _logStep(name: String, t: Long) { + logger.info("[SIL.ledger timing] $name took ${String.format("%.3f", _msSince(t))} ms") + } + val item = stockInLine.item ?: return + + val _tInv = System.nanoTime() val inventory = inventoryRepository.findFirstByItemIdAndDeletedIsFalseOrderByIdAsc(item.id!!) ?: return - - // ✅ 修复:查询最新的 stock_ledger 记录,基于前一笔 balance 计算 - val latestLedger = stockLedgerRepository.findLatestByItemId(item.id!!).firstOrNull() - - // ✅ 修复:如果 latestLedger 为 null(第一次 stock in),previousBalance 应该是 0 - // 因为 inventory.onHandQty 已经被触发器更新为包含本次 stock in 的数量 - val previousBalance = latestLedger?.balance ?: 0.0 - - val newBalance = previousBalance + inQty - - val stockLedger = StockLedger().apply { - this.stockInLine = stockInLine - this.inventory = inventory - this.inQty = inQty - this.outQty = null - this.balance = newBalance - this.type = stockInLine.type - this.itemId = item.id - this.itemCode = item.code - this.uomId = itemUomService.findStockUnitByItemId(item.id!!)?.uom?.id ?: inventory.uom?.id - this.date = LocalDate.now() - } + _logStep("inventoryRepository.findFirstByItemIdAndDeletedIsFalseOrderByIdAsc", _tInv) + + // ✅ 修复:查询最新的 stock_ledger 记录,基于前一笔 balance 计算 + val _tLatest = System.nanoTime() + val latestLedger = stockLedgerRepository.findFirstByItemIdAndDeletedFalseOrderByDateDescIdDesc(item.id!!) + _logStep("stockLedgerRepository.findLatestByItemId", _tLatest) + + // ✅ 修复:如果 latestLedger 为 null(第一次 stock in),previousBalance 应该是 0 + // 因为 inventory.onHandQty 已经被触发器更新为包含本次 stock in 的数量 + val previousBalance = latestLedger?.balance ?: 0.0 + + val newBalance = previousBalance + inQty + + val _tUom = System.nanoTime() + val stockUomId = itemUomService.findStockUnitByItemId(item.id!!)?.uom?.id ?: inventory.uom?.id + _logStep("itemUomService.findStockUnitByItemId", _tUom) + + val stockLedger = StockLedger().apply { + this.stockInLine = stockInLine + this.inventory = inventory + this.inQty = inQty + this.outQty = null + this.balance = newBalance + this.type = stockInLine.type + this.itemId = item.id + this.itemCode = item.code + this.uomId = stockUomId + this.date = LocalDate.now() + } + + val _tSave = System.nanoTime() + stockLedgerRepository.saveAndFlush(stockLedger) + _logStep("stockLedgerRepository.saveAndFlush", _tSave) - stockLedgerRepository.saveAndFlush(stockLedger) + logger.info("[SIL.ledger timing] TOTAL took ${String.format("%.3f", (System.nanoTime() - _t0) / 1_000_000.0)} ms") } @Transactional