Parcourir la source

updated job order relate

master
CANCERYS\kw093 il y a 1 jour
Parent
révision
87380663f0
4 fichiers modifiés avec 123 ajouts et 137 suppressions
  1. +5
    -4
      src/main/java/com/ffii/fpsms/modules/jobOrder/service/JobOrderBomMaterialService.kt
  2. +36
    -2
      src/main/java/com/ffii/fpsms/modules/jobOrder/service/JobOrderService.kt
  3. +2
    -2
      src/main/java/com/ffii/fpsms/modules/productProcess/entity/projections/ProductProcessInfo.kt
  4. +80
    -129
      src/main/java/com/ffii/fpsms/modules/productProcess/service/ProductProcessService.kt

+ 5
- 4
src/main/java/com/ffii/fpsms/modules/jobOrder/service/JobOrderBomMaterialService.kt Voir le fichier

@@ -28,13 +28,14 @@ open class JobOrderBomMaterialService(
val proportion = (jo.reqQty ?: zero).divide(jo.bom?.outputQty ?: BigDecimal.ONE, 5, RoundingMode.HALF_UP)

val jobmRequests = jo.bom?.bomMaterials?.map { bm ->
val salesUnit = bm.item?.id?.let { itemUomService.findSalesUnitByItemId(it) }
// val salesUnit = bm.item?.id?.let { itemUomService.findSalesUnitByItemId(it) }
val stockUnit = bm.item?.id?.let { itemUomService.findStockUnitByItemId(it) }
CreateJobOrderBomMaterialRequest(
joId = joId,
itemId = bm.item?.id,
reqQty = (bm.qty?.times(proportion) ?: zero).setScale(0,RoundingMode.CEILING),
uomId = salesUnit?.uom?.id
//reqQty = (bm.qty?.times(proportion) ?: zero).setScale(0,RoundingMode.CEILING),
reqQty = (bm.saleQty?.times(proportion) ?: zero).setScale(0, RoundingMode.CEILING),
uomId = stockUnit?.uom?.id
)
} ?: listOf()



+ 36
- 2
src/main/java/com/ffii/fpsms/modules/jobOrder/service/JobOrderService.kt Voir le fichier

@@ -66,6 +66,8 @@ import com.ffii.fpsms.modules.productProcess.entity.ProductProcessRepository
import java.math.RoundingMode
import java.time.LocalDate
import java.time.LocalDateTime
import com.ffii.fpsms.modules.master.entity.BomMaterialRepository
import com.ffii.fpsms.modules.master.service.ItemUomService
@Service
open class JobOrderService(
val jobOrderRepository: JobOrderRepository,
@@ -85,7 +87,9 @@ open class JobOrderService(
val inventoryRepository: InventoryRepository,
val stockInLineRepository: StockInLineRepository,
val productProcessRepository: ProductProcessRepository,
val jobOrderBomMaterialRepository: JobOrderBomMaterialRepository
val jobOrderBomMaterialRepository: JobOrderBomMaterialRepository,
val bomMaterialRepository: BomMaterialRepository,
val itemUomService: ItemUomService
) {

open fun allJobOrdersByPage(request: SearchJobOrderInfoRequest): RecordsRes<JobOrderInfo> {
@@ -418,7 +422,8 @@ open class JobOrderService(
status = JobOrderStatus.PENDING
}
jobOrderRepository.save(jo)
val bom = jo.bom
/*
val pols = jo.jobms.filter {
it.item?.type != "CMB" &&
it.item?.type != "consumables" &&
@@ -431,6 +436,35 @@ open class JobOrderService(
uomId = it.uom?.id,
)
}
*/
val pols = jo.jobms.filter { jobm ->
val itemType = jobm.item?.type?.lowercase()
itemType != "cmb" &&
itemType != "consumables" &&
itemType != "consumable" &&
itemType != "nm" &&
jobm.item?.isFee != true
}.map { jobm ->
val itemId = jobm.item?.id ?: 0L
// ✅ 从 BOM Material 获取 saleQty 和 salesUnit(库存单位)
val bomMaterial = bom?.id?.let { bomId ->
bomMaterialRepository.findByBomIdAndItemId(bomId, itemId)
}
// ✅ 使用 stockReqQty (bomMaterial.saleQty) 和 stockUom (bomMaterial.salesUnit)
val stockReqQty = jobm.reqQty ?: bomMaterial?.saleQty ?: BigDecimal.ZERO
val stockUomId = bomMaterial?.salesUnit?.id
?: itemUomService.findStockUnitByItemId(itemId)?.uom?.id // Fallback: 从 Item 获取库存单位
?: jobm.uom?.id // 最后的 fallback
SavePickOrderLineRequest(
itemId = itemId,
qty = stockReqQty, // ✅ 使用库存单位数量
uomId = stockUomId, // ✅ 使用库存单位
)
}
val po = SavePickOrderRequest(
joId = jo.id,
type = PickOrderType.JOB_ORDER,


+ 2
- 2
src/main/java/com/ffii/fpsms/modules/productProcess/entity/projections/ProductProcessInfo.kt Voir le fichier

@@ -96,12 +96,12 @@ data class jobOrderLineInfo(
val itemName: String?,
val type: String?,

val reqQty: Int?,
val reqQty: Double?,
val baseReqQty: Int?,

val stockReqQty: Int?,

val stockQty: Int?,
val stockReqQty: Double?,
val baseStockQty: Int?,

val reqUom: String?,


+ 80
- 129
src/main/java/com/ffii/fpsms/modules/productProcess/service/ProductProcessService.kt Voir le fichier

@@ -685,94 +685,90 @@ val sufficientStockQty = bomMaterials
bomMaterialRepository.findByBomIdAndItemId(bomId, itemId)
}
// Get req UOM from bomMaterial (or fallback to line.uom from JobOrderBomMaterial)
val reqUomId = bomMaterial?.uom?.id ?: line.uom?.id ?: 0L
// ✅ 使用 JobOrderBomMaterial.reqQty(已按比例调整)
// line.reqQty = bm.saleQty * proportion(库存单位,已按比例调整)
val actualReqQty = line.reqQty ?: BigDecimal.ZERO
// ✅ 获取 req UOM - 对于 reqQty,使用 bomMaterial.uom(BOM 的 UOM)
// ✅ 对于 stockReqQty,使用 line.uom(库存单位,已按比例调整)
val reqUomId = bomMaterial?.uom?.id ?: line.uom?.id ?: 0L // BOM 的 UOM
val stockReqUomId = line.uom?.id ?: bomMaterial?.salesUnit?.id ?: 0L // 库存单位 UOM
val reqUom = reqUomId.takeIf { it > 0 }?.let { uomConversionRepository.findByIdAndDeletedFalse(it) }
val uomName = reqUom?.udfudesc
val shortUom = reqUom?.udfShortDesc
val stockReqUom = stockReqUomId.takeIf { it > 0 }?.let { uomConversionRepository.findByIdAndDeletedFalse(it) }
val stockUomName = stockReqUom?.udfudesc
val stockUom = stockUomId?.takeIf { it > 0 }?.let { uomConversionRepository.findByIdAndDeletedFalse(it) }
val stockUomName = stockUom?.udfudesc
val stockUomNameForStock = stockUom?.udfudesc
println("=== Quantity Calculation for Item: ${line.item?.code} (id=$itemId) ===")
println("Original reqQty: ${line.reqQty} in UOM: $uomName (id=$reqUomId)")
println("Original stockQty: $stockQtyValue in UOM: $stockUomName (id=$stockUomId)")
println("JobOrderBomMaterial reqQty (adjusted, stock unit): $actualReqQty in UOM: ${stockReqUom?.udfudesc} (id=$stockReqUomId)")
println("BomMaterial qty (base): ${bomMaterial?.qty}, saleQty (base): ${bomMaterial?.saleQty}")
println("Original stockQty: $stockQtyValue in UOM: $stockUomNameForStock (id=$stockUomId)")
// Convert reqQty to base unit
val baseReqQtyResult = if (reqUomId > 0) {
val jobOrder = jobOrderRepository.findById(joid).orElse(null)
val jobOrderReqQty = jobOrder?.reqQty
val bomOutputQty = jobOrder?.bom?.outputQty
val proportion = if (jobOrderReqQty != null && bomOutputQty != null && bomOutputQty > BigDecimal.ZERO) {
jobOrderReqQty.divide(bomOutputQty, 5, RoundingMode.HALF_UP)
} else {
BigDecimal.ONE
}

// ✅ reqQty 使用 bomMaterial.qty * proportion(BOM 单位)
val reqQtyInBomUnit = (bomMaterial?.qty?.times(proportion) ?: BigDecimal.ZERO)
// ✅ stockReqQty 使用 bomMaterial.saleQty * proportion(库存单位,已按比例调整)
val stockReqQtyInStockUnit = (bomMaterial?.saleQty?.times(proportion) ?: BigDecimal.ZERO)
// ✅ Convert reqQty (BOM unit) to base unit
val baseReqQtyResult = if (reqUomId > 0 && reqQtyInBomUnit > BigDecimal.ZERO) {
try {
// First check if ItemUom exists for this item and UomConversion
val sourceItemUom = itemUomRepository.findFirstByItemIdAndUomIdAndDeletedIsFalse(itemId, reqUomId)
val targetItemUom = itemUomService.findBaseUnitByItemId(itemId)
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}")
println("Converting reqQty (BOM unit): $reqQtyInBomUnit from UOM id=$reqUomId to baseUnit")
// Try conversion first - it may have special case handling
try {
val result = itemUomService.convertUomByItem(
ConvertUomByItemRequest(
itemId = itemId,
qty = line.reqQty ?: BigDecimal.ZERO,
qty = reqQtyInBomUnit, // BOM 单位数量
uomId = reqUomId,
targetUnit = "baseUnit"
)
)
println("Converted reqQty result: ${result.newQty} in base UOM: ${result.udfudesc}")
println("Converted baseReqQty 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")
println("WARNING: No ItemUom found for itemId=$itemId, uomId=$reqUomId. Cannot convert baseReqQty.")
null
} else {
// Re-throw if it's a different error
throw e
}
}
} catch (e: Exception) {
println("Error converting reqQty: ${e.message}")
println("Error converting baseReqQty: ${e.message}")
e.printStackTrace()
null
}
} else {
println("reqUomId is 0 or invalid, skipping reqQty conversion")
println("reqUomId is 0 or reqQtyInBomUnit is 0, skipping baseReqQty conversion")
null
}
val baseReqQty = baseReqQtyResult?.newQty?.toInt() ?: 0
// Get base unit UOM from the base unit ItemUom (same for both req and stock)
val baseUnitItemUom = itemUomService.findBaseUnitByItemId(itemId)
val baseUomName = baseUnitItemUom?.uom?.udfudesc ?: baseReqQtyResult?.udfudesc
val baseShortUom = baseUnitItemUom?.uom?.udfShortDesc ?: baseReqQtyResult?.udfShortDesc
// Convert stockQty to base unit (using stockUomId from inventory lot lines)
// Convert stockQty to base unit
val baseStockQtyResult = if (stockUomId != null && stockUomId > 0) {
try {
// Get source and target ItemUom to show ratioN/ratioD
val sourceItemUom = itemUomRepository.findFirstByItemIdAndUomIdAndDeletedIsFalse(itemId, stockUomId)
val targetItemUom = itemUomService.findBaseUnitByItemId(itemId)
println("Converting stockQty: $stockQtyValue from UOM id=$stockUomId to baseUnit")
println(" Source ItemUom - ratioN: ${sourceItemUom?.ratioN}, ratioD: ${sourceItemUom?.ratioD}")
println(" Target ItemUom (baseUnit) - ratioN: ${targetItemUom?.ratioN}, ratioD: ${targetItemUom?.ratioD}")
if (sourceItemUom != null && targetItemUom != null) {
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 = stockQtyValue.multiply(sourceRatioN).divide(sourceRatioD, 2, RoundingMode.UP)
val finalQty = baseQty.multiply(targetRatioD).divide(targetRatioN, 2, RoundingMode.UP)
println(" Calculation: baseQty = $stockQtyValue * $sourceRatioN / $sourceRatioD = $baseQty")
println(" Calculation: finalQty = $baseQty * $targetRatioD / $targetRatioN = $finalQty")
}
val result = itemUomService.convertUomByItem(
ConvertUomByItemRequest(
itemId = itemId,
@@ -781,99 +777,43 @@ val sufficientStockQty = bomMaterials
targetUnit = "baseUnit"
)
)
println("Converted stockQty result: ${result.newQty} in base UOM: ${result.udfudesc}")
result
} catch (e: Exception) {
println("Error converting stockQty: ${e.message}")
e.printStackTrace()
null
}
} else {
println("stockUomId is null or invalid, skipping stockQty conversion")
null
}
val baseStockQty = baseStockQtyResult?.newQty?.toInt() ?: 0
// Use the same baseUomName for stockBaseUom since it's the same base unit
val baseStockUomName = baseUomName
val baseStockShortUom = baseShortUom
println("Final values - reqQty: ${line.reqQty?.toInt()}, baseReqQty: $baseReqQty, stockQty: $stockQty, baseStockQty: $baseStockQty")

val reqStockQtyResult = if (reqUomId > 0 && stockUomId != null && stockUomId > 0 && reqUomId != stockUomId) {
// ✅ 计算 baseStockReqQty(stockReqQty 转换为 base unit)
val baseStockReqQtyResult = if (stockReqUomId > 0 && stockReqQtyInStockUnit > BigDecimal.ZERO) {
try {
// Convert reqQty from reqUomId to stockUomId via baseUnit
// First convert to baseUnit
val baseReqQtyForStock = if (baseReqQtyResult != null) {
baseReqQtyResult.newQty
} else {
// If baseReqQty conversion failed, try again
try {
itemUomService.convertUomByItem(
ConvertUomByItemRequest(
itemId = itemId,
qty = line.reqQty ?: BigDecimal.ZERO,
uomId = reqUomId,
targetUnit = "baseUnit"
)
).newQty
} catch (e: Exception) {
println("Error converting reqQty to baseUnit for stock conversion: ${e.message}")
null
}
}
if (baseReqQtyForStock != null) {
// Now convert from baseUnit to stockUomId
val sourceItemUom = itemUomService.findBaseUnitByItemId(itemId)
val targetItemUom = itemUomRepository.findFirstByItemIdAndUomIdAndDeletedIsFalse(itemId, stockUomId)
if (sourceItemUom != null && targetItemUom != null) {
val sourceRatioN = sourceItemUom.ratioN ?: BigDecimal.ONE
val sourceRatioD = sourceItemUom.ratioD ?: BigDecimal.ONE
val targetRatioN = targetItemUom.ratioN ?: BigDecimal.ONE
val targetRatioD = targetItemUom.ratioD ?: BigDecimal.ONE
// Convert base to stock: baseQty * targetRatioD / targetRatioN
val stockQtyDecimal = baseReqQtyForStock.multiply(targetRatioD).divide(targetRatioN, 2, RoundingMode.UP)
val stockQty = stockQtyDecimal.setScale(0, RoundingMode.UP) // Round up to integer
val stockUom = uomConversionRepository.findByIdAndDeletedFalse(stockUomId)
println("Converting reqQty to stock unit: ${line.reqQty} (UOM id=$reqUomId) -> $stockQty (UOM id=$stockUomId)")
ConvertUomByItemResponse(
newQty = stockQty,
udfudesc = stockUom?.udfudesc,
udfShortDesc = stockUom?.udfShortDesc
)
} else {
println("WARNING: Cannot convert reqQty to stock unit - missing ItemUom (sourceItemUom=${sourceItemUom != null}, targetItemUom=${targetItemUom != null})")
null
}
} else {
null
}
val result = itemUomService.convertUomByItem(
ConvertUomByItemRequest(
itemId = itemId,
qty = stockReqQtyInStockUnit, // 库存单位数量
uomId = stockReqUomId,
targetUnit = "baseUnit"
)
)
result
} catch (e: Exception) {
println("Error converting reqQty to stock unit: ${e.message}")
e.printStackTrace()
println("Error converting stockReqQty to base unit: ${e.message}")
null
}
} else if (reqUomId > 0 && stockUomId != null && stockUomId > 0 && reqUomId == stockUomId) {
// If reqUomId and stockUomId are the same, no conversion needed
val stockUom = uomConversionRepository.findByIdAndDeletedFalse(stockUomId)
ConvertUomByItemResponse(
newQty = line.reqQty ?: BigDecimal.ZERO,
udfudesc = stockUom?.udfudesc,
udfShortDesc = stockUom?.udfShortDesc
)
} else {
println("Cannot convert reqQty to stock unit - reqUomId=$reqUomId, stockUomId=$stockUomId")
null
}
val reqStockQty = reqStockQtyResult?.newQty?.toInt() ?: 0
val baseStockReqQty = baseStockReqQtyResult?.newQty?.toInt() ?: 0
println("Final values - reqQty (BOM unit): $reqQtyInBomUnit, stockReqQty (stock unit): $stockReqQtyInStockUnit, baseReqQty: $baseReqQty, baseStockReqQty: $baseStockReqQty, stockQty: $stockQty, baseStockQty: $baseStockQty")
println("Final values - reqQty: ${line.reqQty?.toInt()}, baseReqQty: $baseReqQty, stockQty: $stockQty, baseStockQty: $baseStockQty, reqStockQty: $reqStockQty")
// Find BomProcessMaterial
val bomProcessMaterial = bomMaterial?.id?.let { bomMaterialId ->
bomProcessIds.firstNotNullOfOrNull { bomProcessId ->
@@ -882,34 +822,45 @@ val sufficientStockQty = bomMaterials
}
// Calculate availableStatus using base quantities
val availableStatus = if (baseStockQty >= baseReqQty) {
val availableStatus = if (baseStockQty >= baseStockReqQty) {
"available"
} else {
"insufficient"
}
println("Available status: $availableStatus (baseStockQty=$baseStockQty >= baseReqQty=$baseReqQty: ${baseStockQty >= baseReqQty})")
println("Available status: $availableStatus (baseStockQty=$baseStockQty >= baseStockReqQty=$baseStockReqQty)")
println("=== End Quantity Calculation ===\n")
val decimalUomIds = setOf(784L, 786L) // 需要保留小数的 UOM ID(如千克)
jobOrderLineInfo(
id = line.id ?: 0,
itemId = itemId,
itemCode = line.item?.code ?: "",
itemName = line.item?.name ?: "",

reqQty = line.reqQty?.toInt() ?: 0,
baseReqQty = baseReqQty,
// Add this field if not exists
// ✅ reqQty:使用 bomMaterial.qty * proportion(BOM 单位,已按比例调整)
reqQty = if (reqUomId in decimalUomIds) {
reqQtyInBomUnit.toDouble()
} else {
reqQtyInBomUnit.setScale(0, RoundingMode.HALF_UP).toDouble()
},
baseReqQty = baseReqQty,
stockQty = stockQty,
stockReqQty = reqStockQty,
baseStockQty = baseStockQty, // Add this field if not exists
// ✅ stockReqQty:使用 bomMaterial.saleQty * proportion(库存单位,已按比例调整)
stockReqQty = if (stockReqUomId in decimalUomIds) {
stockReqQtyInStockUnit.toDouble()
} else {
stockReqQtyInStockUnit.setScale(0, RoundingMode.HALF_UP).toDouble()
},
baseStockQty = baseStockQty,
reqUom = uomName ?: "",
reqBaseUom = baseUomName ?: "",
stockUom = stockUomName ?: "",
stockBaseUom = baseUomName ?: "",


type = line.item?.type ?: "",
availableStatus = availableStatus,
bomProcessId = bomProcessMaterial?.bomProcess?.id ?: 0,


Chargement…
Annuler
Enregistrer