| @@ -25,7 +25,8 @@ interface JobOrderRepository : AbstractRepository<JobOrder, Long> { | |||
| 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<JobOrder, Long> { | |||
| 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<JobOrder, Long> { | |||
| 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 | |||
| """ | |||
| ) | |||
| @@ -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<JobOrderDetailPickLine>?, | |||
| 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.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<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 { | |||
| 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)) | |||
| } | |||
| } | |||
| @@ -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 | |||
| @@ -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 | |||
| } | |||
| } | |||
| @@ -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", | |||
| ) | |||
| @@ -11,4 +11,6 @@ interface BomMaterialRepository : AbstractRepository<BomMaterial, Long> { | |||
| fun findByM18IdAndDeletedIsFalse(m18Id: Long): BomMaterial? | |||
| fun findAllByBomItemIdAndDeletedIsFalse(itemId: Long): List<BomMaterial> | |||
| fun findAllByBomIdAndDeletedIsFalse(bomId: Long): List<BomMaterial> | |||
| } | |||
| @@ -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<Bom, Long> { | |||
| fun findByM18IdAndDeletedIsFalse(m18Id: Long): 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 | |||
| 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<BomMaterial> { | |||
| return bomMaterialRepository.findAllByBomIdAndDeletedIsFalse(bomId); | |||
| } | |||
| open fun saveBomMaterial(request: SaveBomMaterialRequest): SaveBomMaterialResponse { | |||
| val bomMaterial = request.m18Id?.let { findByM18Id(it) } | |||
| ?: request.id?.let { findById(it) } | |||
| @@ -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<Bom> { | |||
| return bomService.findAll() | |||
| } | |||
| @GetMapping("/combo") | |||
| fun getCombo(): List<BomCombo> { | |||
| return bomRepository.findBomComboByDeletedIsFalse(); | |||
| } | |||
| @PostMapping("/import-bom") | |||
| fun importBom(): ResponseEntity<Resource> { | |||
| val reportResult = bomService.importBOM() | |||