| @@ -691,51 +691,30 @@ open class PickOrderService( | |||
| } else { | |||
| emptyMap() | |||
| } | |||
| // ✅ Pre-calculate available quantities for each item ONCE | |||
| val itemAvailableQtyMap = mutableMapOf<Long, BigDecimal>() | |||
| itemIds.forEach { itemId -> | |||
| val inventory = inventories[itemId] | |||
| val totalAvailableQty = inventory?.sumOf { i -> | |||
| val inQty = i.inQty ?: zero | |||
| val outQty = i.outQty ?: zero | |||
| val holdQty = i.holdQty ?: zero | |||
| inQty.minus(outQty).minus(holdQty) | |||
| } ?: zero | |||
| itemAvailableQtyMap[itemId] = totalAvailableQty | |||
| } | |||
| // Pick Orders | |||
| val releasePickOrderInfos = pos | |||
| val releasePickOrderLineInfos = pos | |||
| .map { po -> | |||
| val releasePickOrderLineInfos = po.pickOrderLines.map { pol -> | |||
| val inventory = pol.item?.id?.let { inventories[it] } | |||
| val itemUom = pol.item?.id?.let { itemUomService.findSalesUnitByItemId(it) } | |||
| val itemId = pol.item?.id | |||
| val availableQty = itemId?.let { itemAvailableQtyMap[it] } ?: zero | |||
| // ✅ Move stockOutLines declaration inside the pol loop | |||
| val stockOutLines = stockOutLinesByPickOrderLineId[pol.id] ?: emptyList() | |||
| // ✅ Fix: Convert availableQty from base units to sales units | |||
| val convertedAvailableQty = inventory?.sumOf { i -> | |||
| val inQty = i.inQty ?: zero | |||
| val outQty = i.outQty ?: zero | |||
| val holdQty = i.holdQty ?: zero | |||
| val baseQty = inQty.minus(outQty).minus(holdQty) | |||
| // ✅ NEW: Subtract completed picked quantities for this specific inventory lot line | |||
| val completedPickedQty = stockOutLines | |||
| .filter { it.status == "completed" || it.status == "COMPLETE" } | |||
| .filter { stockOutLine -> | |||
| // ✅ Check if this stock out line is for the current inventory lot line | |||
| stockOutLine.inventoryLotLineId == i.id | |||
| } | |||
| .sumOf { it.qty ?: zero } | |||
| val adjustedBaseQty = baseQty.minus(completedPickedQty) | |||
| println("Item ID: ${i.item?.id}, BaseQty: $baseQty, CompletedPicked: $completedPickedQty, Adjusted: $adjustedBaseQty") | |||
| // Apply unit conversion if needed | |||
| //val itemUom = pol.item?.id?.let { itemUomService.findSalesUnitByItemId(it) } | |||
| // val ratioN = itemUom?.ratioN | |||
| // val ratioD = itemUom?.ratioD | |||
| // val convertedQty: BigDecimal = if (ratioN != null && ratioD != null && ratioD != zero) { | |||
| // adjustedBaseQty.divide(ratioN.divide(ratioD, 10, java.math.RoundingMode.HALF_UP), 2, java.math.RoundingMode.HALF_UP) | |||
| // } else { | |||
| // adjustedBaseQty | |||
| //} | |||
| adjustedBaseQty | |||
| } | |||
| // ✅ Calculate total picked quantity from stock out lines | |||
| println("=== PICKED QTY DEBUG: Line ${pol.id} ===") | |||
| println("Stock Out Lines: ${stockOutLines.map { "${it.id}(status=${it.status}, qty=${it.qty})" }}") | |||
| @@ -754,7 +733,7 @@ open class PickOrderService( | |||
| itemId = pol.item?.id, | |||
| itemCode = pol.item?.code, | |||
| itemName = pol.item?.name, | |||
| availableQty = convertedAvailableQty, // ✅ Use adjusted available quantity | |||
| availableQty = availableQty, // ✅ Use pre-calculated value | |||
| requiredQty = pol.qty, | |||
| uomCode = pol.uom?.code, | |||
| uomDesc = pol.uom?.udfudesc, | |||
| @@ -786,8 +765,7 @@ open class PickOrderService( | |||
| item.second.let { | |||
| val convertedAvailableQty = inventory?.sumOf { i -> | |||
| val baseQty = (i.availableQty ?: zero) | |||
| baseQty | |||
| baseQty | |||
| } | |||
| it.availableQty = convertedAvailableQty | |||
| @@ -797,7 +775,7 @@ open class PickOrderService( | |||
| val consoCode = pos.firstOrNull()?.consoCode | |||
| return GetPickOrderInfoResponse( | |||
| consoCode = consoCode, | |||
| pickOrders = releasePickOrderInfos, | |||
| pickOrders = releasePickOrderLineInfos, | |||
| items = currentInventoryInfos, | |||
| ) | |||
| } | |||
| @@ -32,6 +32,7 @@ import java.time.format.DateTimeFormatter | |||
| import java.util.Optional | |||
| import kotlin.jvm.optionals.getOrNull | |||
| import com.ffii.fpsms.modules.master.web.models.MessageResponse | |||
| import com.ffii.fpsms.modules.stock.web.model.UpdateInventoryLotLineQuantitiesRequest | |||
| @Service | |||
| open class InventoryLotLineService( | |||
| private val inventoryLotLineRepository: InventoryLotLineRepository, | |||
| @@ -177,4 +178,68 @@ open class InventoryLotLineService( | |||
| "fileName" to qrCodeInfo[0].poCode | |||
| ); | |||
| } | |||
| @Transactional | |||
| open fun updateInventoryLotLineQuantities(request: UpdateInventoryLotLineQuantitiesRequest): MessageResponse { | |||
| try { | |||
| val inventoryLotLine = inventoryLotLineRepository.findById(request.inventoryLotLineId).orElseThrow() | |||
| // Handle quantity updates based on operation | |||
| var newHoldQty = inventoryLotLine.holdQty ?: BigDecimal.ZERO | |||
| var newOutQty = inventoryLotLine.outQty ?: BigDecimal.ZERO | |||
| when (request.operation) { | |||
| "pick" -> { | |||
| // Move from hold_qty to out_qty | |||
| newHoldQty = newHoldQty.minus(request.qty) | |||
| newOutQty = newOutQty.plus(request.qty) | |||
| } | |||
| "release" -> { | |||
| // Move from out_qty back to hold_qty (if needed) | |||
| newOutQty = newOutQty.minus(request.qty) | |||
| newHoldQty = newHoldQty.plus(request.qty) | |||
| } | |||
| else -> { | |||
| throw IllegalArgumentException("Unknown operation: ${request.operation}") | |||
| } | |||
| } | |||
| // Validate quantities | |||
| if (newHoldQty < BigDecimal.ZERO || newOutQty < BigDecimal.ZERO) { | |||
| throw IllegalArgumentException("Invalid quantities: holdQty=$newHoldQty, outQty=$newOutQty") | |||
| } | |||
| val updateRequest = SaveInventoryLotLineRequest( | |||
| id = inventoryLotLine.id, | |||
| inventoryLotId = inventoryLotLine.inventoryLot?.id, | |||
| warehouseId = inventoryLotLine.warehouse?.id, | |||
| stockUomId = inventoryLotLine.stockUom?.id, | |||
| inQty = inventoryLotLine.inQty, | |||
| outQty = newOutQty, | |||
| holdQty = newHoldQty, | |||
| status = inventoryLotLine.status?.value, // Keep existing status | |||
| remarks = inventoryLotLine.remarks | |||
| ) | |||
| val updatedInventoryLotLine = saveInventoryLotLine(updateRequest) | |||
| return MessageResponse( | |||
| id = updatedInventoryLotLine.id, | |||
| name = "Inventory lot line quantities updated", | |||
| code = "SUCCESS", | |||
| type = "inventory_lot_line", | |||
| message = "Updated: holdQty=${newHoldQty}, outQty=${newOutQty}, operation=${request.operation}", | |||
| errorPosition = null | |||
| ) | |||
| } catch (e: Exception) { | |||
| return MessageResponse( | |||
| id = null, | |||
| name = "Failed to update inventory lot line quantities", | |||
| code = "ERROR", | |||
| type = "inventory_lot_line", | |||
| message = "Error: ${e.message}", | |||
| errorPosition = null | |||
| ) | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,3 @@ | |||
| @@ -376,7 +376,9 @@ private fun getStockOutIdFromPickOrderLine(pickOrderLineId: Long): Long { | |||
| // Update quantity if provided | |||
| if (request.qty != null) { | |||
| stockOutLine.qty = request.qty | |||
| val currentQty = stockOutLine.qty?.toDouble() ?: 0.0 | |||
| val newQty = currentQty + request.qty | |||
| stockOutLine.qty = (newQty) | |||
| } | |||
| val savedStockOutLine = stockOutLineRepository.saveAndFlush(stockOutLine) | |||
| @@ -11,6 +11,7 @@ import com.ffii.fpsms.modules.stock.service.StockInLineService | |||
| import com.ffii.fpsms.modules.stock.web.model.ExportQrCodeRequest | |||
| import com.ffii.fpsms.modules.stock.web.model.LotLineInfo | |||
| import com.ffii.fpsms.modules.stock.web.model.SearchInventoryLotLineInfoRequest | |||
| import com.ffii.fpsms.modules.stock.web.model.UpdateInventoryLotLineQuantitiesRequest | |||
| import jakarta.servlet.http.HttpServletResponse | |||
| import jakarta.validation.Valid | |||
| import net.sf.jasperreports.engine.JasperExportManager | |||
| @@ -81,4 +82,9 @@ class InventoryLotLineController ( | |||
| fun updateInventoryLotLineStatus(@RequestBody request: UpdateInventoryLotLineStatusRequest): MessageResponse { | |||
| return inventoryLotLineService.updateInventoryLotLineStatus(request) | |||
| } | |||
| @PostMapping("/updateQuantities") | |||
| fun updateInventoryLotLineQuantities(@RequestBody request: UpdateInventoryLotLineQuantitiesRequest): MessageResponse { | |||
| return inventoryLotLineService.updateInventoryLotLineQuantities(request) | |||
| } | |||
| } | |||
| @@ -0,0 +1,9 @@ | |||
| package com.ffii.fpsms.modules.stock.web.model | |||
| import java.math.BigDecimal | |||
| data class UpdateInventoryLotLineQuantitiesRequest( | |||
| val inventoryLotLineId: Long, | |||
| val qty: BigDecimal, | |||
| val operation: String | |||
| ) | |||