@@ -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?, | |||
) |