| @@ -691,51 +691,30 @@ open class PickOrderService( | |||||
| } else { | } else { | ||||
| emptyMap() | 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 | // Pick Orders | ||||
| val releasePickOrderInfos = pos | |||||
| val releasePickOrderLineInfos = pos | |||||
| .map { po -> | .map { po -> | ||||
| val releasePickOrderLineInfos = po.pickOrderLines.map { pol -> | 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 | // ✅ Move stockOutLines declaration inside the pol loop | ||||
| val stockOutLines = stockOutLinesByPickOrderLineId[pol.id] ?: emptyList() | 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 | // ✅ Calculate total picked quantity from stock out lines | ||||
| println("=== PICKED QTY DEBUG: Line ${pol.id} ===") | println("=== PICKED QTY DEBUG: Line ${pol.id} ===") | ||||
| println("Stock Out Lines: ${stockOutLines.map { "${it.id}(status=${it.status}, qty=${it.qty})" }}") | 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, | itemId = pol.item?.id, | ||||
| itemCode = pol.item?.code, | itemCode = pol.item?.code, | ||||
| itemName = pol.item?.name, | itemName = pol.item?.name, | ||||
| availableQty = convertedAvailableQty, // ✅ Use adjusted available quantity | |||||
| availableQty = availableQty, // ✅ Use pre-calculated value | |||||
| requiredQty = pol.qty, | requiredQty = pol.qty, | ||||
| uomCode = pol.uom?.code, | uomCode = pol.uom?.code, | ||||
| uomDesc = pol.uom?.udfudesc, | uomDesc = pol.uom?.udfudesc, | ||||
| @@ -786,8 +765,7 @@ open class PickOrderService( | |||||
| item.second.let { | item.second.let { | ||||
| val convertedAvailableQty = inventory?.sumOf { i -> | val convertedAvailableQty = inventory?.sumOf { i -> | ||||
| val baseQty = (i.availableQty ?: zero) | val baseQty = (i.availableQty ?: zero) | ||||
| baseQty | |||||
| baseQty | |||||
| } | } | ||||
| it.availableQty = convertedAvailableQty | it.availableQty = convertedAvailableQty | ||||
| @@ -797,7 +775,7 @@ open class PickOrderService( | |||||
| val consoCode = pos.firstOrNull()?.consoCode | val consoCode = pos.firstOrNull()?.consoCode | ||||
| return GetPickOrderInfoResponse( | return GetPickOrderInfoResponse( | ||||
| consoCode = consoCode, | consoCode = consoCode, | ||||
| pickOrders = releasePickOrderInfos, | |||||
| pickOrders = releasePickOrderLineInfos, | |||||
| items = currentInventoryInfos, | items = currentInventoryInfos, | ||||
| ) | ) | ||||
| } | } | ||||
| @@ -32,6 +32,7 @@ import java.time.format.DateTimeFormatter | |||||
| import java.util.Optional | import java.util.Optional | ||||
| import kotlin.jvm.optionals.getOrNull | import kotlin.jvm.optionals.getOrNull | ||||
| import com.ffii.fpsms.modules.master.web.models.MessageResponse | import com.ffii.fpsms.modules.master.web.models.MessageResponse | ||||
| import com.ffii.fpsms.modules.stock.web.model.UpdateInventoryLotLineQuantitiesRequest | |||||
| @Service | @Service | ||||
| open class InventoryLotLineService( | open class InventoryLotLineService( | ||||
| private val inventoryLotLineRepository: InventoryLotLineRepository, | private val inventoryLotLineRepository: InventoryLotLineRepository, | ||||
| @@ -177,4 +178,68 @@ open class InventoryLotLineService( | |||||
| "fileName" to qrCodeInfo[0].poCode | "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 | // Update quantity if provided | ||||
| if (request.qty != null) { | 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) | 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.ExportQrCodeRequest | ||||
| import com.ffii.fpsms.modules.stock.web.model.LotLineInfo | 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.SearchInventoryLotLineInfoRequest | ||||
| import com.ffii.fpsms.modules.stock.web.model.UpdateInventoryLotLineQuantitiesRequest | |||||
| import jakarta.servlet.http.HttpServletResponse | import jakarta.servlet.http.HttpServletResponse | ||||
| import jakarta.validation.Valid | import jakarta.validation.Valid | ||||
| import net.sf.jasperreports.engine.JasperExportManager | import net.sf.jasperreports.engine.JasperExportManager | ||||
| @@ -81,4 +82,9 @@ class InventoryLotLineController ( | |||||
| fun updateInventoryLotLineStatus(@RequestBody request: UpdateInventoryLotLineStatusRequest): MessageResponse { | fun updateInventoryLotLineStatus(@RequestBody request: UpdateInventoryLotLineStatusRequest): MessageResponse { | ||||
| return inventoryLotLineService.updateInventoryLotLineStatus(request) | 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 | |||||
| ) | |||||