From 0eeacf5c165faf67ce04f9214c9b505c4753b1b6 Mon Sep 17 00:00:00 2001 From: "cyril.tsui" Date: Wed, 30 Jul 2025 17:48:14 +0800 Subject: [PATCH] [Job Order] Can manual create job order now --- .../jobOrder/entity/JobOrderRepository.kt | 7 +++-- .../entity/projections/JobOrderInfo.kt | 6 ++-- .../service/JobOrderBomMaterialService.kt | 30 ++++++++++++++++++- .../jobOrder/service/JobOrderService.kt | 9 +++--- .../jobOrder/web/JobOrderController.kt | 14 +++++++++ .../web/model/CreateJobOrderRequest.kt | 4 +-- .../master/entity/BomMaterialRepository.kt | 2 ++ .../modules/master/entity/BomRepository.kt | 3 ++ .../master/entity/projections/BomCombo.kt | 11 +++++++ .../master/service/BomMaterialService.kt | 5 ++++ .../fpsms/modules/master/web/BomController.kt | 9 +++++- 11 files changed, 87 insertions(+), 13 deletions(-) create mode 100644 src/main/java/com/ffii/fpsms/modules/master/entity/projections/BomCombo.kt diff --git a/src/main/java/com/ffii/fpsms/modules/jobOrder/entity/JobOrderRepository.kt b/src/main/java/com/ffii/fpsms/modules/jobOrder/entity/JobOrderRepository.kt index a3f2c99..feaa85f 100644 --- a/src/main/java/com/ffii/fpsms/modules/jobOrder/entity/JobOrderRepository.kt +++ b/src/main/java/com/ffii/fpsms/modules/jobOrder/entity/JobOrderRepository.kt @@ -25,7 +25,8 @@ interface JobOrderRepository : AbstractRepository { jo.code, b.name, jo.reqQty, - b.outputQtyUom, + -- b.outputQtyUom, + uc2.udfudesc as uom, json_arrayagg( json_object( 'id', jobm.id, @@ -40,6 +41,8 @@ interface JobOrderRepository : AbstractRepository { jo.status from job_order jo left join bom b on b.id = jo.bomId + left join item_uom iu on b.itemId = iu.itemId and iu.salesUnit = true + left join uom_conversion uc2 on uc2.id = iu.uomId left join job_order_bom_material jobm on jo.id = jobm.jobOrderId left join items i on i.id = jobm.itemId left join uom_conversion uc on uc.id = jobm.uomId @@ -47,7 +50,7 @@ interface JobOrderRepository : AbstractRepository { left join inventory_lot_line ill on ill.id = sol.inventoryLotLineId left join inventory_lot il on il.id = ill.inventoryLotId where jo.id = :id - group by jo.id + group by jo.id, uc2.udfudesc limit 1 """ ) diff --git a/src/main/java/com/ffii/fpsms/modules/jobOrder/entity/projections/JobOrderInfo.kt b/src/main/java/com/ffii/fpsms/modules/jobOrder/entity/projections/JobOrderInfo.kt index e05b09a..143e5f4 100644 --- a/src/main/java/com/ffii/fpsms/modules/jobOrder/entity/projections/JobOrderInfo.kt +++ b/src/main/java/com/ffii/fpsms/modules/jobOrder/entity/projections/JobOrderInfo.kt @@ -12,7 +12,7 @@ interface JobOrderInfo { val name: String; val reqQty: BigDecimal; - @get:Value("#{target.bom.outputQtyUom}") + @get:Value("#{target.bom.item.itemUoms.^[salesUnit == true && deleted == false]?.uom.udfudesc}") val uom: String; val status: String; } @@ -23,7 +23,7 @@ interface JobOrderDetailWithJsonString { val code: String?; val name: String?; val reqQty: BigDecimal?; - val outputQtyUom: String?; + val uom: String?; val pickLines: String?; val status: String?; } @@ -33,7 +33,7 @@ data class JobOrderDetail( val code: String?, val name: String?, val reqQty: BigDecimal?, - val outputQtyUom: String?, + val uom: String?, val pickLines: List?, val status: String? ) diff --git a/src/main/java/com/ffii/fpsms/modules/jobOrder/service/JobOrderBomMaterialService.kt b/src/main/java/com/ffii/fpsms/modules/jobOrder/service/JobOrderBomMaterialService.kt index f72b8f9..9265e68 100644 --- a/src/main/java/com/ffii/fpsms/modules/jobOrder/service/JobOrderBomMaterialService.kt +++ b/src/main/java/com/ffii/fpsms/modules/jobOrder/service/JobOrderBomMaterialService.kt @@ -6,16 +6,40 @@ import com.ffii.fpsms.modules.jobOrder.entity.JobOrderRepository import com.ffii.fpsms.modules.jobOrder.web.model.CreateJobOrderBomMaterialRequest import com.ffii.fpsms.modules.master.entity.ItemsRepository import com.ffii.fpsms.modules.master.entity.UomConversionRepository +import com.ffii.fpsms.modules.master.service.ItemUomService import com.ffii.fpsms.modules.master.web.models.MessageResponse import org.springframework.stereotype.Service +import java.math.BigDecimal +import java.math.RoundingMode import kotlin.jvm.optionals.getOrNull @Service open class JobOrderBomMaterialService( - val jobOrderBomMaterialRepository: JobOrderBomMaterialRepository, private val jobOrderRepository: JobOrderRepository, + val jobOrderBomMaterialRepository: JobOrderBomMaterialRepository, + private val jobOrderRepository: JobOrderRepository, private val itemsRepository: ItemsRepository, + val itemUomService: ItemUomService, private val uomConversionRepository: UomConversionRepository ) { + open fun createJobOrderBomMaterialRequests(joId: Long): List { + val zero = BigDecimal.ZERO + val jo = jobOrderRepository.findById(joId).getOrNull() ?: throw NoSuchElementException() + 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) } + + CreateJobOrderBomMaterialRequest( + joId = joId, + itemId = bm.item?.id, + reqQty = bm.qty?.times(proportion) ?: zero, + uomId = salesUnit?.uom?.id + ) + } ?: listOf() + + return jobmRequests + } + fun createJobOrderBomMaterials(request: List): MessageResponse { val joBomMaterials = request.map { req -> val jo = req.joId?.let { jobOrderRepository.findById(it).getOrNull() } @@ -42,4 +66,8 @@ open class JobOrderBomMaterialService( errorPosition = null ) } + + fun createJobOrderBomMaterialsByJoId(joId: Long): MessageResponse { + return createJobOrderBomMaterials(createJobOrderBomMaterialRequests(joId)) + } } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/jobOrder/service/JobOrderService.kt b/src/main/java/com/ffii/fpsms/modules/jobOrder/service/JobOrderService.kt index 2d83482..c91459e 100644 --- a/src/main/java/com/ffii/fpsms/modules/jobOrder/service/JobOrderService.kt +++ b/src/main/java/com/ffii/fpsms/modules/jobOrder/service/JobOrderService.kt @@ -2,6 +2,7 @@ package com.ffii.fpsms.modules.jobOrder.service import com.ffii.core.response.RecordsRes import com.ffii.core.utils.GsonUtils +import com.ffii.fpsms.modules.common.SecurityUtils import com.ffii.fpsms.modules.jobOrder.entity.JobOrder import com.ffii.fpsms.modules.jobOrder.entity.JobOrderRepository import com.ffii.fpsms.modules.jobOrder.entity.projections.JobOrderDetail @@ -61,7 +62,7 @@ open class JobOrderService( code = sqlResult.code, name = sqlResult.name, reqQty = sqlResult.reqQty, - outputQtyUom = sqlResult.outputQtyUom, + uom = sqlResult.uom, pickLines = jsonResult, status = sqlResult.status ) @@ -90,7 +91,7 @@ open class JobOrderService( open fun createJobOrder(request: CreateJobOrderRequest): MessageResponse { val jo = JobOrder() val bom = request.bomId?.let { bomService.findById(it) } - val approver = request.approverId?.let { userService.find(it).getOrNull() } + val approver = request.approverId?.let { userService.find(it).getOrNull() } ?: SecurityUtils.getUser().getOrNull() val prodScheduleLine = request.prodScheduleLineId?.let { productionScheduleLineRepository.findById(it).getOrNull() } val code = assignJobNo() @@ -98,8 +99,8 @@ open class JobOrderService( this.code = code this.bom = bom //TODO: planStart & planEnd - planStart = LocalDateTime.now() - planEnd = LocalDateTime.now() + planStart = request.planStart ?: LocalDateTime.now() + planEnd = request.planEnd ?: LocalDateTime.now() reqQty = request.reqQty status = request.status type = request.type diff --git a/src/main/java/com/ffii/fpsms/modules/jobOrder/web/JobOrderController.kt b/src/main/java/com/ffii/fpsms/modules/jobOrder/web/JobOrderController.kt index 52caea5..fd254bb 100644 --- a/src/main/java/com/ffii/fpsms/modules/jobOrder/web/JobOrderController.kt +++ b/src/main/java/com/ffii/fpsms/modules/jobOrder/web/JobOrderController.kt @@ -3,7 +3,9 @@ package com.ffii.fpsms.modules.jobOrder.web import com.ffii.core.response.RecordsRes import com.ffii.fpsms.modules.jobOrder.entity.projections.JobOrderDetail import com.ffii.fpsms.modules.jobOrder.entity.projections.JobOrderInfo +import com.ffii.fpsms.modules.jobOrder.service.JobOrderBomMaterialService import com.ffii.fpsms.modules.jobOrder.service.JobOrderService +import com.ffii.fpsms.modules.jobOrder.web.model.CreateJobOrderRequest import com.ffii.fpsms.modules.jobOrder.web.model.JobOrderReleaseRequest import com.ffii.fpsms.modules.jobOrder.web.model.SearchJobOrderInfoRequest import com.ffii.fpsms.modules.master.web.models.MessageResponse @@ -20,6 +22,7 @@ import org.springframework.web.bind.annotation.RestController @RequestMapping("/jo") class JobOrderController( private val jobOrderService: JobOrderService, + private val jobOrderBomMaterialService: JobOrderBomMaterialService, ) { @GetMapping("/getRecordByPage") @@ -36,4 +39,15 @@ class JobOrderController( fun releaseJobOrder(@Valid @RequestBody request: JobOrderReleaseRequest): MessageResponse { return jobOrderService.releaseJobOrder(request) } + + @PostMapping("/manualCreate") + fun manualCreateJobOrder(@Valid @RequestBody request: CreateJobOrderRequest): MessageResponse { + val jo = jobOrderService.createJobOrder(request) + if (jo.id == null) { + throw NoSuchElementException() + } + jobOrderBomMaterialService.createJobOrderBomMaterialsByJoId(jo.id) + + return jo + } } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/jobOrder/web/model/CreateJobOrderRequest.kt b/src/main/java/com/ffii/fpsms/modules/jobOrder/web/model/CreateJobOrderRequest.kt index 2ca6a14..c9b05f2 100644 --- a/src/main/java/com/ffii/fpsms/modules/jobOrder/web/model/CreateJobOrderRequest.kt +++ b/src/main/java/com/ffii/fpsms/modules/jobOrder/web/model/CreateJobOrderRequest.kt @@ -9,8 +9,8 @@ data class CreateJobOrderRequest ( val planEnd: LocalDateTime? = null, val reqQty: BigDecimal?, val type: String? = "detailed", - val approverId: Long?, - val prodScheduleLineId: Long?, + val approverId: Long? = null, + val prodScheduleLineId: Long? = null, val status: String = "planning", ) diff --git a/src/main/java/com/ffii/fpsms/modules/master/entity/BomMaterialRepository.kt b/src/main/java/com/ffii/fpsms/modules/master/entity/BomMaterialRepository.kt index b6d2d8a..d0ed3f5 100644 --- a/src/main/java/com/ffii/fpsms/modules/master/entity/BomMaterialRepository.kt +++ b/src/main/java/com/ffii/fpsms/modules/master/entity/BomMaterialRepository.kt @@ -11,4 +11,6 @@ interface BomMaterialRepository : AbstractRepository { fun findByM18IdAndDeletedIsFalse(m18Id: Long): BomMaterial? fun findAllByBomItemIdAndDeletedIsFalse(itemId: Long): List + + fun findAllByBomIdAndDeletedIsFalse(bomId: Long): List } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/master/entity/BomRepository.kt b/src/main/java/com/ffii/fpsms/modules/master/entity/BomRepository.kt index 79b0a68..1d3a0ef 100644 --- a/src/main/java/com/ffii/fpsms/modules/master/entity/BomRepository.kt +++ b/src/main/java/com/ffii/fpsms/modules/master/entity/BomRepository.kt @@ -1,6 +1,7 @@ package com.ffii.fpsms.modules.master.entity import com.ffii.core.support.AbstractRepository +import com.ffii.fpsms.modules.master.entity.projections.BomCombo import org.springframework.stereotype.Repository import java.io.Serializable @@ -13,4 +14,6 @@ interface BomRepository : AbstractRepository { fun findByM18IdAndDeletedIsFalse(m18Id: Long): Bom? fun findByItemIdAndDeletedIsFalse(itemId: Serializable): Bom? + + fun findBomComboByDeletedIsFalse(): List } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/master/entity/projections/BomCombo.kt b/src/main/java/com/ffii/fpsms/modules/master/entity/projections/BomCombo.kt new file mode 100644 index 0000000..80fb748 --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/master/entity/projections/BomCombo.kt @@ -0,0 +1,11 @@ +package com.ffii.fpsms.modules.master.entity.projections + +import org.springframework.beans.factory.annotation.Value + +interface BomCombo { + val id: Long; + @get:Value("#{target.id}") + val value: Long; + @get:Value("#{target.code} - #{target.name} - #{target.item.itemUoms.^[salesUnit == true && deleted == false]?.uom.udfudesc}") + val label: String; +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/master/service/BomMaterialService.kt b/src/main/java/com/ffii/fpsms/modules/master/service/BomMaterialService.kt index 548e0e8..feaebe1 100644 --- a/src/main/java/com/ffii/fpsms/modules/master/service/BomMaterialService.kt +++ b/src/main/java/com/ffii/fpsms/modules/master/service/BomMaterialService.kt @@ -1,5 +1,6 @@ package com.ffii.fpsms.modules.master.service +import com.ffii.fpsms.modules.jobOrder.web.model.CreateJobOrderBomMaterialRequest import com.ffii.fpsms.modules.master.entity.* import com.ffii.fpsms.modules.master.web.models.SaveBomMaterialRequest import com.ffii.fpsms.modules.master.web.models.SaveBomMaterialResponse @@ -20,6 +21,10 @@ open class BomMaterialService( return bomMaterialRepository.findByM18IdAndDeletedIsFalse(m18Id); } + open fun findAllByBomId(bomId: Long): List { + return bomMaterialRepository.findAllByBomIdAndDeletedIsFalse(bomId); + } + open fun saveBomMaterial(request: SaveBomMaterialRequest): SaveBomMaterialResponse { val bomMaterial = request.m18Id?.let { findByM18Id(it) } ?: request.id?.let { findById(it) } diff --git a/src/main/java/com/ffii/fpsms/modules/master/web/BomController.kt b/src/main/java/com/ffii/fpsms/modules/master/web/BomController.kt index 90aa3d3..72b0189 100644 --- a/src/main/java/com/ffii/fpsms/modules/master/web/BomController.kt +++ b/src/main/java/com/ffii/fpsms/modules/master/web/BomController.kt @@ -1,6 +1,8 @@ package com.ffii.fpsms.modules.master.web import com.ffii.fpsms.modules.master.entity.Bom +import com.ffii.fpsms.modules.master.entity.BomRepository +import com.ffii.fpsms.modules.master.entity.projections.BomCombo import com.ffii.fpsms.modules.master.service.BomService import org.springframework.core.io.ByteArrayResource import org.springframework.core.io.Resource @@ -16,13 +18,18 @@ import java.time.LocalDate @RestController @RequestMapping("/bom") class BomController ( - val bomService: BomService, + val bomService: BomService, private val bomRepository: BomRepository, ) { @GetMapping fun getBoms(): List { return bomService.findAll() } + @GetMapping("/combo") + fun getCombo(): List { + return bomRepository.findBomComboByDeletedIsFalse(); + } + @PostMapping("/import-bom") fun importBom(): ResponseEntity { val reportResult = bomService.importBOM()