From c64250987e34bc393b5a32074856a245cc0e254f Mon Sep 17 00:00:00 2001 From: Fai Luk Date: Fri, 18 Sep 2026 10:55:18 +0800 Subject: [PATCH] align the calculation of unit with stock in line data create calculation --- .../modules/master/service/ItemUomService.kt | 25 +++++++++++++++++-- .../projections/PurchaseOrderLineInfo.kt | 3 +++ .../service/PurchaseOrderService.kt | 9 +++++++ 3 files changed, 35 insertions(+), 2 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 275b80c..1ccd30e 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 @@ -224,6 +224,27 @@ open class ItemUomService( return stockQty.setScale(0, RoundingMode.DOWN) } + /** + * Count-type stock units must be whole numbers: PCS/CTN…, 包, 箱, 件, 個, PCS, CTN, etc. + * Weight/volume units (克, 公斤, 升…) keep 2 decimals. + */ + open fun isIntegerCountStockUom(stockUnit: ItemUom?): Boolean { + if (stockUnit == null) return false + val code = stockUnit.uom?.code?.trim().orEmpty() + if (code.startsWith("PCS/CTN", ignoreCase = true)) return true + val short = stockUnit.uom?.udfShortDesc?.trim().orEmpty() + return isIntegerCountUnitToken(short) || isIntegerCountUnitToken(code) + } + + private fun isIntegerCountUnitToken(raw: String): Boolean { + val token = raw.trim() + if (token.isEmpty()) return false + if (token == "包" || token == "箱" || token == "件" || token == "個") return true + val upper = token.uppercase() + return upper == "PCS" || upper == "PC" || upper == "CTN" || upper == "EA" || + upper == "PK" || upper == "PKT" || upper == "BOX" + } + /** * Stock UOM code [PCS/CTN]* denotes pieces-per-carton; displayed stock qty should be whole cartons (integer). */ @@ -232,9 +253,9 @@ open class ItemUomService( return code.startsWith("PCS/CTN", ignoreCase = true) } - /** After ratio math: 2 decimals, or integer when stock unit is PCS/CTN… */ + /** After ratio math: integer HALF_UP for count units (包/箱/PCS…), else 2 decimals. */ private fun finalizePreciseStockQty(stockUnit: ItemUom?, stockQty: BigDecimal): BigDecimal { - if (isPiecesPerCartonStockUomCode(stockUnit)) { + if (isIntegerCountStockUom(stockUnit) || isPiecesPerCartonStockUomCode(stockUnit)) { return stockQty.setScale(0, RoundingMode.HALF_UP) } return stockQty.setScale(2, RoundingMode.HALF_UP) diff --git a/src/main/java/com/ffii/fpsms/modules/purchaseOrder/entity/projections/PurchaseOrderLineInfo.kt b/src/main/java/com/ffii/fpsms/modules/purchaseOrder/entity/projections/PurchaseOrderLineInfo.kt index 0828c75..f89fc50 100644 --- a/src/main/java/com/ffii/fpsms/modules/purchaseOrder/entity/projections/PurchaseOrderLineInfo.kt +++ b/src/main/java/com/ffii/fpsms/modules/purchaseOrder/entity/projections/PurchaseOrderLineInfo.kt @@ -63,4 +63,7 @@ data class StockUomForPoLine ( val stockRatioD: BigDecimal? = null, val purchaseRatioN: BigDecimal? = null, val purchaseRatioD: BigDecimal? = null, + val sourceRatioN: BigDecimal? = null, + val sourceRatioD: BigDecimal? = null, + val stockQtyInteger: Boolean? = null, ) \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/purchaseOrder/service/PurchaseOrderService.kt b/src/main/java/com/ffii/fpsms/modules/purchaseOrder/service/PurchaseOrderService.kt index 52a14a9..d794d58 100644 --- a/src/main/java/com/ffii/fpsms/modules/purchaseOrder/service/PurchaseOrderService.kt +++ b/src/main/java/com/ffii/fpsms/modules/purchaseOrder/service/PurchaseOrderService.kt @@ -294,6 +294,12 @@ open fun getPoSummariesByIds(ids: List): List { val itemId = thisPol.item?.id val qtyM18 = thisPol.qtyM18 val uomM18Id = thisPol.uomM18?.id + val sourceUomId = uomM18Id ?: thisPol.uom?.id + val sourceItemUom = if (itemId != null && sourceUomId != null) { + itemUomService.findFirstByItemIdAndUomId(itemId, sourceUomId) + } else { + null + } val stockQtyFromM18 = if (itemId != null && qtyM18 != null && uomM18Id != null) { itemUomService.convertQtyToStockQtyPrecise(itemId, uomM18Id, qtyM18) } else { @@ -312,6 +318,9 @@ open fun getPoSummariesByIds(ids: List): List { stockRatioD = iu?.ratioD, purchaseRatioN = purchaseUnit?.ratioN, purchaseRatioD = purchaseUnit?.ratioD, + sourceRatioN = sourceItemUom?.ratioN ?: purchaseUnit?.ratioN, + sourceRatioD = sourceItemUom?.ratioD ?: purchaseUnit?.ratioD, + stockQtyInteger = itemUomService.isIntegerCountStockUom(iu), ) } } ?: StockUomForPoLine() PoLineWithStockInLine(