@@ -2,6 +2,7 @@ package com.ffii.fpsms.modules.jobOrder.entity | |||
import com.ffii.core.entity.BaseEntity | |||
import com.ffii.fpsms.modules.master.entity.Bom | |||
import com.ffii.fpsms.modules.master.entity.ProductionScheduleLine | |||
import com.ffii.fpsms.modules.user.entity.User | |||
import jakarta.persistence.* | |||
import jakarta.validation.constraints.NotNull | |||
@@ -58,8 +59,8 @@ open class JobOrder : BaseEntity<Long>() { | |||
@JoinColumn(name = "approverId", referencedColumnName = "id") | |||
open var approver: User? = null | |||
// @ManyToOne | |||
// @JoinColumn(name = "jobPlanningLogId") | |||
@Column(name = "prodScheduleLineId") | |||
open var prodScheduleLineId: Long? = null | |||
@OneToOne | |||
@JoinColumn(name = "prodScheduleLineId") | |||
// @Column(name = "prodScheduleLineId") | |||
open var prodScheduleLine: ProductionScheduleLine? = null | |||
} |
@@ -1,21 +1,23 @@ | |||
package com.ffii.fpsms.modules.jobOrder.entity | |||
import com.fasterxml.jackson.annotation.JsonBackReference | |||
import com.ffii.core.entity.BaseEntity | |||
import com.ffii.fpsms.modules.master.entity.Items | |||
import com.ffii.fpsms.modules.master.entity.UomConversion | |||
import com.ffii.fpsms.modules.stock.entity.InventoryLotLine | |||
import com.ffii.fpsms.modules.stock.entity.SuggestedPickLot | |||
import jakarta.persistence.* | |||
import jakarta.validation.constraints.NotNull | |||
import jakarta.validation.constraints.Size | |||
import java.math.BigDecimal | |||
@Entity | |||
@Table(name = "job_order_material") | |||
open class JobOrderMaterial : BaseEntity<Long>() { | |||
@Table(name = "job_order_bom_material") | |||
open class JobOrderBomMaterial : BaseEntity<Long>() { | |||
@NotNull | |||
@ManyToOne | |||
@JoinColumn(name = "jopId", nullable = false) | |||
open var jop: JobOrderProcess? = null | |||
@JoinColumn(name = "jobOrderId", nullable = false) | |||
open var jobOrder: JobOrder? = null | |||
@NotNull | |||
@ManyToOne | |||
@@ -23,8 +25,8 @@ open class JobOrderMaterial : BaseEntity<Long>() { | |||
open var item: Items? = null | |||
@NotNull | |||
@Column(name = "qty", nullable = false, precision = 14, scale = 2) | |||
open var qty: BigDecimal? = null | |||
@Column(name = "reqQty", nullable = false, precision = 14, scale = 2) | |||
open var reqQty: BigDecimal? = null | |||
@NotNull | |||
@ManyToOne | |||
@@ -32,11 +34,11 @@ open class JobOrderMaterial : BaseEntity<Long>() { | |||
open var uom: UomConversion? = null | |||
@ManyToOne | |||
@JoinColumn(name = "inventoryLotLineId") | |||
open var inventoryLotLine: InventoryLotLine? = null | |||
@JoinColumn(name = "suggestedPickLotId") | |||
open var suggestedPickLot: SuggestedPickLot? = null | |||
@Size(max = 255) | |||
@NotNull | |||
@ManyToOne | |||
@JoinColumn(name = "joProcessDetailId", nullable = false) | |||
open var joProcessDetail: JobOrderProcessDetail? = null | |||
@Column(name = "status", nullable = false) | |||
open var status: String? = null | |||
} |
@@ -4,5 +4,5 @@ import com.ffii.core.support.AbstractRepository | |||
import org.springframework.stereotype.Repository | |||
@Repository | |||
interface JobOrderMaterialRepository : AbstractRepository<JobOrderMaterial, Long> { | |||
interface JobOrderBomMaterialRepository : AbstractRepository<JobOrderBomMaterial, Long> { | |||
} |
@@ -11,12 +11,12 @@ import java.time.LocalDateTime | |||
@Table(name = "job_order_process") | |||
open class JobOrderProcess : BaseEntity<Long>() { | |||
@NotNull | |||
@ManyToOne(fetch = FetchType.LAZY, optional = false) | |||
@ManyToOne | |||
@JoinColumn(name = "joId", nullable = false) | |||
open var jo: JobOrder? = null | |||
@NotNull | |||
@ManyToOne(fetch = FetchType.LAZY, optional = false) | |||
@ManyToOne | |||
@JoinColumn(name = "processId", nullable = false) | |||
open var process: Process? = null | |||
@@ -1,8 +1,13 @@ | |||
package com.ffii.fpsms.modules.jobOrder.entity | |||
import com.ffii.core.support.AbstractRepository | |||
import org.springframework.data.jpa.repository.Query | |||
import org.springframework.stereotype.Repository | |||
@Repository | |||
interface JobOrderRepository : AbstractRepository<JobOrder, Long> { | |||
@Query(""" | |||
select jo.code from JobOrder jo where jo.code like :prefix% order by jo.code desc limit 1 | |||
""") | |||
fun findLatestCodeByPrefix(prefix: String): String? | |||
} |
@@ -0,0 +1,45 @@ | |||
package com.ffii.fpsms.modules.jobOrder.service | |||
import com.ffii.fpsms.modules.jobOrder.entity.JobOrderBomMaterial | |||
import com.ffii.fpsms.modules.jobOrder.entity.JobOrderBomMaterialRepository | |||
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.web.models.MessageResponse | |||
import org.springframework.stereotype.Service | |||
import kotlin.jvm.optionals.getOrNull | |||
@Service | |||
open class JobOrderBomMaterialService( | |||
val jobOrderBomMaterialRepository: JobOrderBomMaterialRepository, private val jobOrderRepository: JobOrderRepository, | |||
private val itemsRepository: ItemsRepository, | |||
private val uomConversionRepository: UomConversionRepository | |||
) { | |||
fun createJobOrderBomMaterials(request: List<CreateJobOrderBomMaterialRequest>): MessageResponse { | |||
val joBomMaterials = request.map { req -> | |||
val jo = req.joId?.let { jobOrderRepository.findById(it).getOrNull() } | |||
val item = req.itemId?.let { itemsRepository.findById(it).getOrNull() } | |||
val uom = req.uomId?.let { uomConversionRepository.findById(it).getOrNull() } | |||
JobOrderBomMaterial().apply { | |||
jobOrder = jo | |||
this.item = item | |||
reqQty = req.reqQty | |||
this.uom = uom | |||
status = req.status | |||
} | |||
} | |||
jobOrderBomMaterialRepository.saveAll(joBomMaterials) | |||
return MessageResponse( | |||
id = null, | |||
name = null, | |||
code = null, | |||
type = null, | |||
message = "Success", | |||
errorPosition = null | |||
) | |||
} | |||
} |
@@ -7,8 +7,11 @@ import com.ffii.core.support.JdbcDao | |||
import com.ffii.fpsms.m18.entity.M18DataLogRepository | |||
import com.ffii.fpsms.modules.jobOrder.entity.JobOrderProcess | |||
import com.ffii.fpsms.modules.jobOrder.entity.JobOrderProcessRepository | |||
import com.ffii.fpsms.modules.jobOrder.entity.JobOrderRepository | |||
import com.ffii.fpsms.modules.jobOrder.web.model.CreateJobOrderProcessRequest | |||
import com.ffii.fpsms.modules.jobOrder.web.model.MachineRequest | |||
import com.ffii.fpsms.modules.jobOrder.web.model.OperatorResponse | |||
import com.ffii.fpsms.modules.master.entity.ProcessRepository | |||
import com.ffii.fpsms.modules.master.entity.ShopRepository | |||
import com.ffii.fpsms.modules.master.service.CurrencyService | |||
import com.ffii.fpsms.modules.master.service.ShopService | |||
@@ -45,9 +48,36 @@ import kotlin.jvm.optionals.getOrNull | |||
open class JobOrderProcessService( | |||
private val jdbcDao: JdbcDao, | |||
private val jobOrderProcessRepository: JobOrderProcessRepository, | |||
private val userRepository: UserRepository | |||
private val userRepository: UserRepository, private val processRepository: ProcessRepository, | |||
private val jobOrderRepository: JobOrderRepository, | |||
) : AbstractBaseEntityService<JobOrderProcess, Long, JobOrderProcessRepository>(jdbcDao, jobOrderProcessRepository) { | |||
open fun createJobOrderProcesses(request: List<CreateJobOrderProcessRequest>): MessageResponse{ | |||
val joProcesses = request.map { req -> | |||
val jo = req.joId?.let { jobOrderRepository.findById(it).getOrNull() } | |||
val process = req.processId?.let { processRepository.findById(it).getOrNull() } | |||
JobOrderProcess().apply { | |||
this.jo = jo | |||
this.process = process | |||
status = req.status | |||
seqNo = req.seqNo | |||
remarks = req.remarks | |||
} | |||
} | |||
jobOrderProcessRepository.saveAll(joProcesses) | |||
return MessageResponse( | |||
id = null, | |||
name = null, | |||
code = null, | |||
type = null, | |||
message = "Success", | |||
errorPosition = null | |||
) | |||
} | |||
open fun isOperatorExist(request: OperatorRequest): MessageResponse{ | |||
val User = userRepository.findByUsernameAndDeletedFalse(request.username) | |||
@@ -0,0 +1,75 @@ | |||
package com.ffii.fpsms.modules.jobOrder.service | |||
import com.ffii.fpsms.modules.jobOrder.entity.JobOrder | |||
import com.ffii.fpsms.modules.jobOrder.entity.JobOrderRepository | |||
import com.ffii.fpsms.modules.jobOrder.web.model.CreateJobOrderRequest | |||
import com.ffii.fpsms.modules.master.entity.ProductionScheduleLineRepository | |||
import com.ffii.fpsms.modules.master.service.BomService | |||
import com.ffii.fpsms.modules.master.web.models.MessageResponse | |||
import com.ffii.fpsms.modules.user.service.UserService | |||
import org.springframework.stereotype.Service | |||
import java.time.LocalDate | |||
import java.time.LocalDateTime | |||
import java.time.format.DateTimeFormatter | |||
import kotlin.jvm.optionals.getOrNull | |||
@Service | |||
open class JobOrderService( | |||
val jobOrderRepository: JobOrderRepository, | |||
val bomService: BomService, | |||
val userService: UserService, | |||
val productionScheduleLineRepository: ProductionScheduleLineRepository, | |||
) { | |||
open fun assignJobNo(): String { | |||
val suffixFormat = "%03d" | |||
val pattern = "yyyyMMdd" | |||
val formatter = DateTimeFormatter.ofPattern(pattern) | |||
val prefix = "JO" | |||
val midfix = LocalDate.now().format(formatter) | |||
val suffix = String.format(suffixFormat, 1) | |||
val latestCode = jobOrderRepository.findLatestCodeByPrefix("${prefix}-${midfix}") | |||
if (latestCode != null) { | |||
val splitLatestCode = latestCode.split("-") | |||
if (splitLatestCode.size > 2) { | |||
val latestNo = splitLatestCode[2].toInt() | |||
return listOf<String>(prefix, midfix, String.format(suffixFormat, latestNo + 1)).joinToString("-") | |||
} | |||
} | |||
return listOf<String>(prefix, midfix, suffix).joinToString("-") | |||
} | |||
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 prodScheduleLine = request.prodScheduleLineId?.let { productionScheduleLineRepository.findById(it).getOrNull() } | |||
val code = assignJobNo() | |||
jo.apply { | |||
this.code = code | |||
this.bom = bom | |||
//TODO: planStart & planEnd | |||
planStart = LocalDateTime.now() | |||
planEnd = LocalDateTime.now() | |||
reqQty = request.reqQty | |||
status = request.status | |||
type = request.type | |||
this.approver = approver | |||
this.prodScheduleLine = prodScheduleLine | |||
} | |||
val savedJo = jobOrderRepository.saveAndFlush(jo); | |||
return MessageResponse( | |||
id = savedJo.id, | |||
name = null, | |||
code = savedJo.code, | |||
type = savedJo.type, | |||
message = "Success", | |||
errorPosition = null | |||
) | |||
} | |||
} |
@@ -0,0 +1,31 @@ | |||
package com.ffii.fpsms.modules.jobOrder.web.model | |||
import java.math.BigDecimal | |||
import java.time.LocalDateTime | |||
data class CreateJobOrderRequest ( | |||
val bomId: Long?, | |||
val planStart: LocalDateTime? = null, | |||
val planEnd: LocalDateTime? = null, | |||
val reqQty: BigDecimal?, | |||
val type: String? = "detailed", | |||
val approverId: Long?, | |||
val prodScheduleLineId: Long?, | |||
val status: String = "planning", | |||
) | |||
data class CreateJobOrderBomMaterialRequest ( | |||
val joId: Long?, | |||
val itemId: Long?, | |||
val reqQty: BigDecimal?, | |||
val uomId: Long?, | |||
val status: String = "pending", | |||
) | |||
data class CreateJobOrderProcessRequest ( | |||
val joId: Long?, | |||
val processId: Long?, | |||
val seqNo: Long?, | |||
val remarks: String? = null, | |||
val status: String = "pending", | |||
) |
@@ -61,6 +61,10 @@ open class Bom : BaseEntity<Long>() { | |||
@OneToMany(mappedBy = "bom", cascade = [CascadeType.ALL], orphanRemoval = true) | |||
open var bomMaterials: MutableList<BomMaterial> = mutableListOf() | |||
@JsonManagedReference | |||
@OneToMany(mappedBy = "bom", cascade = [CascadeType.ALL], orphanRemoval = true) | |||
open var bomProcesses: MutableList<BomProcess> = mutableListOf() | |||
@Column(name = "m18Id") | |||
open var m18Id: Long? = null | |||
@@ -11,4 +11,6 @@ interface BomRepository : AbstractRepository<Bom, Long> { | |||
fun findByIdAndDeletedIsFalse(id: Serializable): Bom? | |||
fun findByM18IdAndDeletedIsFalse(m18Id: Long): Bom? | |||
fun findByItemIdAndDeletedIsFalse(itemId: Serializable): Bom? | |||
} |
@@ -2,6 +2,7 @@ package com.ffii.fpsms.modules.master.entity | |||
import com.ffii.core.support.AbstractRepository | |||
import com.ffii.fpsms.modules.master.entity.projections.DetailedProdScheduleWithLine | |||
import com.ffii.fpsms.modules.master.entity.projections.DetailedProdScheduleWithLineWithJsonString | |||
import com.ffii.fpsms.modules.master.entity.projections.ProdScheduleInfo | |||
import org.springframework.data.domain.Page | |||
import org.springframework.data.domain.Pageable | |||
@@ -81,29 +82,39 @@ interface ProductionScheduleRepository : AbstractRepository<ProductionSchedule, | |||
@Query(nativeQuery = true, | |||
value = | |||
""" | |||
with prod_equip as ( | |||
with prod_prop as ( | |||
select | |||
psl.id as pslId, | |||
coalesce(psl.prodQty, 0) / coalesce(b.outputQty, 1) as proportion | |||
from production_schedule ps | |||
left join production_schedule_line psl on psl.prodScheduleId = ps.id | |||
left join bom b on b.itemId = psl.itemId | |||
where ps.id = :id and b.id is not null | |||
), | |||
prod_equip as ( | |||
select | |||
r1.pslId, | |||
json_array(group_concat(json_object(r1.equipName, r1.totalMinute))) as prodTimeInMinute | |||
json_arrayagg(json_object('equipName', r1.equipName, 'totalMinutes', r1.totalMinutes)) as prodTimeInMinute | |||
from ( | |||
select | |||
psl.id as pslId, | |||
coalesce(e.name, 'N/A') as equipName, | |||
sum(coalesce(bp.prepTimeInMinute, 0) + coalesce(bp.durationInMinute, 0) + coalesce(bp.postProdTimeInMinute, 0)) as totalMinute | |||
ceil(sum((coalesce(bp.prepTimeInMinute, 0) + coalesce(bp.durationInMinute, 0) + coalesce(bp.postProdTimeInMinute, 0)) * pp.proportion)) as totalMinutes | |||
from production_schedule ps | |||
left join production_schedule_line psl on psl.prodScheduleId = ps.id | |||
left join prod_prop pp on pp.pslId = psl.id | |||
left join bom b on b.itemId = psl.itemId | |||
left join bom_process bp on bp.bomId = b.id | |||
left join equipment e on bp.equipmentId = e.id | |||
where ps.id = :id and b.id is not null | |||
group by psl.id, e.id | |||
group by psl.id, e.id, pp.proportion | |||
) r1 | |||
group by r1.pslId | |||
), | |||
prod_material as ( | |||
select | |||
r2.pslId, | |||
json_array(group_concat(json_object('id', r2.id, 'code', r2.code, 'name', r2.name, 'type', r2.`type`, 'availableQty', r2.availableQty, 'demandQty', r2.demandQty))) as bomMaterials | |||
json_arrayagg(json_object('id', r2.id, 'code', r2.code, 'name', r2.name, 'type', r2.`type`, 'availableQty', r2.availableQty, 'demandQty', r2.demandQty)) as bomMaterials | |||
from ( | |||
select | |||
psl.id as pslId, | |||
@@ -112,15 +123,16 @@ interface ProductionScheduleRepository : AbstractRepository<ProductionSchedule, | |||
bmi.name, | |||
bmi.`type`, | |||
coalesce(i.onHandQty, 0) - coalesce(i.onHoldQty, 0) - coalesce(i.unavailableQty, 0) as availableQty, | |||
coalesce(psl.prodQty, 0) as demandQty | |||
ceil(coalesce(bm.qty, 0) * pp.proportion) as demandQty | |||
from production_schedule ps | |||
left join production_schedule_line psl on psl.prodScheduleId = ps.id | |||
left join prod_prop pp on pp.pslId = psl.id | |||
left join bom b on b.itemId = psl.itemId | |||
left join bom_material bm on bm.bomId = b.id | |||
left join items bmi on bmi.id = bm.itemId | |||
left join inventory i on i.itemId = bmi.id | |||
where ps.id = :id and bmi.id is not null | |||
group by ps.id, psl.id, bm.id, i.id | |||
group by ps.id, psl.id, bm.id, i.id, pp.proportion | |||
) r2 | |||
group by r2.pslId | |||
) | |||
@@ -129,9 +141,9 @@ interface ProductionScheduleRepository : AbstractRepository<ProductionSchedule, | |||
prod.scheduleAt, | |||
prod.totalFGType, | |||
prod.totalEstProdCount, | |||
json_array(group_concat( | |||
json_arrayagg( | |||
json_object('id', prod.pslId, 'bomMaterials', prod.bomMaterials, 'jobNo', prod.jobNo, 'code', prod.code, 'name', prod.name, 'type', prod.type, 'demandQty', prod.demandQty, 'prodTimeInMinute', prod.prodTimeInMinute, 'priority', prod.priority) | |||
)) | |||
) as prodScheduleLines | |||
from ( | |||
select | |||
ps.id, | |||
@@ -140,7 +152,7 @@ interface ProductionScheduleRepository : AbstractRepository<ProductionSchedule, | |||
ps.totalEstProdCount, | |||
psl.id as pslId, | |||
pm.bomMaterials, | |||
jo.code as jobNo, | |||
coalesce(jo.code, 'N/A') as jobNo, | |||
psli.code, | |||
psli.name, | |||
psli.`type`, | |||
@@ -159,5 +171,5 @@ interface ProductionScheduleRepository : AbstractRepository<ProductionSchedule, | |||
) prod group by prod.id limit 1 | |||
""" | |||
) | |||
fun findDetailedProdScheduleWithLine(id: Long): List<DetailedProdScheduleWithLine> | |||
fun findDetailedProdScheduleWithLine(id: Long): DetailedProdScheduleWithLineWithJsonString? | |||
} |
@@ -15,7 +15,16 @@ interface ProdScheduleInfo { | |||
val type: String? | |||
} | |||
// Detailed Production Schedule With Line | |||
data class DetailedProdScheduleWithLine( | |||
interface DetailedProdScheduleWithLineWithJsonString { | |||
val id: Long? | |||
val scheduleAt: LocalDateTime? | |||
val totalEstProdCount: BigDecimal? | |||
val totalFGType: Long? | |||
// val prodScheduleLines: List<DetailedProdScheduleLineInfo>? | |||
val prodScheduleLines: String? | |||
} | |||
data class DetailedProdScheduleWithLine ( | |||
val id: Long?, | |||
val scheduleAt: LocalDateTime?, | |||
val totalEstProdCount: BigDecimal?, | |||
@@ -32,7 +41,7 @@ data class DetailedProdScheduleLineInfo( | |||
val type: String?, | |||
val demandQty: BigDecimal?, | |||
val prodTimeInMinute: List<DetailedProdScheduleLineProdTime>?, | |||
val priority: BigDecimal?, | |||
val priority: BigDecimal? | |||
) | |||
data class DetailedProdScheduleLineBomMaterial ( | |||
@@ -46,7 +55,7 @@ data class DetailedProdScheduleLineBomMaterial ( | |||
data class DetailedProdScheduleLineProdTime ( | |||
val equipName: String?, | |||
val minutes: BigDecimal?, | |||
val totalMinutes: BigDecimal? | |||
) | |||
// Rough Production Schedule With Line | |||
@@ -37,6 +37,10 @@ open class BomService( | |||
return bomRepository.findByM18IdAndDeletedIsFalse(m18Id) | |||
} | |||
open fun findByItemId(itemId: Long): Bom? { | |||
return bomRepository.findByItemIdAndDeletedIsFalse(itemId) | |||
} | |||
open fun saveBom(request: SaveBomRequest): SaveBomResponse { | |||
val item = request.code.let { itemsService.findByM18BomCode(it) } ?: request.itemId?.let { itemsService.findById(it) } | |||
@@ -3,13 +3,23 @@ package com.ffii.fpsms.modules.master.service | |||
import com.ffii.core.response.RecordsRes | |||
import com.ffii.core.support.AbstractBaseEntityService | |||
import com.ffii.core.support.JdbcDao | |||
import com.ffii.fpsms.modules.common.SecurityUtils | |||
import com.ffii.fpsms.modules.jobOrder.service.JobOrderBomMaterialService | |||
import com.ffii.fpsms.modules.jobOrder.service.JobOrderProcessService | |||
import com.ffii.fpsms.modules.jobOrder.service.JobOrderService | |||
import com.ffii.fpsms.modules.jobOrder.web.model.CreateJobOrderBomMaterialRequest | |||
import com.ffii.fpsms.modules.jobOrder.web.model.CreateJobOrderProcessRequest | |||
import com.ffii.fpsms.modules.jobOrder.web.model.CreateJobOrderRequest | |||
import com.ffii.fpsms.modules.master.entity.* | |||
import com.ffii.fpsms.modules.master.entity.projections.* | |||
import com.ffii.fpsms.modules.master.web.models.MessageResponse | |||
import com.ffii.fpsms.modules.master.web.models.ReleaseProdScheduleLineRequest | |||
import com.ffii.fpsms.modules.master.web.models.SearchProdScheduleRequest | |||
import com.google.gson.Gson | |||
import com.google.gson.reflect.TypeToken | |||
import org.springframework.data.domain.PageRequest | |||
import org.springframework.stereotype.Service | |||
import java.math.BigDecimal | |||
import java.math.RoundingMode | |||
import java.time.LocalDateTime | |||
import java.time.format.DateTimeFormatter | |||
import java.util.* | |||
@@ -25,7 +35,12 @@ open class ProductionScheduleService( | |||
private val itemService: ItemsService, | |||
private val productionScheduleRepository: ProductionScheduleRepository, | |||
private val productionScheduleLineRepository: ProductionScheduleLineRepository, | |||
private val bomMaterialService: BomMaterialService, private val bomMaterialRepository: BomMaterialRepository, | |||
private val bomMaterialService: BomMaterialService, | |||
private val bomMaterialRepository: BomMaterialRepository, | |||
private val jobOrderService: JobOrderService, | |||
private val jobOrderBomMaterialService: JobOrderBomMaterialService, | |||
private val bomService: BomService, | |||
private val jobOrderProcessService: JobOrderProcessService, | |||
) : AbstractBaseEntityService<ProductionSchedule, Long, ProductionScheduleRepository>( | |||
jdbcDao, | |||
productionScheduleRepository | |||
@@ -68,12 +83,14 @@ open class ProductionScheduleService( | |||
val prodScheduleLineInfosByFg = prodScheduleLines.map { line -> | |||
val bomMaterials = line.item.id?.let { bomMaterialRepository.findAllByBomItemIdAndDeletedIsFalse(it) } | |||
?.map { bm -> | |||
val proportion = | |||
if (line.prodQty > 0 && bm.bom?.outputQty != null && (bm.bom?.outputQty ?: zero) > zero) { | |||
BigDecimal(line.prodQty).divide(bm.bom!!.outputQty, 2, RoundingMode.HALF_UP) | |||
} else { | |||
zero | |||
} | |||
// val proportion = | |||
// if (line.prodQty > 0 && bm.bom?.outputQty != null && (bm.bom?.outputQty ?: zero) > zero) { | |||
// BigDecimal(line.prodQty).divide(bm.bom!!.outputQty, 2, RoundingMode.HALF_UP) | |||
// } else { | |||
// BigDecimal.ONE | |||
// } | |||
val proportion = BigDecimal(line.prodQty).divide(bm.bom?.outputQty ?: BigDecimal.ONE) | |||
val demandQty = bm.qty?.times(proportion) ?: zero | |||
@@ -150,12 +167,14 @@ open class ProductionScheduleService( | |||
val bomMaterial = line.item.id?.let { bomMaterialRepository.findAllByBomItemIdAndDeletedIsFalse(it) } | |||
bomMaterial?.map { bm -> | |||
val proportion = | |||
if (line.prodQty > 0 && bm.bom?.outputQty != null && (bm.bom?.outputQty ?: zero) > zero) { | |||
BigDecimal(line.prodQty).divide(bm.bom!!.outputQty, 2, RoundingMode.HALF_UP) | |||
} else { | |||
zero | |||
} | |||
// val proportion = | |||
// if (line.prodQty > 0 && bm.bom?.outputQty != null && (bm.bom?.outputQty ?: zero) > zero) { | |||
// BigDecimal(line.prodQty).divide(bm.bom!!.outputQty, 2, RoundingMode.HALF_UP) | |||
// } else { | |||
// BigDecimal.ONE | |||
// } | |||
val proportion = BigDecimal(line.prodQty).divide(bm.bom?.outputQty ?: BigDecimal.ONE) | |||
val demandQty = bm.qty?.times(proportion) ?: zero | |||
@@ -235,8 +254,76 @@ open class ProductionScheduleService( | |||
) | |||
} | |||
open fun detailedProdScheduleDetail(id: Long): RoughProdScheduleWithLine? { | |||
return null | |||
open fun detailedProdScheduleDetail(id: Long): DetailedProdScheduleWithLine { | |||
val sqlResult = productionScheduleRepository.findDetailedProdScheduleWithLine(id) ?: throw NoSuchElementException() | |||
val gson = Gson() | |||
val type = object : TypeToken<List<DetailedProdScheduleLineInfo>?>() {}.type | |||
val gsonResult: List<DetailedProdScheduleLineInfo>? = gson.fromJson(sqlResult.prodScheduleLines, type) | |||
return DetailedProdScheduleWithLine( | |||
id = sqlResult.id, | |||
scheduleAt = sqlResult.scheduleAt, | |||
totalEstProdCount = sqlResult.totalEstProdCount, | |||
totalFGType = sqlResult.totalFGType, | |||
prodScheduleLines = gsonResult | |||
) | |||
} | |||
open fun releaseProdScheduleLine(request: ReleaseProdScheduleLineRequest): MessageResponse { | |||
val prodScheduleLine = request.id.let { productionScheduleLineRepository.findById(it).getOrNull() } ?: throw NoSuchElementException() | |||
val bom = prodScheduleLine.item.id?.let { bomService.findByItemId(it) } | |||
val approver = SecurityUtils.getUser().getOrNull() | |||
val proportion = request.demandQty.divide(bom?.outputQty ?: BigDecimal.ONE) | |||
// Update Prod Schedule Line Prod qty | |||
prodScheduleLine.apply { prodQty = request.demandQty.toDouble() } | |||
productionScheduleLineRepository.save(prodScheduleLine) | |||
// Create Job Order | |||
val joRequest = CreateJobOrderRequest( | |||
bomId = bom?.id, | |||
reqQty = request.demandQty, | |||
approverId = approver?.id, | |||
prodScheduleLineId = request.id | |||
) | |||
val jo = jobOrderService.createJobOrder(joRequest) | |||
// Create Job Order Bom Materials | |||
val jobmRequests = bom?.bomMaterials?.map { bm -> | |||
CreateJobOrderBomMaterialRequest( | |||
joId = jo.id, | |||
itemId = bm.item?.id, | |||
reqQty = bm.qty?.times(proportion) ?: BigDecimal.ZERO, | |||
uomId = bm.uom?.id | |||
) | |||
} | |||
if (jobmRequests != null) { | |||
jobOrderBomMaterialService.createJobOrderBomMaterials(jobmRequests) | |||
} | |||
// Create Job Order Process | |||
val jopRequests = bom?.bomProcesses?.map { bp -> | |||
CreateJobOrderProcessRequest( | |||
joId = jo.id, | |||
processId = bp.process?.id, | |||
seqNo = bp.seqNo, | |||
) | |||
} | |||
if (jopRequests != null) { | |||
jobOrderProcessService.createJobOrderProcesses(jopRequests) | |||
} | |||
return MessageResponse( | |||
id = request.id, | |||
name = null, | |||
code = null, | |||
type = null, | |||
message = "Success", | |||
errorPosition = null | |||
) | |||
} | |||
//====================細排相關 START====================// | |||
@@ -3,11 +3,14 @@ package com.ffii.fpsms.modules.master.web | |||
import com.ffii.core.response.RecordsRes | |||
import com.ffii.core.utils.CriteriaArgsBuilder | |||
import com.ffii.fpsms.modules.master.entity.ProductionScheduleRepository | |||
import com.ffii.fpsms.modules.master.entity.projections.DetailedProdScheduleWithLine | |||
import com.ffii.fpsms.modules.master.entity.projections.ProdScheduleInfo | |||
import com.ffii.fpsms.modules.master.entity.projections.RoughProdScheduleWithLine | |||
import com.ffii.fpsms.modules.master.service.ProductionScheduleService | |||
import com.ffii.fpsms.modules.master.service.ProductionScheduleService.FinishedGood | |||
import com.ffii.fpsms.modules.master.service.ProductionScheduleService.RoughScheduleObj | |||
import com.ffii.fpsms.modules.master.web.models.MessageResponse | |||
import com.ffii.fpsms.modules.master.web.models.ReleaseProdScheduleLineRequest | |||
import com.ffii.fpsms.modules.master.web.models.SearchProdScheduleRequest | |||
import jakarta.servlet.http.HttpServletRequest | |||
import org.springframework.web.bind.annotation.* | |||
@@ -41,16 +44,26 @@ class ProductionScheduleController( | |||
return productionScheduleService.getLatestScheduleAt("rough") | |||
} | |||
@GetMapping("/detail/{id}") | |||
@GetMapping("/detail/rough/{id}") | |||
fun getScheduleDetail(@PathVariable id: Long): RoughProdScheduleWithLine { | |||
return productionScheduleService.roughProdScheduleDetail(id) | |||
} | |||
@GetMapping("/detail/detailed/{id}") | |||
fun getDetailedProdScheduleDetail(@PathVariable id: Long): DetailedProdScheduleWithLine { | |||
return productionScheduleService.detailedProdScheduleDetail(id) | |||
} | |||
@GetMapping("/getRecordByPage") | |||
fun allProdSchedulesByPage(@ModelAttribute request: SearchProdScheduleRequest): RecordsRes<ProdScheduleInfo> { | |||
return productionScheduleService.allProdSchedulesByPage(request); | |||
} | |||
@PostMapping("/releaseLine") | |||
fun releaseProdScheduleLine(request: ReleaseProdScheduleLineRequest): MessageResponse { | |||
return productionScheduleService.releaseProdScheduleLine(request) | |||
} | |||
@RequestMapping(value = ["/testDetailSchedule"], method = [RequestMethod.GET]) | |||
fun generateDetailSchedule(request: HttpServletRequest?): Int { | |||
try { | |||
@@ -0,0 +1,8 @@ | |||
package com.ffii.fpsms.modules.master.web.models | |||
import java.math.BigDecimal | |||
data class ReleaseProdScheduleLineRequest( | |||
val id: Long, | |||
val demandQty: BigDecimal, | |||
) |