| @@ -25,7 +25,8 @@ interface JobOrderRepository : AbstractRepository<JobOrder, Long> { | |||||
| jo.code, | jo.code, | ||||
| b.name, | b.name, | ||||
| jo.reqQty, | jo.reqQty, | ||||
| b.outputQtyUom, | |||||
| -- b.outputQtyUom, | |||||
| uc2.udfudesc as uom, | |||||
| json_arrayagg( | json_arrayagg( | ||||
| json_object( | json_object( | ||||
| 'id', jobm.id, | 'id', jobm.id, | ||||
| @@ -40,6 +41,8 @@ interface JobOrderRepository : AbstractRepository<JobOrder, Long> { | |||||
| jo.status | jo.status | ||||
| from job_order jo | from job_order jo | ||||
| left join bom b on b.id = jo.bomId | 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 job_order_bom_material jobm on jo.id = jobm.jobOrderId | ||||
| left join items i on i.id = jobm.itemId | left join items i on i.id = jobm.itemId | ||||
| left join uom_conversion uc on uc.id = jobm.uomId | left join uom_conversion uc on uc.id = jobm.uomId | ||||
| @@ -47,7 +50,7 @@ interface JobOrderRepository : AbstractRepository<JobOrder, Long> { | |||||
| left join inventory_lot_line ill on ill.id = sol.inventoryLotLineId | left join inventory_lot_line ill on ill.id = sol.inventoryLotLineId | ||||
| left join inventory_lot il on il.id = ill.inventoryLotId | left join inventory_lot il on il.id = ill.inventoryLotId | ||||
| where jo.id = :id | where jo.id = :id | ||||
| group by jo.id | |||||
| group by jo.id, uc2.udfudesc | |||||
| limit 1 | limit 1 | ||||
| """ | """ | ||||
| ) | ) | ||||
| @@ -12,7 +12,7 @@ interface JobOrderInfo { | |||||
| val name: String; | val name: String; | ||||
| val reqQty: BigDecimal; | val reqQty: BigDecimal; | ||||
| @get:Value("#{target.bom.outputQtyUom}") | |||||
| @get:Value("#{target.bom.item.itemUoms.^[salesUnit == true && deleted == false]?.uom.udfudesc}") | |||||
| val uom: String; | val uom: String; | ||||
| val status: String; | val status: String; | ||||
| } | } | ||||
| @@ -23,7 +23,7 @@ interface JobOrderDetailWithJsonString { | |||||
| val code: String?; | val code: String?; | ||||
| val name: String?; | val name: String?; | ||||
| val reqQty: BigDecimal?; | val reqQty: BigDecimal?; | ||||
| val outputQtyUom: String?; | |||||
| val uom: String?; | |||||
| val pickLines: String?; | val pickLines: String?; | ||||
| val status: String?; | val status: String?; | ||||
| } | } | ||||
| @@ -33,7 +33,7 @@ data class JobOrderDetail( | |||||
| val code: String?, | val code: String?, | ||||
| val name: String?, | val name: String?, | ||||
| val reqQty: BigDecimal?, | val reqQty: BigDecimal?, | ||||
| val outputQtyUom: String?, | |||||
| val uom: String?, | |||||
| val pickLines: List<JobOrderDetailPickLine>?, | val pickLines: List<JobOrderDetailPickLine>?, | ||||
| val status: String? | val status: String? | ||||
| ) | ) | ||||
| @@ -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.jobOrder.web.model.CreateJobOrderBomMaterialRequest | ||||
| import com.ffii.fpsms.modules.master.entity.ItemsRepository | import com.ffii.fpsms.modules.master.entity.ItemsRepository | ||||
| import com.ffii.fpsms.modules.master.entity.UomConversionRepository | 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 com.ffii.fpsms.modules.master.web.models.MessageResponse | ||||
| import org.springframework.stereotype.Service | import org.springframework.stereotype.Service | ||||
| import java.math.BigDecimal | |||||
| import java.math.RoundingMode | |||||
| import kotlin.jvm.optionals.getOrNull | import kotlin.jvm.optionals.getOrNull | ||||
| @Service | @Service | ||||
| open class JobOrderBomMaterialService( | open class JobOrderBomMaterialService( | ||||
| val jobOrderBomMaterialRepository: JobOrderBomMaterialRepository, private val jobOrderRepository: JobOrderRepository, | |||||
| val jobOrderBomMaterialRepository: JobOrderBomMaterialRepository, | |||||
| private val jobOrderRepository: JobOrderRepository, | |||||
| private val itemsRepository: ItemsRepository, | private val itemsRepository: ItemsRepository, | ||||
| val itemUomService: ItemUomService, | |||||
| private val uomConversionRepository: UomConversionRepository | private val uomConversionRepository: UomConversionRepository | ||||
| ) { | ) { | ||||
| open fun createJobOrderBomMaterialRequests(joId: Long): List<CreateJobOrderBomMaterialRequest> { | |||||
| 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<CreateJobOrderBomMaterialRequest>): MessageResponse { | fun createJobOrderBomMaterials(request: List<CreateJobOrderBomMaterialRequest>): MessageResponse { | ||||
| val joBomMaterials = request.map { req -> | val joBomMaterials = request.map { req -> | ||||
| val jo = req.joId?.let { jobOrderRepository.findById(it).getOrNull() } | val jo = req.joId?.let { jobOrderRepository.findById(it).getOrNull() } | ||||
| @@ -42,4 +66,8 @@ open class JobOrderBomMaterialService( | |||||
| errorPosition = null | errorPosition = null | ||||
| ) | ) | ||||
| } | } | ||||
| fun createJobOrderBomMaterialsByJoId(joId: Long): MessageResponse { | |||||
| return createJobOrderBomMaterials(createJobOrderBomMaterialRequests(joId)) | |||||
| } | |||||
| } | } | ||||
| @@ -2,6 +2,7 @@ package com.ffii.fpsms.modules.jobOrder.service | |||||
| import com.ffii.core.response.RecordsRes | import com.ffii.core.response.RecordsRes | ||||
| import com.ffii.core.utils.GsonUtils | 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.JobOrder | ||||
| import com.ffii.fpsms.modules.jobOrder.entity.JobOrderRepository | import com.ffii.fpsms.modules.jobOrder.entity.JobOrderRepository | ||||
| import com.ffii.fpsms.modules.jobOrder.entity.projections.JobOrderDetail | import com.ffii.fpsms.modules.jobOrder.entity.projections.JobOrderDetail | ||||
| @@ -61,7 +62,7 @@ open class JobOrderService( | |||||
| code = sqlResult.code, | code = sqlResult.code, | ||||
| name = sqlResult.name, | name = sqlResult.name, | ||||
| reqQty = sqlResult.reqQty, | reqQty = sqlResult.reqQty, | ||||
| outputQtyUom = sqlResult.outputQtyUom, | |||||
| uom = sqlResult.uom, | |||||
| pickLines = jsonResult, | pickLines = jsonResult, | ||||
| status = sqlResult.status | status = sqlResult.status | ||||
| ) | ) | ||||
| @@ -90,7 +91,7 @@ open class JobOrderService( | |||||
| open fun createJobOrder(request: CreateJobOrderRequest): MessageResponse { | open fun createJobOrder(request: CreateJobOrderRequest): MessageResponse { | ||||
| val jo = JobOrder() | val jo = JobOrder() | ||||
| val bom = request.bomId?.let { bomService.findById(it) } | 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 prodScheduleLine = request.prodScheduleLineId?.let { productionScheduleLineRepository.findById(it).getOrNull() } | ||||
| val code = assignJobNo() | val code = assignJobNo() | ||||
| @@ -98,8 +99,8 @@ open class JobOrderService( | |||||
| this.code = code | this.code = code | ||||
| this.bom = bom | this.bom = bom | ||||
| //TODO: planStart & planEnd | //TODO: planStart & planEnd | ||||
| planStart = LocalDateTime.now() | |||||
| planEnd = LocalDateTime.now() | |||||
| planStart = request.planStart ?: LocalDateTime.now() | |||||
| planEnd = request.planEnd ?: LocalDateTime.now() | |||||
| reqQty = request.reqQty | reqQty = request.reqQty | ||||
| status = request.status | status = request.status | ||||
| type = request.type | type = request.type | ||||
| @@ -3,7 +3,9 @@ package com.ffii.fpsms.modules.jobOrder.web | |||||
| import com.ffii.core.response.RecordsRes | import com.ffii.core.response.RecordsRes | ||||
| import com.ffii.fpsms.modules.jobOrder.entity.projections.JobOrderDetail | import com.ffii.fpsms.modules.jobOrder.entity.projections.JobOrderDetail | ||||
| import com.ffii.fpsms.modules.jobOrder.entity.projections.JobOrderInfo | 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.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.JobOrderReleaseRequest | ||||
| import com.ffii.fpsms.modules.jobOrder.web.model.SearchJobOrderInfoRequest | import com.ffii.fpsms.modules.jobOrder.web.model.SearchJobOrderInfoRequest | ||||
| import com.ffii.fpsms.modules.master.web.models.MessageResponse | import com.ffii.fpsms.modules.master.web.models.MessageResponse | ||||
| @@ -20,6 +22,7 @@ import org.springframework.web.bind.annotation.RestController | |||||
| @RequestMapping("/jo") | @RequestMapping("/jo") | ||||
| class JobOrderController( | class JobOrderController( | ||||
| private val jobOrderService: JobOrderService, | private val jobOrderService: JobOrderService, | ||||
| private val jobOrderBomMaterialService: JobOrderBomMaterialService, | |||||
| ) { | ) { | ||||
| @GetMapping("/getRecordByPage") | @GetMapping("/getRecordByPage") | ||||
| @@ -36,4 +39,15 @@ class JobOrderController( | |||||
| fun releaseJobOrder(@Valid @RequestBody request: JobOrderReleaseRequest): MessageResponse { | fun releaseJobOrder(@Valid @RequestBody request: JobOrderReleaseRequest): MessageResponse { | ||||
| return jobOrderService.releaseJobOrder(request) | 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 | |||||
| } | |||||
| } | } | ||||
| @@ -9,8 +9,8 @@ data class CreateJobOrderRequest ( | |||||
| val planEnd: LocalDateTime? = null, | val planEnd: LocalDateTime? = null, | ||||
| val reqQty: BigDecimal?, | val reqQty: BigDecimal?, | ||||
| val type: String? = "detailed", | val type: String? = "detailed", | ||||
| val approverId: Long?, | |||||
| val prodScheduleLineId: Long?, | |||||
| val approverId: Long? = null, | |||||
| val prodScheduleLineId: Long? = null, | |||||
| val status: String = "planning", | val status: String = "planning", | ||||
| ) | ) | ||||
| @@ -11,4 +11,6 @@ interface BomMaterialRepository : AbstractRepository<BomMaterial, Long> { | |||||
| fun findByM18IdAndDeletedIsFalse(m18Id: Long): BomMaterial? | fun findByM18IdAndDeletedIsFalse(m18Id: Long): BomMaterial? | ||||
| fun findAllByBomItemIdAndDeletedIsFalse(itemId: Long): List<BomMaterial> | fun findAllByBomItemIdAndDeletedIsFalse(itemId: Long): List<BomMaterial> | ||||
| fun findAllByBomIdAndDeletedIsFalse(bomId: Long): List<BomMaterial> | |||||
| } | } | ||||
| @@ -1,6 +1,7 @@ | |||||
| package com.ffii.fpsms.modules.master.entity | package com.ffii.fpsms.modules.master.entity | ||||
| import com.ffii.core.support.AbstractRepository | import com.ffii.core.support.AbstractRepository | ||||
| import com.ffii.fpsms.modules.master.entity.projections.BomCombo | |||||
| import org.springframework.stereotype.Repository | import org.springframework.stereotype.Repository | ||||
| import java.io.Serializable | import java.io.Serializable | ||||
| @@ -13,4 +14,6 @@ interface BomRepository : AbstractRepository<Bom, Long> { | |||||
| fun findByM18IdAndDeletedIsFalse(m18Id: Long): Bom? | fun findByM18IdAndDeletedIsFalse(m18Id: Long): Bom? | ||||
| fun findByItemIdAndDeletedIsFalse(itemId: Serializable): Bom? | fun findByItemIdAndDeletedIsFalse(itemId: Serializable): Bom? | ||||
| fun findBomComboByDeletedIsFalse(): List<BomCombo> | |||||
| } | } | ||||
| @@ -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; | |||||
| } | |||||
| @@ -1,5 +1,6 @@ | |||||
| package com.ffii.fpsms.modules.master.service | 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.entity.* | ||||
| import com.ffii.fpsms.modules.master.web.models.SaveBomMaterialRequest | import com.ffii.fpsms.modules.master.web.models.SaveBomMaterialRequest | ||||
| import com.ffii.fpsms.modules.master.web.models.SaveBomMaterialResponse | import com.ffii.fpsms.modules.master.web.models.SaveBomMaterialResponse | ||||
| @@ -20,6 +21,10 @@ open class BomMaterialService( | |||||
| return bomMaterialRepository.findByM18IdAndDeletedIsFalse(m18Id); | return bomMaterialRepository.findByM18IdAndDeletedIsFalse(m18Id); | ||||
| } | } | ||||
| open fun findAllByBomId(bomId: Long): List<BomMaterial> { | |||||
| return bomMaterialRepository.findAllByBomIdAndDeletedIsFalse(bomId); | |||||
| } | |||||
| open fun saveBomMaterial(request: SaveBomMaterialRequest): SaveBomMaterialResponse { | open fun saveBomMaterial(request: SaveBomMaterialRequest): SaveBomMaterialResponse { | ||||
| val bomMaterial = request.m18Id?.let { findByM18Id(it) } | val bomMaterial = request.m18Id?.let { findByM18Id(it) } | ||||
| ?: request.id?.let { findById(it) } | ?: request.id?.let { findById(it) } | ||||
| @@ -1,6 +1,8 @@ | |||||
| package com.ffii.fpsms.modules.master.web | package com.ffii.fpsms.modules.master.web | ||||
| import com.ffii.fpsms.modules.master.entity.Bom | 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 com.ffii.fpsms.modules.master.service.BomService | ||||
| import org.springframework.core.io.ByteArrayResource | import org.springframework.core.io.ByteArrayResource | ||||
| import org.springframework.core.io.Resource | import org.springframework.core.io.Resource | ||||
| @@ -16,13 +18,18 @@ import java.time.LocalDate | |||||
| @RestController | @RestController | ||||
| @RequestMapping("/bom") | @RequestMapping("/bom") | ||||
| class BomController ( | class BomController ( | ||||
| val bomService: BomService, | |||||
| val bomService: BomService, private val bomRepository: BomRepository, | |||||
| ) { | ) { | ||||
| @GetMapping | @GetMapping | ||||
| fun getBoms(): List<Bom> { | fun getBoms(): List<Bom> { | ||||
| return bomService.findAll() | return bomService.findAll() | ||||
| } | } | ||||
| @GetMapping("/combo") | |||||
| fun getCombo(): List<BomCombo> { | |||||
| return bomRepository.findBomComboByDeletedIsFalse(); | |||||
| } | |||||
| @PostMapping("/import-bom") | @PostMapping("/import-bom") | ||||
| fun importBom(): ResponseEntity<Resource> { | fun importBom(): ResponseEntity<Resource> { | ||||
| val reportResult = bomService.importBOM() | val reportResult = bomService.importBOM() | ||||