From 46bd971612c03be03767ac34891c2131dba2f0ac Mon Sep 17 00:00:00 2001 From: "CANCERYS\\kw093" Date: Wed, 14 Jan 2026 14:06:39 +0800 Subject: [PATCH] update base unity --- .../modules/master/service/ItemUomService.kt | 37 ++++++-- .../service/ProductProcessService.kt | 92 ++++++++++--------- 2 files changed, 78 insertions(+), 51 deletions(-) diff --git a/src/main/java/com/ffii/fpsms/modules/master/service/ItemUomService.kt b/src/main/java/com/ffii/fpsms/modules/master/service/ItemUomService.kt index 5a5e1b7..3cb9979 100644 --- a/src/main/java/com/ffii/fpsms/modules/master/service/ItemUomService.kt +++ b/src/main/java/com/ffii/fpsms/modules/master/service/ItemUomService.kt @@ -68,8 +68,8 @@ open class ItemUomService( val stockUnit = findStockUnitByItemId(itemId) ?: return purchaseQty; val one = BigDecimal.ONE; - val baseQty = purchaseQty.multiply(purchaseUnit.ratioN ?: one).divide(purchaseUnit.ratioD ?: one, 2, RoundingMode.HALF_UP) - val stockQty = baseQty.multiply(stockUnit.ratioD ?: one).divide(stockUnit.ratioN ?: one, 2, RoundingMode.HALF_UP) + val baseQty = purchaseQty.multiply(purchaseUnit.ratioN ?: one).divide(purchaseUnit.ratioD ?: one, 2, RoundingMode.UP) + val stockQty = baseQty.multiply(stockUnit.ratioD ?: one).divide(stockUnit.ratioN ?: one, 2, RoundingMode.UP) return stockQty; } @@ -118,31 +118,52 @@ open class ItemUomService( open fun convertUomByItem(request: ConvertUomByItemRequest): ConvertUomByItemResponse { + // Special case: Direct conversion from KG (784) to gram (4) or vice versa + // This handles cases where ItemUom records might not exist + if (request.uomId == 784L && request.targetUnit.lowercase() == "baseunit") { + // Check if base unit is gram (id=4) + val baseUnitItemUom = findBaseUnitByItemId(request.itemId) + val baseUnitUomId = baseUnitItemUom?.uom?.id + + if (baseUnitUomId == 4L) { + // Direct conversion: 1 KG = 1000 grams + val convertedQty = request.qty.multiply(BigDecimal(1000)) + val gramUom = uomConversionService.findById(4L) + ?: throw IllegalArgumentException("UomConversion not found for id=4 (gram)") + + return ConvertUomByItemResponse( + newQty = convertedQty, + udfudesc = gramUom.udfudesc, + udfShortDesc = gramUom.udfShortDesc + ) + } + } + // Find source ItemUom by itemId and uomId val sourceItemUom = itemUomRespository.findByItemIdAndUomIdAndDeletedIsFalse(request.itemId, request.uomId) ?: throw IllegalArgumentException("Source ItemUom not found for itemId=${request.itemId}, uomId=${request.uomId}") - + // Find target ItemUom by itemId and targetUnit val targetItemUom = findTargetItemUom(request.itemId, request.targetUnit) ?: throw IllegalArgumentException("Target ItemUom not found for itemId=${request.itemId}, targetUnit=${request.targetUnit}") - + // Convert quantity using ratioN/ratioD via base unit val one = BigDecimal.ONE val sourceRatioN = sourceItemUom.ratioN ?: one val sourceRatioD = sourceItemUom.ratioD ?: one val targetRatioN = targetItemUom.ratioN ?: one val targetRatioD = targetItemUom.ratioD ?: one - + // Convert source qty to base: qty * ratioN / ratioD val baseQty = request.qty.multiply(sourceRatioN).divide(sourceRatioD, 2, RoundingMode.HALF_UP) - + // Convert base to target: baseQty * ratioD / ratioN val newQty = baseQty.multiply(targetRatioD).divide(targetRatioN, 2, RoundingMode.HALF_UP) - + // Get UomConversion from target ItemUom val uomConversion = targetItemUom.uom ?: throw IllegalArgumentException("Target UomConversion not found for target ItemUom") - + return ConvertUomByItemResponse( newQty = newQty, udfudesc = uomConversion.udfudesc, diff --git a/src/main/java/com/ffii/fpsms/modules/productProcess/service/ProductProcessService.kt b/src/main/java/com/ffii/fpsms/modules/productProcess/service/ProductProcessService.kt index b71cbc0..1a5a30b 100644 --- a/src/main/java/com/ffii/fpsms/modules/productProcess/service/ProductProcessService.kt +++ b/src/main/java/com/ffii/fpsms/modules/productProcess/service/ProductProcessService.kt @@ -501,28 +501,41 @@ open class ProductProcessService( val itemIds = bomMaterials.mapNotNull { it.item?.id } // calculate each item's available stock - val stockQtyMap = bomMaterials.mapNotNull { material -> - val itemId = material.item?.id ?: return@mapNotNull null - val availableLots = inventoryLotLineRepository - .findAllByInventoryLotItemIdAndStatus(itemId, InventoryLotLineStatus.AVAILABLE) - val stockQty = availableLots.sumOf { lot -> - (lot.inQty ?: BigDecimal.ZERO) - .minus(lot.outQty ?: BigDecimal.ZERO) - .minus(lot.holdQty ?: BigDecimal.ZERO) - } - // Get the stockUom from the first available lot line - // stockItemUomId is the ItemUom id, we need to get the UomConversion id from it - val stockItemUom = availableLots.firstOrNull()?.stockUom - if (stockItemUom == null) { - println("WARNING: No stockUom found for itemId=$itemId in available lots") - } - val stockUomId = stockItemUom?.uom?.id - if (stockUomId == null && stockItemUom != null) { - println("WARNING: stockItemUom.id=${stockItemUom.id} exists but has no uom (UomConversion)") - } - println("DEBUG itemId=$itemId: stockItemUom.id=${stockItemUom?.id}, stockUom.uom.id=$stockUomId, availableLots count=${availableLots.size}") - itemId to Pair(stockQty, stockUomId) - }.toMap() + // Around line 504-524, update the stockQtyMap calculation +val stockQtyMap = bomMaterials.mapNotNull { material -> + val itemId = material.item?.id ?: return@mapNotNull null + val availableLots = inventoryLotLineRepository + .findAllByInventoryLotItemIdAndStatus(itemId, InventoryLotLineStatus.AVAILABLE) + val stockQty = availableLots.sumOf { lot -> + (lot.inQty ?: BigDecimal.ZERO) + .minus(lot.outQty ?: BigDecimal.ZERO) + .minus(lot.holdQty ?: BigDecimal.ZERO) + } + // Get the stockUom from the first available lot line + // stockItemUomId is the ItemUom id, we need to get the UomConversion id from it + val stockItemUom = availableLots.firstOrNull()?.stockUom + var stockUomId = stockItemUom?.uom?.id + + // Fallback: If no lots exist, get stockUom from item's stockUnit ItemUom + if (stockUomId == null && availableLots.isEmpty()) { + val stockUnitItemUom = itemUomService.findStockUnitByItemId(itemId) + stockUomId = stockUnitItemUom?.uom?.id + if (stockUomId != null) { + println("DEBUG itemId=$itemId: Using fallback stockUnit ItemUom - stockUom.uom.id=$stockUomId") + } else { + println("WARNING: No stockUom found for itemId=$itemId in available lots and no stockUnit ItemUom configured") + } + } else { + if (stockItemUom == null) { + println("WARNING: No stockUom found for itemId=$itemId in available lots") + } + if (stockUomId == null && stockItemUom != null) { + println("WARNING: stockItemUom.id=${stockItemUom.id} exists but has no uom (UomConversion)") + } + } + println("DEBUG itemId=$itemId: stockItemUom.id=${stockItemUom?.id}, stockUom.uom.id=$stockUomId, availableLots count=${availableLots.size}") + itemId to Pair(stockQty, stockUomId) +}.toMap() // calculate statistics val totalStockQty = stockQtyMap.values.sumOf { (qty, _) -> qty.toInt() } @@ -690,28 +703,10 @@ val sufficientStockQty = bomMaterials println("Converting reqQty: ${line.reqQty} from UOM id=$reqUomId to baseUnit") println(" Source ItemUom - ratioN: ${sourceItemUom?.ratioN}, ratioD: ${sourceItemUom?.ratioD}") println(" Target ItemUom (baseUnit) - ratioN: ${targetItemUom?.ratioN}, ratioD: ${targetItemUom?.ratioD}") + println(" Base Unit UOM id: ${targetItemUom?.uom?.id}") - if (sourceItemUom == null) { - println("WARNING: No ItemUom found for itemId=$itemId, uomId=$reqUomId (UomConversion id). Cannot convert reqQty.") - println(" This means the item doesn't have this UOM configured in item_uom table.") - println(" Calculation: Cannot calculate - missing Source ItemUom with ratioN and ratioD") - null - } else if (targetItemUom == null) { - println("WARNING: No baseUnit ItemUom found for itemId=$itemId. Cannot convert reqQty.") - println(" Calculation: Cannot calculate - missing Target ItemUom (baseUnit)") - null - } else { - val sourceRatioN = sourceItemUom.ratioN ?: BigDecimal.ONE - val sourceRatioD = sourceItemUom.ratioD ?: BigDecimal.ONE - val targetRatioN = targetItemUom.ratioN ?: BigDecimal.ONE - val targetRatioD = targetItemUom.ratioD ?: BigDecimal.ONE - - val baseQty = (line.reqQty ?: BigDecimal.ZERO).multiply(sourceRatioN).divide(sourceRatioD, 2, RoundingMode.HALF_UP) - val finalQty = baseQty.multiply(targetRatioD).divide(targetRatioN, 2, RoundingMode.HALF_UP) - - println(" Calculation: baseQty = ${line.reqQty} * $sourceRatioN / $sourceRatioD = $baseQty") - println(" Calculation: finalQty = $baseQty * $targetRatioD / $targetRatioN = $finalQty") - + // Try conversion first - it may have special case handling + try { val result = itemUomService.convertUomByItem( ConvertUomByItemRequest( itemId = itemId, @@ -722,6 +717,17 @@ val sufficientStockQty = bomMaterials ) println("Converted reqQty result: ${result.newQty} in base UOM: ${result.udfudesc}") result + } catch (e: IllegalArgumentException) { + // If conversion fails, check if it's because sourceItemUom is missing + if (sourceItemUom == null) { + println("WARNING: No ItemUom found for itemId=$itemId, uomId=$reqUomId (UomConversion id). Cannot convert reqQty.") + println(" This means the item doesn't have this UOM configured in item_uom table.") + println(" Calculation: Cannot calculate - missing Source ItemUom with ratioN and ratioD") + null + } else { + // Re-throw if it's a different error + throw e + } } } catch (e: Exception) { println("Error converting reqQty: ${e.message}")