From faa516ccaa3575ca4c9492c08a8cc1b8f9f0cd1f Mon Sep 17 00:00:00 2001 From: "cyril.tsui" Date: Wed, 9 Jul 2025 16:57:52 +0800 Subject: [PATCH] [prod schedule] update for rough prod schedule --- .../entity/ProductionScheduleRepository.kt | 93 +++++++++++++++++-- .../entity/projections/ProdScheduleInfo.kt | 57 +++++++++--- .../service/ProductionScheduleService.kt | 26 ++++-- .../web/ProductionScheduleController.kt | 19 ++-- .../web/models/SearchProdScheduleRequest.kt | 2 +- 5 files changed, 160 insertions(+), 37 deletions(-) diff --git a/src/main/java/com/ffii/fpsms/modules/master/entity/ProductionScheduleRepository.kt b/src/main/java/com/ffii/fpsms/modules/master/entity/ProductionScheduleRepository.kt index cc6896c..bfd0543 100644 --- a/src/main/java/com/ffii/fpsms/modules/master/entity/ProductionScheduleRepository.kt +++ b/src/main/java/com/ffii/fpsms/modules/master/entity/ProductionScheduleRepository.kt @@ -1,14 +1,12 @@ 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.ProdScheduleInfo -import com.ffii.fpsms.modules.master.entity.projections.ProdScheduleWithLine import org.springframework.data.domain.Page import org.springframework.data.domain.Pageable import org.springframework.data.jpa.repository.Query import org.springframework.stereotype.Repository -import java.io.Serializable -import java.time.LocalDate import java.time.LocalDateTime @Repository @@ -44,7 +42,7 @@ interface ProductionScheduleRepository : AbstractRepository?, pageable: Pageable ): Page + + @Query(nativeQuery = true, + value = + """ + with prod_equip as ( + select + r1.pslId, + json_array(group_concat(json_object(r1.equipName, r1.totalMinute))) 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 + from production_schedule ps + left join production_schedule_line psl on psl.prodScheduleId = ps.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 + ) 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 + from ( + select + psl.id as pslId, + bm.id, + bmi.code, + bmi.name, + bmi.`type`, + coalesce(i.onHandQty, 0) - coalesce(i.onHoldQty, 0) - coalesce(i.unavailableQty, 0) as availableQty, + coalesce(psl.prodQty, 0) as demandQty + from production_schedule ps + left join production_schedule_line psl on psl.prodScheduleId = ps.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 + ) r2 + group by r2.pslId + ) + select + prod.id, + prod.scheduleAt, + prod.totalFGType, + prod.totalEstProdCount, + json_array(group_concat( + 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) + )) + from ( + select + ps.id, + ps.scheduleAt, + ps.totalFGType, + ps.totalEstProdCount, + psl.id as pslId, + pm.bomMaterials, + jo.code as jobNo, + psli.code, + psli.name, + psli.`type`, + psl.prodQty as demandQty, + pe.prodTimeInMinute, + psl.itemPriority as priority + from production_schedule ps + left join production_schedule_line psl on psl.prodScheduleId = ps.id + left join items psli on psli.id = psl.itemId + left join job_order jo on jo.prodScheduleLineId = psl.id + left join prod_equip pe on pe.pslId = psl.id + left join prod_material pm on pm.pslId = psl.id + where psl.deleted is false and pe.prodTimeInMinute is not null and pm.bomMaterials is not null + and ps.id = :id + group by psl.id, jo.id + ) prod group by prod.id limit 1 + """ + ) + fun findDetailedProdScheduleWithLine(id: Long): List } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/master/entity/projections/ProdScheduleInfo.kt b/src/main/java/com/ffii/fpsms/modules/master/entity/projections/ProdScheduleInfo.kt index d71e57f..17e104a 100644 --- a/src/main/java/com/ffii/fpsms/modules/master/entity/projections/ProdScheduleInfo.kt +++ b/src/main/java/com/ffii/fpsms/modules/master/entity/projections/ProdScheduleInfo.kt @@ -1,6 +1,5 @@ package com.ffii.fpsms.modules.master.entity.projections -import org.springframework.beans.factory.annotation.Value import java.math.BigDecimal import java.time.LocalDate import java.time.LocalDateTime @@ -15,9 +14,43 @@ interface ProdScheduleInfo { val totalFGType: Long? val type: String? } +// Detailed Production Schedule With Line +data class DetailedProdScheduleWithLine( + val id: Long?, + val scheduleAt: LocalDateTime?, + val totalEstProdCount: BigDecimal?, + val totalFGType: Long?, + val prodScheduleLines: List? +) + +data class DetailedProdScheduleLineInfo( + val id: Long?, + val bomMaterials: List?, + val jobNo: String?, + val code: String?, + val name: String?, + val type: String?, + val demandQty: BigDecimal?, + val prodTimeInMinute: List?, + val priority: BigDecimal?, +) + +data class DetailedProdScheduleLineBomMaterial ( + val id: Long?, + val code: String?, + val name: String?, + val type: String?, + val availableQty: BigDecimal?, + val demandQty: BigDecimal? +) + +data class DetailedProdScheduleLineProdTime ( + val equipName: String?, + val minutes: BigDecimal?, +) -// Production Schedule With Line -data class ProdScheduleWithLine( +// Rough Production Schedule With Line +data class RoughProdScheduleWithLine( val id: Long?, val scheduleAt: LocalDateTime?, val schedulePeriod: LocalDateTime?, @@ -25,13 +58,13 @@ data class ProdScheduleWithLine( val totalEstProdCount: BigDecimal?, val totalFGType: Long?, val type: String?, - val prodScheduleLinesByFg: List, - val prodScheduleLinesByFgByDate: Map>, - val prodScheduleLinesByBom: List, - val prodScheduleLinesByBomByDate: Map> + val prodScheduleLinesByFg: List, + val prodScheduleLinesByFgByDate: Map>, + val prodScheduleLinesByBom: List, + val prodScheduleLinesByBomByDate: Map> ) -data class ProdScheduleLineInfoByFg( +data class RoughProdScheduleLineInfoByFg( val id: Long?, val code: String?, val name: String?, @@ -41,11 +74,11 @@ data class ProdScheduleLineInfoByFg( val lastMonthAvgSales: BigDecimal?, val estCloseBal: BigDecimal?, val priority: Long?, - val bomMaterials: List?, + val bomMaterials: List?, val assignDate: Long?, ) -data class ProdScheduleLineBomMaterialInfoByFg( +data class RoughProdScheduleLineBomMaterialInfoByFg( val id: Long?, val code: String?, val name: String?, @@ -55,7 +88,7 @@ data class ProdScheduleLineBomMaterialInfoByFg( val uomName: String? ) -data class ProdScheduleLineInfoByBom( +data class RoughProdScheduleLineInfoByBom( val id: Long?, val code: String?, val name: String?, @@ -72,7 +105,7 @@ data class ProdScheduleLineInfoByBom( val uomName: String?, ) -data class ProdScheduleLineInfoByBomByDate( +data class RoughProdScheduleLineInfoByBomByDate( val id: Long?, val code: String?, val name: String?, diff --git a/src/main/java/com/ffii/fpsms/modules/master/service/ProductionScheduleService.kt b/src/main/java/com/ffii/fpsms/modules/master/service/ProductionScheduleService.kt index 1ab259a..7d2b402 100644 --- a/src/main/java/com/ffii/fpsms/modules/master/service/ProductionScheduleService.kt +++ b/src/main/java/com/ffii/fpsms/modules/master/service/ProductionScheduleService.kt @@ -45,7 +45,7 @@ open class ProductionScheduleService( schedulePeriod = request.schedulePeriod, schedulePeriodTo = request.schedulePeriodTo, totalEstProdCount = request.totalEstProdCount, - type = request.type, + types = request.types, pageable = pageable ) @@ -54,7 +54,7 @@ open class ProductionScheduleService( return RecordsRes(records, total.toInt()); } - open fun prodScheduleDetail(id: Long): ProdScheduleWithLine { + open fun roughProdScheduleDetail(id: Long): RoughProdScheduleWithLine { val zero = BigDecimal.ZERO val prodSchedule = productionScheduleRepository.findById(id).getOrNull() ?: throw NoSuchElementException() val dayOfWeekValue = @@ -77,7 +77,7 @@ open class ProductionScheduleService( val demandQty = bm.qty?.times(proportion) ?: zero - ProdScheduleLineBomMaterialInfoByFg( + RoughProdScheduleLineBomMaterialInfoByFg( id = bm.id, code = bm.item?.code, name = bm.item?.name, @@ -90,7 +90,7 @@ open class ProductionScheduleService( ) } - ProdScheduleLineInfoByFg( + RoughProdScheduleLineInfoByFg( id = line.id, code = line.item.code, name = line.item.name, @@ -115,7 +115,7 @@ open class ProductionScheduleService( .flatMap { it.bomMaterials ?: mutableListOf() } .groupBy { it.code } .map { (code, bm) -> - ProdScheduleLineBomMaterialInfoByFg( + RoughProdScheduleLineBomMaterialInfoByFg( id = if (bm.isNotEmpty()) bm[0].id else null, code = if (bm.isNotEmpty()) bm[0].code else null, name = if (bm.isNotEmpty()) bm[0].name else null, @@ -126,7 +126,7 @@ open class ProductionScheduleService( ) } - ProdScheduleLineInfoByFg( + RoughProdScheduleLineInfoByFg( id = if (infos.isNotEmpty()) infos[0].assignDate else null, code = if (infos.isNotEmpty()) infos[0].code else null, name = if (infos.isNotEmpty()) infos[0].name else null, @@ -159,7 +159,7 @@ open class ProductionScheduleService( val demandQty = bm.qty?.times(proportion) ?: zero - ProdScheduleLineInfoByBomByDate( + RoughProdScheduleLineInfoByBomByDate( id = bm.item?.id, code = bm.item?.code, name = bm.item?.name, @@ -181,7 +181,7 @@ open class ProductionScheduleService( val prodScheduleLinesInfoByBom = prodScheduleLinesInfoByBom7Days .groupBy { Pair(it.assignDate, it.code) } .map { (key, line) -> - ProdScheduleLineInfoByBomByDate( + RoughProdScheduleLineInfoByBomByDate( id = if (line.isNotEmpty()) line[0].id else null, code = if (line.isNotEmpty()) line[0].code else null, name = if (line.isNotEmpty()) line[0].name else null, @@ -199,7 +199,7 @@ open class ProductionScheduleService( val sumProdScheduleLinesInfoByBom = prodScheduleLinesInfoByBom7Days .groupBy { it.code } .map { (_, line) -> - ProdScheduleLineInfoByBom( + RoughProdScheduleLineInfoByBom( id = if (line.isNotEmpty()) line[0].id else null, code = if (line.isNotEmpty()) line[0].code else null, name = if (line.isNotEmpty()) line[0].name else null, @@ -217,8 +217,10 @@ open class ProductionScheduleService( ) } + + // ---------------------------------- Response ----------------------------------// - return ProdScheduleWithLine( + return RoughProdScheduleWithLine( id = prodSchedule.id, scheduleAt = prodSchedule.scheduleAt, schedulePeriod = schedulePeriod, @@ -233,6 +235,10 @@ open class ProductionScheduleService( ) } + open fun detailedProdScheduleDetail(id: Long): RoughProdScheduleWithLine? { + return null + } + //====================細排相關 START====================// open fun getDailyProductionCount(assignDate: Int, selectedDate: LocalDateTime): Int { diff --git a/src/main/java/com/ffii/fpsms/modules/master/web/ProductionScheduleController.kt b/src/main/java/com/ffii/fpsms/modules/master/web/ProductionScheduleController.kt index c29e3fd..0b1e9ad 100644 --- a/src/main/java/com/ffii/fpsms/modules/master/web/ProductionScheduleController.kt +++ b/src/main/java/com/ffii/fpsms/modules/master/web/ProductionScheduleController.kt @@ -4,7 +4,7 @@ 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.ProdScheduleInfo -import com.ffii.fpsms.modules.master.entity.projections.ProdScheduleWithLine +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 @@ -42,8 +42,8 @@ class ProductionScheduleController( } @GetMapping("/detail/{id}") - fun getScheduleDetail(@PathVariable id: Long): ProdScheduleWithLine { - return productionScheduleService.prodScheduleDetail(id) + fun getScheduleDetail(@PathVariable id: Long): RoughProdScheduleWithLine { + return productionScheduleService.roughProdScheduleDetail(id) } @GetMapping("/getRecordByPage") @@ -54,16 +54,19 @@ class ProductionScheduleController( @RequestMapping(value = ["/testDetailSchedule"], method = [RequestMethod.GET]) fun generateDetailSchedule(request: HttpServletRequest?): Int { try { + // For test + val genDate = request?.getParameter("genDate").let { LocalDateTime.parse(it) } + val today = LocalDateTime.now() val latestRoughScheduleAt = productionScheduleService.getLatestScheduleAt("rough") - val assignDate = abs(Duration.between(latestRoughScheduleAt, today).toDays() % 7) - val finalAssignDate = if (assignDate.toInt() == 0) { - 1 - } else assignDate.toInt() + val assignDate = abs(Duration.between(latestRoughScheduleAt, today).toDays() % 7) + 1 +// val finalAssignDate = if (assignDate.toInt() == 0) { +// 1 +// } else assignDate.toInt() //TODO: update this to receive selectedDate and assignDate from schedule // productionScheduleService.generateDetailedScheduleByDay(1, LocalDateTime.of(2025,6,25,0,0,0)) println("today: $today | latestRoughScheduleAty: $latestRoughScheduleAt | assignDate: $assignDate ") - productionScheduleService.generateDetailedScheduleByDay(finalAssignDate, LocalDateTime.now()) + productionScheduleService.generateDetailedScheduleByDay(assignDate.toInt(), genDate ?: LocalDateTime.now()) return 200 } catch (e: Exception) { throw RuntimeException("Error generate schedule: ${e.message}", e) diff --git a/src/main/java/com/ffii/fpsms/modules/master/web/models/SearchProdScheduleRequest.kt b/src/main/java/com/ffii/fpsms/modules/master/web/models/SearchProdScheduleRequest.kt index fe0a0de..c894d82 100644 --- a/src/main/java/com/ffii/fpsms/modules/master/web/models/SearchProdScheduleRequest.kt +++ b/src/main/java/com/ffii/fpsms/modules/master/web/models/SearchProdScheduleRequest.kt @@ -8,7 +8,7 @@ data class SearchProdScheduleRequest ( val schedulePeriod: String?, val schedulePeriodTo: String?, val totalEstProdCount: Double?, - val type: String?, + val types: List?, val pageSize: Int?, val pageNum: Int?, ) \ No newline at end of file