|
|
|
@@ -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 |
|
|
|
|