| @@ -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<ProductionSchedule, | |||
| and (:schedulePeriod = '' or datediff(schedulePeriod, coalesce(:schedulePeriod, schedulePeriod)) = 0) | |||
| and (:schedulePeriodTo = '' or datediff(schedulePeriodTo, coalesce(:schedulePeriodTo, schedulePeriodTo)) = 0) | |||
| and (:totalEstProdCount is null or :totalEstProdCount = '' or totalEstProdCount = :totalEstProdCount) | |||
| and (:type is null or type = :type) | |||
| and (coalesce(:types) is null or type in :types) | |||
| order by id desc; | |||
| """, | |||
| countQuery = | |||
| @@ -67,7 +65,7 @@ interface ProductionScheduleRepository : AbstractRepository<ProductionSchedule, | |||
| and (:schedulePeriod = '' or datediff(schedulePeriod, coalesce(:schedulePeriod, schedulePeriod)) = 0) | |||
| and (:schedulePeriodTo = '' or datediff(schedulePeriodTo, coalesce(:schedulePeriodTo, schedulePeriodTo)) = 0) | |||
| and (:totalEstProdCount is null or :totalEstProdCount = '' or totalEstProdCount = :totalEstProdCount) | |||
| and (:type is null or type = :type) | |||
| and (coalesce(:types) is null or type in :types) | |||
| order by id desc; | |||
| """, | |||
| ) | |||
| @@ -76,7 +74,90 @@ interface ProductionScheduleRepository : AbstractRepository<ProductionSchedule, | |||
| schedulePeriod: String?, | |||
| schedulePeriodTo: String?, | |||
| totalEstProdCount: Double?, | |||
| type: String?, | |||
| types: List<String>?, | |||
| pageable: Pageable | |||
| ): Page<ProdScheduleInfo> | |||
| @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<DetailedProdScheduleWithLine> | |||
| } | |||
| @@ -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<DetailedProdScheduleLineInfo>? | |||
| ) | |||
| data class DetailedProdScheduleLineInfo( | |||
| val id: Long?, | |||
| val bomMaterials: List<DetailedProdScheduleLineBomMaterial>?, | |||
| val jobNo: String?, | |||
| val code: String?, | |||
| val name: String?, | |||
| val type: String?, | |||
| val demandQty: BigDecimal?, | |||
| val prodTimeInMinute: List<DetailedProdScheduleLineProdTime>?, | |||
| 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<ProdScheduleLineInfoByFg>, | |||
| val prodScheduleLinesByFgByDate: Map<Long?, List<ProdScheduleLineInfoByFg>>, | |||
| val prodScheduleLinesByBom: List<ProdScheduleLineInfoByBom>, | |||
| val prodScheduleLinesByBomByDate: Map<Long?, List<ProdScheduleLineInfoByBomByDate>> | |||
| val prodScheduleLinesByFg: List<RoughProdScheduleLineInfoByFg>, | |||
| val prodScheduleLinesByFgByDate: Map<Long?, List<RoughProdScheduleLineInfoByFg>>, | |||
| val prodScheduleLinesByBom: List<RoughProdScheduleLineInfoByBom>, | |||
| val prodScheduleLinesByBomByDate: Map<Long?, List<RoughProdScheduleLineInfoByBomByDate>> | |||
| ) | |||
| 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<ProdScheduleLineBomMaterialInfoByFg>?, | |||
| val bomMaterials: List<RoughProdScheduleLineBomMaterialInfoByFg>?, | |||
| 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?, | |||
| @@ -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<ProdScheduleInfo>(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 { | |||
| @@ -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) | |||
| @@ -8,7 +8,7 @@ data class SearchProdScheduleRequest ( | |||
| val schedulePeriod: String?, | |||
| val schedulePeriodTo: String?, | |||
| val totalEstProdCount: Double?, | |||
| val type: String?, | |||
| val types: List<String>?, | |||
| val pageSize: Int?, | |||
| val pageNum: Int?, | |||
| ) | |||