| @@ -28,10 +28,14 @@ open class M18SchedulerService( | |||||
| ) { | ) { | ||||
| var logger: Logger = LoggerFactory.getLogger(JwtTokenUtil::class.java) | var logger: Logger = LoggerFactory.getLogger(JwtTokenUtil::class.java) | ||||
| val dataStringFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd") | val dataStringFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd") | ||||
| val defaultCronExpression = "0 0 2 * * *"; | |||||
| @Volatile | @Volatile | ||||
| var scheduledM18Po: ScheduledFuture<*>? = null | var scheduledM18Po: ScheduledFuture<*>? = null | ||||
| @Volatile | |||||
| var scheduledM18Master: ScheduledFuture<*>? = null | |||||
| fun isValidCronExpression(cronExpression: String): Boolean { | fun isValidCronExpression(cronExpression: String): Boolean { | ||||
| return try { | return try { | ||||
| CronTrigger(cronExpression) | CronTrigger(cronExpression) | ||||
| @@ -43,35 +47,39 @@ open class M18SchedulerService( | |||||
| @PostConstruct | @PostConstruct | ||||
| fun init() { | fun init() { | ||||
| scheduleM18PoTask() | scheduleM18PoTask() | ||||
| scheduleM18MasterData() | |||||
| } | } | ||||
| fun scheduleM18PoTask() { | |||||
| val defaultCronExpression = "0 0 2 * * *"; | |||||
| scheduledM18Po?.cancel(false) | |||||
| fun commonSchedule(scheduled: ScheduledFuture<*>?, settingName: String, scheduleFunc: () -> Unit) { | |||||
| scheduled?.cancel(false) | |||||
| var cron = settingsService.findByName(SettingNames.SCHEDULE_M18_PO).getOrNull()?.value ?: defaultCronExpression; | |||||
| var cron = settingsService.findByName(settingName).getOrNull()?.value ?: defaultCronExpression; | |||||
| if (!isValidCronExpression(cron)) { | if (!isValidCronExpression(cron)) { | ||||
| cron = defaultCronExpression | cron = defaultCronExpression | ||||
| } | } | ||||
| scheduledM18Po = taskScheduler.schedule( | scheduledM18Po = taskScheduler.schedule( | ||||
| { | { | ||||
| // testTask(); | |||||
| getM18Pos() | |||||
| scheduleFunc() | |||||
| }, | }, | ||||
| CronTrigger(cron) | CronTrigger(cron) | ||||
| ) | ) | ||||
| println("Cron: $cron") | |||||
| } | } | ||||
| fun testTask() { | |||||
| println("Test: ${LocalDateTime.now()}") | |||||
| // Scheduler | |||||
| fun scheduleM18PoTask() { | |||||
| commonSchedule(scheduledM18Po, SettingNames.SCHEDULE_M18_PO, ::getM18Pos) | |||||
| } | } | ||||
| fun scheduleM18MasterData() { | |||||
| commonSchedule(scheduledM18Master, SettingNames.SCHEDULE_M18_MASTER, ::getM18MasterData) | |||||
| } | |||||
| // Tasks | |||||
| // @Async | // @Async | ||||
| // @Scheduled(cron = "0 0 2 * * *") // (SS/MM/HH/DD/MM/YY) | |||||
| // @Scheduled(cron = "0 0 2 * * *") // (SS/MM/HH/DD/MM/YY) | // @Scheduled(cron = "0 0 2 * * *") // (SS/MM/HH/DD/MM/YY) | ||||
| open fun getM18Pos() { | open fun getM18Pos() { | ||||
| logger.info("Daily Scheduler - PO") | |||||
| val currentTime = LocalDateTime.now() | val currentTime = LocalDateTime.now() | ||||
| val today = currentTime.toLocalDate().atStartOfDay() | val today = currentTime.toLocalDate().atStartOfDay() | ||||
| val yesterday = today.minusDays(1L) | val yesterday = today.minusDays(1L) | ||||
| @@ -84,4 +92,22 @@ open class M18SchedulerService( | |||||
| logger.info("today: ${today.format(dataStringFormat)}") | logger.info("today: ${today.format(dataStringFormat)}") | ||||
| logger.info("yesterday: ${yesterday.format(dataStringFormat)}") | logger.info("yesterday: ${yesterday.format(dataStringFormat)}") | ||||
| } | } | ||||
| open fun getM18MasterData() { | |||||
| logger.info("Daily Scheduler - Master Data") | |||||
| val currentTime = LocalDateTime.now() | |||||
| val today = currentTime.toLocalDate().atStartOfDay() | |||||
| val yesterday = today.minusDays(1L) | |||||
| val request = M18CommonRequest( | |||||
| modifiedDateTo = today.format(dataStringFormat), | |||||
| modifiedDateFrom = yesterday.format(dataStringFormat) | |||||
| ) | |||||
| m18MasterDataService.saveUnits(request) | |||||
| m18MasterDataService.saveProducts(request) | |||||
| m18MasterDataService.saveVendors(request) | |||||
| m18MasterDataService.saveBusinessUnits(request) | |||||
| m18MasterDataService.saveCurrencies(request) | |||||
| logger.info("today: ${today.format(dataStringFormat)}") | |||||
| logger.info("yesterday: ${yesterday.format(dataStringFormat)}") | |||||
| } | |||||
| } | } | ||||
| @@ -5,6 +5,7 @@ import com.ffii.fpsms.m18.M18Config | |||||
| import com.ffii.fpsms.m18.service.* | import com.ffii.fpsms.m18.service.* | ||||
| import com.ffii.fpsms.m18.web.models.M18CommonRequest | import com.ffii.fpsms.m18.web.models.M18CommonRequest | ||||
| import com.ffii.fpsms.modules.common.SettingNames | import com.ffii.fpsms.modules.common.SettingNames | ||||
| import com.ffii.fpsms.modules.common.scheduler.SchedulerService | |||||
| import com.ffii.fpsms.modules.master.entity.ItemUom | import com.ffii.fpsms.modules.master.entity.ItemUom | ||||
| import com.ffii.fpsms.modules.master.entity.Items | import com.ffii.fpsms.modules.master.entity.Items | ||||
| import com.ffii.fpsms.modules.master.entity.ShopRepository | import com.ffii.fpsms.modules.master.entity.ShopRepository | ||||
| @@ -31,7 +32,7 @@ class M18TestController ( | |||||
| private val itemUomService: ItemUomService, | private val itemUomService: ItemUomService, | ||||
| private val m18PurchaseQuotationService: M18PurchaseQuotationService, | private val m18PurchaseQuotationService: M18PurchaseQuotationService, | ||||
| private val m18DeliveryOrderService: M18DeliveryOrderService, | private val m18DeliveryOrderService: M18DeliveryOrderService, | ||||
| val m18SchedulerService: M18SchedulerService, | |||||
| val schedulerService: SchedulerService, | |||||
| private val settingsService: SettingsService, | private val settingsService: SettingsService, | ||||
| ) { | ) { | ||||
| var logger: Logger = LoggerFactory.getLogger(JwtTokenUtil::class.java) | var logger: Logger = LoggerFactory.getLogger(JwtTokenUtil::class.java) | ||||
| @@ -60,13 +61,13 @@ class M18TestController ( | |||||
| @GetMapping("/test4") | @GetMapping("/test4") | ||||
| fun test4(): Any { | fun test4(): Any { | ||||
| return m18SchedulerService.getM18Pos(); | |||||
| return schedulerService.getM18Pos(); | |||||
| } | } | ||||
| // --------------------------------------------- Scheduler --------------------------------------------- /// | // --------------------------------------------- Scheduler --------------------------------------------- /// | ||||
| @GetMapping("/schedule/po") // | @GetMapping("/schedule/po") // | ||||
| fun schedulePo(@RequestParam @Valid newCron: String) { | fun schedulePo(@RequestParam @Valid newCron: String) { | ||||
| settingsService.update(SettingNames.SCHEDULE_M18_PO, newCron); | settingsService.update(SettingNames.SCHEDULE_M18_PO, newCron); | ||||
| m18SchedulerService.scheduleM18PoTask() | |||||
| schedulerService.scheduleM18PoTask() | |||||
| } | } | ||||
| // --------------------------------------------- Master Data --------------------------------------------- /// | // --------------------------------------------- Master Data --------------------------------------------- /// | ||||
| @@ -25,6 +25,11 @@ public abstract class SettingNames { | |||||
| */ | */ | ||||
| public static final String SCHEDULE_M18_PO = "SCHEDULE.m18.po"; | public static final String SCHEDULE_M18_PO = "SCHEDULE.m18.po"; | ||||
| public static final String SCHEDULE_M18_MASTER = "SCHEDULE.m18.master"; | |||||
| public static final String SCHEDULE_PROD_ROUGH = "SCHEDULE.prod.rough"; | |||||
| public static final String SCHEDULE_PROD_DETAILED = "SCHEDULE.prod.detailed"; | |||||
| /* | /* | ||||
| * Mail settings | * Mail settings | ||||
| */ | */ | ||||
| @@ -0,0 +1,186 @@ | |||||
| package com.ffii.fpsms.modules.common.scheduler | |||||
| import com.ffii.core.utils.JwtTokenUtil | |||||
| import com.ffii.fpsms.m18.service.M18DeliveryOrderService | |||||
| import com.ffii.fpsms.m18.service.M18MasterDataService | |||||
| import com.ffii.fpsms.m18.service.M18PurchaseOrderService | |||||
| import com.ffii.fpsms.m18.web.models.M18CommonRequest | |||||
| import com.ffii.fpsms.modules.common.SettingNames | |||||
| import com.ffii.fpsms.modules.master.service.ProductionScheduleService | |||||
| import com.ffii.fpsms.modules.settings.service.SettingsService | |||||
| import jakarta.annotation.PostConstruct | |||||
| import org.slf4j.Logger | |||||
| import org.slf4j.LoggerFactory | |||||
| import org.springframework.scheduling.TaskScheduler | |||||
| import org.springframework.scheduling.support.CronTrigger | |||||
| import org.springframework.stereotype.Service | |||||
| import java.time.LocalDate | |||||
| import java.time.LocalDateTime | |||||
| import java.time.format.DateTimeFormatter | |||||
| import java.util.HashMap | |||||
| import java.util.concurrent.ScheduledFuture | |||||
| import kotlin.jvm.optionals.getOrNull | |||||
| @Service | |||||
| open class SchedulerService( | |||||
| val settingsService: SettingsService, | |||||
| val taskScheduler: TaskScheduler, | |||||
| val productionScheduleService: ProductionScheduleService, | |||||
| val m18PurchaseOrderService: M18PurchaseOrderService, | |||||
| val m18DeliveryOrderService: M18DeliveryOrderService, | |||||
| val m18MasterDataService: M18MasterDataService, | |||||
| ) { | |||||
| var logger: Logger = LoggerFactory.getLogger(JwtTokenUtil::class.java) | |||||
| val dataStringFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd") | |||||
| val defaultCronExpression = "0 0 2 * * *"; | |||||
| @Volatile | |||||
| var scheduledM18Po: ScheduledFuture<*>? = null | |||||
| @Volatile | |||||
| var scheduledM18Master: ScheduledFuture<*>? = null | |||||
| @Volatile | |||||
| var scheduledRoughProd: ScheduledFuture<*>? = null | |||||
| @Volatile | |||||
| var scheduledDetailedProd: ScheduledFuture<*>? = null | |||||
| // Common Function | |||||
| fun isValidCronExpression(cronExpression: String): Boolean { | |||||
| return try { | |||||
| CronTrigger(cronExpression) | |||||
| true | |||||
| } catch (e: IllegalArgumentException) { | |||||
| false | |||||
| } | |||||
| } | |||||
| fun commonSchedule(scheduled: ScheduledFuture<*>?, settingName: String, scheduleFunc: () -> Unit) { | |||||
| scheduled?.cancel(false) | |||||
| var cron = settingsService.findByName(settingName).getOrNull()?.value ?: defaultCronExpression; | |||||
| if (!isValidCronExpression(cron)) { | |||||
| cron = defaultCronExpression | |||||
| } | |||||
| scheduledM18Po = taskScheduler.schedule( | |||||
| { | |||||
| scheduleFunc() | |||||
| }, | |||||
| CronTrigger(cron) | |||||
| ) | |||||
| } | |||||
| // Init Scheduler | |||||
| @PostConstruct | |||||
| fun init() { | |||||
| scheduleM18PoTask(); | |||||
| scheduleM18MasterData(); | |||||
| scheduleRoughProd(); | |||||
| scheduleDetailedProd(); | |||||
| } | |||||
| // Scheduler | |||||
| // --------------------------- FP-MTMS --------------------------- // | |||||
| fun scheduleRoughProd() { | |||||
| commonSchedule(scheduledRoughProd, SettingNames.SCHEDULE_PROD_ROUGH, ::getRoughProdSchedule) | |||||
| } | |||||
| fun scheduleDetailedProd() { | |||||
| commonSchedule(scheduledDetailedProd, SettingNames.SCHEDULE_PROD_DETAILED, ::getDetailedProdSchedule) | |||||
| } | |||||
| // --------------------------- M18 --------------------------- // | |||||
| fun scheduleM18PoTask() { | |||||
| commonSchedule(scheduledM18Po, SettingNames.SCHEDULE_M18_PO, ::getM18Pos) | |||||
| } | |||||
| fun scheduleM18MasterData() { | |||||
| commonSchedule(scheduledM18Master, SettingNames.SCHEDULE_M18_MASTER, ::getM18MasterData) | |||||
| } | |||||
| // Function for schedule | |||||
| // --------------------------- FP-MTMS --------------------------- // | |||||
| open fun getRoughProdSchedule() { | |||||
| try { | |||||
| logger.info("Daily Scheduler - Rough Prod") | |||||
| val demoFGList = productionScheduleService.convertToFinishedGoodList(); | |||||
| val result: HashMap<ProductionScheduleService.RoughScheduleObj, Double> = productionScheduleService.generateRoughScheduleByWeek(demoFGList) | |||||
| val sortedEntries = result.entries.sortedBy { it.value } | |||||
| var accProdCount = 0.0; | |||||
| var fgCount = 0L; | |||||
| for ((roughScheduleRecord, totalDifference) in sortedEntries) { | |||||
| accProdCount += roughScheduleRecord.totalProductionCount; | |||||
| fgCount ++; | |||||
| println("[totalDifference:" + totalDifference + "] - " + roughScheduleRecord.toString()) | |||||
| } | |||||
| // Convert to List<HashMap<RoughScheduleObj, Double>> | |||||
| productionScheduleService.saveRoughScheduleOutput(sortedEntries, accProdCount, fgCount); | |||||
| } catch (e: Exception) { | |||||
| throw RuntimeException("Error generate schedule: ${e.message}", e) | |||||
| } | |||||
| } | |||||
| open fun getDetailedProdSchedule() { | |||||
| try { | |||||
| logger.info("Daily Scheduler - Detailed Prod") | |||||
| // For test | |||||
| val today = LocalDateTime.now() | |||||
| // T, T+1, T+2 | |||||
| for (day in longArrayOf(0L, 1L, 2L)) { | |||||
| val produceAt = today.plusDays(day) | |||||
| val latestRoughScheduleAt = productionScheduleService.getScheduledAtByDate(produceAt.toLocalDate()); | |||||
| // assume schedule period is monday to sunday | |||||
| val assignDate = produceAt.dayOfWeek.value | |||||
| //TODO: update this to receive selectedDate and assignDate from schedule | |||||
| println("produceAt: $produceAt | today: $today | latestRoughScheduleAt: $latestRoughScheduleAt | assignDate: $assignDate ") | |||||
| productionScheduleService.generateDetailedScheduleByDay(assignDate, latestRoughScheduleAt.atStartOfDay(), produceAt) | |||||
| } | |||||
| } catch (e: Exception) { | |||||
| throw RuntimeException("Error generate schedule: ${e.message}", e) | |||||
| } | |||||
| } | |||||
| // --------------------------- M18 --------------------------- // | |||||
| // @Async | |||||
| // @Scheduled(cron = "0 0 2 * * *") // (SS/MM/HH/DD/MM/YY) | |||||
| open fun getM18Pos() { | |||||
| logger.info("Daily Scheduler - PO") | |||||
| val currentTime = LocalDateTime.now() | |||||
| val today = currentTime.toLocalDate().atStartOfDay() | |||||
| val yesterday = today.minusDays(1L) | |||||
| val request = M18CommonRequest( | |||||
| modifiedDateTo = today.format(dataStringFormat), | |||||
| modifiedDateFrom = yesterday.format(dataStringFormat) | |||||
| ) | |||||
| m18PurchaseOrderService.savePurchaseOrders(request); | |||||
| m18DeliveryOrderService.saveDeliveryOrders(request); | |||||
| logger.info("today: ${today.format(dataStringFormat)}") | |||||
| logger.info("yesterday: ${yesterday.format(dataStringFormat)}") | |||||
| } | |||||
| open fun getM18MasterData() { | |||||
| logger.info("Daily Scheduler - Master Data") | |||||
| val currentTime = LocalDateTime.now() | |||||
| val today = currentTime.toLocalDate().atStartOfDay() | |||||
| val yesterday = today.minusDays(1L) | |||||
| val request = M18CommonRequest( | |||||
| modifiedDateTo = today.format(dataStringFormat), | |||||
| modifiedDateFrom = yesterday.format(dataStringFormat) | |||||
| ) | |||||
| m18MasterDataService.saveUnits(request) | |||||
| m18MasterDataService.saveProducts(request) | |||||
| // m18MasterDataService.saveBoms(request) | |||||
| m18MasterDataService.saveVendors(request) | |||||
| m18MasterDataService.saveBusinessUnits(request) | |||||
| m18MasterDataService.saveCurrencies(request) | |||||
| logger.info("today: ${today.format(dataStringFormat)}") | |||||
| logger.info("yesterday: ${yesterday.format(dataStringFormat)}") | |||||
| } | |||||
| } | |||||
| @@ -39,6 +39,7 @@ import org.springframework.data.domain.Pageable | |||||
| import org.springframework.stereotype.Service | import org.springframework.stereotype.Service | ||||
| import org.springframework.transaction.annotation.Transactional | import org.springframework.transaction.annotation.Transactional | ||||
| import java.io.IOException | import java.io.IOException | ||||
| import java.math.BigDecimal | |||||
| import java.util.HashMap | import java.util.HashMap | ||||
| import java.util.Objects | import java.util.Objects | ||||
| import kotlin.jvm.optionals.getOrDefault | import kotlin.jvm.optionals.getOrDefault | ||||
| @@ -52,6 +53,21 @@ open class JobOrderProcessService( | |||||
| private val jobOrderRepository: JobOrderRepository, | private val jobOrderRepository: JobOrderRepository, | ||||
| ) : AbstractBaseEntityService<JobOrderProcess, Long, JobOrderProcessRepository>(jdbcDao, jobOrderProcessRepository) { | ) : AbstractBaseEntityService<JobOrderProcess, Long, JobOrderProcessRepository>(jdbcDao, jobOrderProcessRepository) { | ||||
| open fun createJobOrderProcessRequests(joId: Long): List<CreateJobOrderProcessRequest> { | |||||
| val zero = BigDecimal.ZERO | |||||
| val jo = jobOrderRepository.findById(joId).getOrNull() ?: throw NoSuchElementException() | |||||
| val jopRequests = jo.bom?.bomProcesses?.map { bp -> | |||||
| CreateJobOrderProcessRequest( | |||||
| joId = jo.id, | |||||
| processId = bp.process?.id, | |||||
| seqNo = bp.seqNo, | |||||
| ) | |||||
| } ?: listOf(); | |||||
| return jopRequests | |||||
| } | |||||
| open fun createJobOrderProcesses(request: List<CreateJobOrderProcessRequest>): MessageResponse{ | open fun createJobOrderProcesses(request: List<CreateJobOrderProcessRequest>): MessageResponse{ | ||||
| val joProcesses = request.map { req -> | val joProcesses = request.map { req -> | ||||
| val jo = req.joId?.let { jobOrderRepository.findById(it).getOrNull() } | val jo = req.joId?.let { jobOrderRepository.findById(it).getOrNull() } | ||||
| @@ -110,6 +126,10 @@ open class JobOrderProcessService( | |||||
| } | } | ||||
| } | } | ||||
| fun createJobOrderProcessesByJoId(joId: Long): MessageResponse { | |||||
| return createJobOrderProcesses(createJobOrderProcessRequests(joId)); | |||||
| } | |||||
| // open fun isCorrectMachineUsed(request: MachineRequest): MessageResponse{ | // open fun isCorrectMachineUsed(request: MachineRequest): MessageResponse{ | ||||
| // // Further development, this with check equipment for the job process | // // Further development, this with check equipment for the job process | ||||
| // | // | ||||
| @@ -4,6 +4,7 @@ import com.ffii.core.response.RecordsRes | |||||
| import com.ffii.fpsms.modules.jobOrder.entity.projections.JobOrderDetail | import com.ffii.fpsms.modules.jobOrder.entity.projections.JobOrderDetail | ||||
| import com.ffii.fpsms.modules.jobOrder.entity.projections.JobOrderInfo | import com.ffii.fpsms.modules.jobOrder.entity.projections.JobOrderInfo | ||||
| import com.ffii.fpsms.modules.jobOrder.service.JobOrderBomMaterialService | 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.service.JobOrderService | ||||
| import com.ffii.fpsms.modules.jobOrder.web.model.CreateJobOrderRequest | 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.JobOrderReleaseRequest | ||||
| @@ -23,6 +24,7 @@ import org.springframework.web.bind.annotation.RestController | |||||
| class JobOrderController( | class JobOrderController( | ||||
| private val jobOrderService: JobOrderService, | private val jobOrderService: JobOrderService, | ||||
| private val jobOrderBomMaterialService: JobOrderBomMaterialService, | private val jobOrderBomMaterialService: JobOrderBomMaterialService, | ||||
| private val jobOrderProcessService: JobOrderProcessService | |||||
| ) { | ) { | ||||
| @GetMapping("/getRecordByPage") | @GetMapping("/getRecordByPage") | ||||
| @@ -47,6 +49,7 @@ class JobOrderController( | |||||
| throw NoSuchElementException() | throw NoSuchElementException() | ||||
| } | } | ||||
| jobOrderBomMaterialService.createJobOrderBomMaterialsByJoId(jo.id) | jobOrderBomMaterialService.createJobOrderBomMaterialsByJoId(jo.id) | ||||
| jobOrderProcessService.createJobOrderProcessesByJoId(jo.id) | |||||
| return jo | return jo | ||||
| } | } | ||||
| @@ -11,6 +11,9 @@ open class ProductionSchedule : BaseEntity<Long>() { | |||||
| @Column(name = "scheduleAt") | @Column(name = "scheduleAt") | ||||
| open var scheduleAt: LocalDateTime = LocalDateTime.now() | open var scheduleAt: LocalDateTime = LocalDateTime.now() | ||||
| @Column(name = "produceAt") | |||||
| open var produceAt: LocalDateTime? = null; | |||||
| @Column(name = "totalEstProdCount") | @Column(name = "totalEstProdCount") | ||||
| open var totalEstProdCount: Double = 0.0 | open var totalEstProdCount: Double = 0.0 | ||||
| @@ -18,6 +18,9 @@ interface ProductionScheduleRepository : AbstractRepository<ProductionSchedule, | |||||
| ) | ) | ||||
| fun getLatestRoughScheduleAt(type: String?): LocalDateTime | fun getLatestRoughScheduleAt(type: String?): LocalDateTime | ||||
| fun findByTypeAndProduceAtAndDeletedIsFalse(type: String, produceAt: LocalDateTime): ProductionSchedule? | |||||
| // Mainly for rough prod schedule page | |||||
| @Query( | @Query( | ||||
| nativeQuery = true, | nativeQuery = true, | ||||
| value = | value = | ||||
| @@ -27,7 +30,6 @@ interface ProductionScheduleRepository : AbstractRepository<ProductionSchedule, | |||||
| ps.id, | ps.id, | ||||
| ps.deleted, | ps.deleted, | ||||
| ps.scheduleAt, | ps.scheduleAt, | ||||
| -- ps.produceAt, | |||||
| date_add(ps.scheduleAt, interval 7 + 1 - if(weekday(ps.scheduleAt) = 6, 0, weekday(ps.scheduleAt) + 1) day) as schedulePeriod, | date_add(ps.scheduleAt, interval 7 + 1 - if(weekday(ps.scheduleAt) = 6, 0, weekday(ps.scheduleAt) + 1) day) as schedulePeriod, | ||||
| date_add(ps.scheduleAt, interval 7 + 1 + 6 - if(weekday(ps.scheduleAt) = 6, 0, weekday(ps.scheduleAt) + 1) day) as schedulePeriodTo, | date_add(ps.scheduleAt, interval 7 + 1 + 6 - if(weekday(ps.scheduleAt) = 6, 0, weekday(ps.scheduleAt) + 1) day) as schedulePeriodTo, | ||||
| ps.totalEstProdCount, | ps.totalEstProdCount, | ||||
| @@ -38,7 +40,6 @@ interface ProductionScheduleRepository : AbstractRepository<ProductionSchedule, | |||||
| select * from prod | select * from prod | ||||
| where deleted = false | where deleted = false | ||||
| and (:scheduleAt = '' or datediff(scheduleAt, coalesce(:scheduleAt, scheduleAt)) = 0) | and (:scheduleAt = '' or datediff(scheduleAt, coalesce(:scheduleAt, scheduleAt)) = 0) | ||||
| -- and (:produceAt = '' or datediff(produceAt, coalesce(:produceAt, produceAt)) = 0) | |||||
| and (:schedulePeriod = '' or datediff(schedulePeriod, coalesce(:schedulePeriod, schedulePeriod)) = 0) | and (:schedulePeriod = '' or datediff(schedulePeriod, coalesce(:schedulePeriod, schedulePeriod)) = 0) | ||||
| and (:schedulePeriodTo = '' or datediff(schedulePeriodTo, coalesce(:schedulePeriodTo, schedulePeriodTo)) = 0) | and (:schedulePeriodTo = '' or datediff(schedulePeriodTo, coalesce(:schedulePeriodTo, schedulePeriodTo)) = 0) | ||||
| and (:totalEstProdCount is null or :totalEstProdCount = '' or totalEstProdCount = :totalEstProdCount) | and (:totalEstProdCount is null or :totalEstProdCount = '' or totalEstProdCount = :totalEstProdCount) | ||||
| @@ -52,7 +53,6 @@ interface ProductionScheduleRepository : AbstractRepository<ProductionSchedule, | |||||
| ps.id, | ps.id, | ||||
| ps.deleted, | ps.deleted, | ||||
| ps.scheduleAt, | ps.scheduleAt, | ||||
| -- ps.produceAt, | |||||
| date_add(ps.scheduleAt, interval 7 + 1 - if(weekday(ps.scheduleAt) = 6, 0, weekday(ps.scheduleAt) + 1) day) as schedulePeriod, | date_add(ps.scheduleAt, interval 7 + 1 - if(weekday(ps.scheduleAt) = 6, 0, weekday(ps.scheduleAt) + 1) day) as schedulePeriod, | ||||
| date_add(ps.scheduleAt, interval 7 + 1 + 6 - if(weekday(ps.scheduleAt) = 6, 0, weekday(ps.scheduleAt) + 1) day) as schedulePeriodTo, | date_add(ps.scheduleAt, interval 7 + 1 + 6 - if(weekday(ps.scheduleAt) = 6, 0, weekday(ps.scheduleAt) + 1) day) as schedulePeriodTo, | ||||
| ps.totalEstProdCount, | ps.totalEstProdCount, | ||||
| @@ -63,7 +63,6 @@ interface ProductionScheduleRepository : AbstractRepository<ProductionSchedule, | |||||
| select count(*) from prod | select count(*) from prod | ||||
| where deleted = false | where deleted = false | ||||
| and (:scheduleAt = '' or datediff(scheduleAt, coalesce(:scheduleAt, scheduleAt)) = 0) | and (:scheduleAt = '' or datediff(scheduleAt, coalesce(:scheduleAt, scheduleAt)) = 0) | ||||
| -- and (:produceAt = '' or datediff(produceAt, coalesce(:produceAt, produceAt)) = 0) | |||||
| and (:schedulePeriod = '' or datediff(schedulePeriod, coalesce(:schedulePeriod, schedulePeriod)) = 0) | and (:schedulePeriod = '' or datediff(schedulePeriod, coalesce(:schedulePeriod, schedulePeriod)) = 0) | ||||
| and (:schedulePeriodTo = '' or datediff(schedulePeriodTo, coalesce(:schedulePeriodTo, schedulePeriodTo)) = 0) | and (:schedulePeriodTo = '' or datediff(schedulePeriodTo, coalesce(:schedulePeriodTo, schedulePeriodTo)) = 0) | ||||
| and (:totalEstProdCount is null or :totalEstProdCount = '' or totalEstProdCount = :totalEstProdCount) | and (:totalEstProdCount is null or :totalEstProdCount = '' or totalEstProdCount = :totalEstProdCount) | ||||
| @@ -73,7 +72,6 @@ interface ProductionScheduleRepository : AbstractRepository<ProductionSchedule, | |||||
| ) | ) | ||||
| fun findProdScheduleInfoByPage( | fun findProdScheduleInfoByPage( | ||||
| scheduleAt: String?, | scheduleAt: String?, | ||||
| // produceAt: String?, | |||||
| schedulePeriod: String?, | schedulePeriod: String?, | ||||
| schedulePeriodTo: String?, | schedulePeriodTo: String?, | ||||
| totalEstProdCount: Double?, | totalEstProdCount: Double?, | ||||
| @@ -81,6 +79,38 @@ interface ProductionScheduleRepository : AbstractRepository<ProductionSchedule, | |||||
| pageable: Pageable | pageable: Pageable | ||||
| ): Page<ProdScheduleInfo> | ): Page<ProdScheduleInfo> | ||||
| // Mainly for detailed prod schedule page | |||||
| @Query( | |||||
| nativeQuery = true, | |||||
| value = | |||||
| """ | |||||
| select | |||||
| ps.id, | |||||
| ps.deleted, | |||||
| ps.scheduleAt, | |||||
| ps.produceAt, | |||||
| ps.totalEstProdCount, | |||||
| ps.totalFGType, | |||||
| ps.`type` | |||||
| from production_schedule ps | |||||
| where deleted = false | |||||
| and produceAt is not null | |||||
| and scheduleAt in (select max(ps2.scheduleAt) from production_schedule ps2 group by ps2.produceAt) | |||||
| -- and (:scheduleAt = '' or datediff(scheduleAt, coalesce(:scheduleAt, scheduleAt)) = 0) | |||||
| and (:produceAt = '' or datediff(produceAt, coalesce(:produceAt, produceAt)) = 0) | |||||
| and (:totalEstProdCount is null or :totalEstProdCount = '' or totalEstProdCount = :totalEstProdCount) | |||||
| and (coalesce(:types) is null or type in :types) | |||||
| order by id desc; | |||||
| """ | |||||
| ) | |||||
| fun findProdScheduleInfoByProduceAtByPage( | |||||
| // scheduleAt: String?, | |||||
| produceAt: String?, | |||||
| totalEstProdCount: Double?, | |||||
| types: List<String>?, | |||||
| pageable: Pageable | |||||
| ): Page<ProdScheduleInfo> | |||||
| @Query(nativeQuery = true, | @Query(nativeQuery = true, | ||||
| value = | value = | ||||
| """ | """ | ||||
| @@ -49,7 +49,7 @@ open class ItemsService( | |||||
| open fun getRoughScheduleList(): List<Map<String, Any>> { | open fun getRoughScheduleList(): List<Map<String, Any>> { | ||||
| val now = LocalDateTime.now() | val now = LocalDateTime.now() | ||||
| val lastMonthStart = now.minusMonths(1).withDayOfMonth(1) // Start of last month | |||||
| val lastMonthStart = now.minusMonths(1).withDayOfMonth(1).withHour(0).withMinute(0).withSecond(0) // Start of last month | |||||
| val lastMonthEnd = now.minusDays(now.dayOfMonth.toLong()).withHour(23).withMinute(59).withSecond(59) // End of last month | val lastMonthEnd = now.minusDays(now.dayOfMonth.toLong()).withHour(23).withMinute(59).withSecond(59) // End of last month | ||||
| val curMonthStart = now.withDayOfMonth(1) // Start of last month | val curMonthStart = now.withDayOfMonth(1) // Start of last month | ||||
| @@ -82,6 +82,7 @@ open class ItemsService( | |||||
| + " WHERE do.deleted = false " | + " WHERE do.deleted = false " | ||||
| + " AND do.estimatedArrivalDate >= :lastMonthStart " | + " AND do.estimatedArrivalDate >= :lastMonthStart " | ||||
| + " AND do.estimatedArrivalDate <= :lastMonthEnd " | + " AND do.estimatedArrivalDate <= :lastMonthEnd " | ||||
| + " AND dol.itemId is not null " | |||||
| + " GROUP BY dol.itemId, dol.itemNo, i.name " | + " GROUP BY dol.itemId, dol.itemNo, i.name " | ||||
| ); | ); | ||||
| return jdbcDao.queryForList(sql.toString(), args); | return jdbcDao.queryForList(sql.toString(), args); | ||||
| @@ -16,6 +16,7 @@ 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.MessageResponse | ||||
| import com.ffii.fpsms.modules.master.web.models.ReleaseProdScheduleLineRequest | import com.ffii.fpsms.modules.master.web.models.ReleaseProdScheduleLineRequest | ||||
| import com.ffii.fpsms.modules.master.web.models.SearchProdScheduleRequest | import com.ffii.fpsms.modules.master.web.models.SearchProdScheduleRequest | ||||
| import com.ffii.fpsms.modules.settings.service.SettingsService | |||||
| import com.ffii.fpsms.modules.stock.entity.Inventory | import com.ffii.fpsms.modules.stock.entity.Inventory | ||||
| import com.ffii.fpsms.modules.stock.entity.InventoryRepository | import com.ffii.fpsms.modules.stock.entity.InventoryRepository | ||||
| import com.ffii.fpsms.modules.stock.service.InventoryService | import com.ffii.fpsms.modules.stock.service.InventoryService | ||||
| @@ -26,6 +27,7 @@ import com.google.gson.JsonDeserializer | |||||
| import com.google.gson.JsonElement | import com.google.gson.JsonElement | ||||
| import com.google.gson.reflect.TypeToken | import com.google.gson.reflect.TypeToken | ||||
| import org.springframework.data.domain.PageRequest | import org.springframework.data.domain.PageRequest | ||||
| import org.springframework.scheduling.support.CronTrigger | |||||
| import org.springframework.stereotype.Service | import org.springframework.stereotype.Service | ||||
| import org.springframework.transaction.annotation.Transactional | import org.springframework.transaction.annotation.Transactional | ||||
| import java.lang.reflect.Type | import java.lang.reflect.Type | ||||
| @@ -35,6 +37,7 @@ import java.time.DayOfWeek | |||||
| import java.time.LocalDate | import java.time.LocalDate | ||||
| import java.time.LocalDateTime | import java.time.LocalDateTime | ||||
| import java.time.format.DateTimeFormatter | import java.time.format.DateTimeFormatter | ||||
| import java.util.concurrent.ScheduledFuture | |||||
| import kotlin.collections.component1 | import kotlin.collections.component1 | ||||
| import kotlin.collections.component2 | import kotlin.collections.component2 | ||||
| import kotlin.jvm.optionals.getOrNull | import kotlin.jvm.optionals.getOrNull | ||||
| @@ -54,13 +57,15 @@ open class ProductionScheduleService( | |||||
| private val jobOrderProcessService: JobOrderProcessService, | private val jobOrderProcessService: JobOrderProcessService, | ||||
| private val inventoryService: InventoryService, | private val inventoryService: InventoryService, | ||||
| private val inventoryRepository: InventoryRepository, | private val inventoryRepository: InventoryRepository, | ||||
| private val itemUomService: ItemUomService | |||||
| private val itemUomService: ItemUomService, | |||||
| private val settingsService: SettingsService, | |||||
| ) : AbstractBaseEntityService<ProductionSchedule, Long, ProductionScheduleRepository>( | ) : AbstractBaseEntityService<ProductionSchedule, Long, ProductionScheduleRepository>( | ||||
| jdbcDao, | jdbcDao, | ||||
| productionScheduleRepository | productionScheduleRepository | ||||
| ) { | ) { | ||||
| // do mapping with projection | // do mapping with projection | ||||
| open val formatter: DateTimeFormatter? = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss") | open val formatter: DateTimeFormatter? = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss") | ||||
| val defaultCronExpression = "0 0 2 * * *"; | |||||
| open fun getLatestScheduleAt(type: String?): LocalDateTime { | open fun getLatestScheduleAt(type: String?): LocalDateTime { | ||||
| return productionScheduleRepository.getLatestRoughScheduleAt(type) | return productionScheduleRepository.getLatestRoughScheduleAt(type) | ||||
| @@ -69,7 +74,7 @@ open class ProductionScheduleService( | |||||
| open fun getScheduledAtByDate(inputDate: LocalDate): LocalDate { | open fun getScheduledAtByDate(inputDate: LocalDate): LocalDate { | ||||
| val daysToSubtract = when (inputDate.dayOfWeek) { | val daysToSubtract = when (inputDate.dayOfWeek) { | ||||
| DayOfWeek.WEDNESDAY -> 7 | DayOfWeek.WEDNESDAY -> 7 | ||||
| else -> (inputDate.dayOfWeek.value + 7 - DayOfWeek.WEDNESDAY.value) % 7 | |||||
| else -> inputDate.dayOfWeek.value + 7 - DayOfWeek.WEDNESDAY.value | |||||
| } | } | ||||
| val lastWednesday = inputDate.minusDays(daysToSubtract.toLong()) | val lastWednesday = inputDate.minusDays(daysToSubtract.toLong()) | ||||
| @@ -82,7 +87,6 @@ open class ProductionScheduleService( | |||||
| val response = productionScheduleRepository.findProdScheduleInfoByPage( | val response = productionScheduleRepository.findProdScheduleInfoByPage( | ||||
| scheduleAt = request.scheduleAt, | scheduleAt = request.scheduleAt, | ||||
| // produceAt = request.produceAt, | |||||
| schedulePeriod = request.schedulePeriod, | schedulePeriod = request.schedulePeriod, | ||||
| schedulePeriodTo = request.schedulePeriodTo, | schedulePeriodTo = request.schedulePeriodTo, | ||||
| totalEstProdCount = request.totalEstProdCount, | totalEstProdCount = request.totalEstProdCount, | ||||
| @@ -95,6 +99,29 @@ open class ProductionScheduleService( | |||||
| return RecordsRes<ProdScheduleInfo>(records, total.toInt()); | return RecordsRes<ProdScheduleInfo>(records, total.toInt()); | ||||
| } | } | ||||
| open fun allRoughProdSchedulesByPage(request: SearchProdScheduleRequest): RecordsRes<ProdScheduleInfo> { | |||||
| // request.apply { | |||||
| // types = listOf("rough") | |||||
| // } | |||||
| return allProdSchedulesByPage(request); | |||||
| } | |||||
| open fun allDetailedProdSchedulesByPage(request: SearchProdScheduleRequest): RecordsRes<ProdScheduleInfo> { | |||||
| val pageable = PageRequest.of(request.pageNum ?: 0, request.pageSize ?: 10); | |||||
| val response = productionScheduleRepository.findProdScheduleInfoByProduceAtByPage( | |||||
| produceAt = request.produceAt, | |||||
| totalEstProdCount = request.totalEstProdCount, | |||||
| // types = listOf("detailed", "manual"), | |||||
| types = request.types, | |||||
| pageable = pageable | |||||
| ) | |||||
| val records = response.content | |||||
| val total = response.totalElements | |||||
| return RecordsRes<ProdScheduleInfo>(records, total.toInt()); | |||||
| } | |||||
| open fun roughProdScheduleDetail(id: Long): RoughProdScheduleWithLine { | open fun roughProdScheduleDetail(id: Long): RoughProdScheduleWithLine { | ||||
| val zero = BigDecimal.ZERO | val zero = BigDecimal.ZERO | ||||
| val prodSchedule = productionScheduleRepository.findById(id).getOrNull() ?: throw NoSuchElementException() | val prodSchedule = productionScheduleRepository.findById(id).getOrNull() ?: throw NoSuchElementException() | ||||
| @@ -303,10 +330,18 @@ open class ProductionScheduleService( | |||||
| @Transactional(rollbackFor = [java.lang.Exception::class]) | @Transactional(rollbackFor = [java.lang.Exception::class]) | ||||
| open fun saveProdScheduleLine(request: ReleaseProdScheduleLineRequest): MessageResponse { | open fun saveProdScheduleLine(request: ReleaseProdScheduleLineRequest): MessageResponse { | ||||
| val prodScheduleLine = request.id.let { productionScheduleLineRepository.findById(it).getOrNull() } ?: throw NoSuchElementException() | val prodScheduleLine = request.id.let { productionScheduleLineRepository.findById(it).getOrNull() } ?: throw NoSuchElementException() | ||||
| val prodSchedule = prodScheduleLine.productionSchedule | |||||
| // Update Prod Schedule Type | |||||
| prodSchedule.apply { | |||||
| type = "manual" | |||||
| } | |||||
| productionScheduleRepository.saveAndFlush(prodSchedule) | |||||
| // Update Prod Schedule Line Prod qty | // Update Prod Schedule Line Prod qty | ||||
| prodScheduleLine.apply { | prodScheduleLine.apply { | ||||
| prodQty = request.demandQty.toDouble() | prodQty = request.demandQty.toDouble() | ||||
| type = "manual" | |||||
| } | } | ||||
| productionScheduleLineRepository.saveAndFlush(prodScheduleLine) | productionScheduleLineRepository.saveAndFlush(prodScheduleLine) | ||||
| @@ -329,10 +364,21 @@ open class ProductionScheduleService( | |||||
| val bom = prodScheduleLine.item.id?.let { bomService.findByItemId(it) } | val bom = prodScheduleLine.item.id?.let { bomService.findByItemId(it) } | ||||
| val approver = SecurityUtils.getUser().getOrNull() | val approver = SecurityUtils.getUser().getOrNull() | ||||
| val proportion = request.demandQty.divide(bom?.outputQty ?: BigDecimal.ONE, 5, RoundingMode.HALF_UP) | val proportion = request.demandQty.divide(bom?.outputQty ?: BigDecimal.ONE, 5, RoundingMode.HALF_UP) | ||||
| val isSameQty = request.demandQty.equals(prodScheduleLine.prodQty) | |||||
| // Update Prod Schedule Type | |||||
| if (!isSameQty) { | |||||
| val prodSchedule = prodScheduleLine.productionSchedule | |||||
| prodSchedule.apply { | |||||
| type = "manual" | |||||
| } | |||||
| productionScheduleRepository.save(prodSchedule) | |||||
| } | |||||
| // Update Prod Schedule Line Prod qty | // Update Prod Schedule Line Prod qty | ||||
| prodScheduleLine.apply { | prodScheduleLine.apply { | ||||
| prodQty = request.demandQty.toDouble() | prodQty = request.demandQty.toDouble() | ||||
| this.type = if (isSameQty) this.type else "manual" | |||||
| approverId = approver?.id | approverId = approver?.id | ||||
| } | } | ||||
| productionScheduleLineRepository.save(prodScheduleLine) | productionScheduleLineRepository.save(prodScheduleLine) | ||||
| @@ -459,9 +505,15 @@ open class ProductionScheduleService( | |||||
| return jdbcDao.queryForInt(sql.toString(), args); | return jdbcDao.queryForInt(sql.toString(), args); | ||||
| } | } | ||||
| open fun generateDetailedScheduleByDay(assignDate: Int, selectedDate: LocalDateTime) { | |||||
| open fun generateDetailedScheduleByDay(assignDate: Int, selectedDate: LocalDateTime, produceAt: LocalDateTime) { | |||||
| val detailedScheduleOutputList = ArrayList<ProductionScheduleRecord>() | val detailedScheduleOutputList = ArrayList<ProductionScheduleRecord>() | ||||
| // check the produce date have manual changes. | |||||
| val manualChange = productionScheduleRepository.findByTypeAndProduceAtAndDeletedIsFalse("detailed", produceAt) | |||||
| if (manualChange != null) { | |||||
| return; | |||||
| } | |||||
| //increasement available | //increasement available | ||||
| var idleProductionCount = 22000.0 - getDailyProductionCount(assignDate, selectedDate); | var idleProductionCount = 22000.0 - getDailyProductionCount(assignDate, selectedDate); | ||||
| @@ -518,17 +570,19 @@ open class ProductionScheduleService( | |||||
| tempPriority++ | tempPriority++ | ||||
| } | } | ||||
| saveDetailedScheduleOutput(sortedOutputList, accProdCount, fgCount) | |||||
| saveDetailedScheduleOutput(sortedOutputList, accProdCount, fgCount, produceAt) | |||||
| } | } | ||||
| open fun saveDetailedScheduleOutput( | open fun saveDetailedScheduleOutput( | ||||
| sortedEntries: List<ProductionScheduleRecord>, | sortedEntries: List<ProductionScheduleRecord>, | ||||
| accProdCount: Double, | accProdCount: Double, | ||||
| fgCount: Long | |||||
| fgCount: Long, | |||||
| produceAt: LocalDateTime, | |||||
| ) { | ) { | ||||
| val tempObj = ProductionSchedule() | val tempObj = ProductionSchedule() | ||||
| tempObj.id = -1; | tempObj.id = -1; | ||||
| tempObj.scheduleAt = LocalDateTime.now() | tempObj.scheduleAt = LocalDateTime.now() | ||||
| tempObj.produceAt = produceAt; | |||||
| tempObj.totalFGType = fgCount; | tempObj.totalFGType = fgCount; | ||||
| tempObj.totalEstProdCount = accProdCount; | tempObj.totalEstProdCount = accProdCount; | ||||
| tempObj.type = "detailed" | tempObj.type = "detailed" | ||||
| @@ -539,7 +593,6 @@ open class ProductionScheduleService( | |||||
| } | } | ||||
| } | } | ||||
| private fun saveDetailedScheduleLineToDatabase(parentId: Long, detailedScheduleObj: ProductionScheduleRecord) { | private fun saveDetailedScheduleLineToDatabase(parentId: Long, detailedScheduleObj: ProductionScheduleRecord) { | ||||
| try { | try { | ||||
| val prodSchedule = productionScheduleRepository.findById(parentId).get() | val prodSchedule = productionScheduleRepository.findById(parentId).get() | ||||
| @@ -627,7 +680,6 @@ open class ProductionScheduleService( | |||||
| sql.append(" ORDER BY psl.assignDate, psl.itemPriority ASC "); | sql.append(" ORDER BY psl.assignDate, psl.itemPriority ASC "); | ||||
| print(sql) | print(sql) | ||||
| print(args.toString()) | |||||
| val resultList = jdbcDao.queryForList(sql.toString(), args); | val resultList = jdbcDao.queryForList(sql.toString(), args); | ||||
| print(resultList) | print(resultList) | ||||
| @@ -2,6 +2,7 @@ package com.ffii.fpsms.modules.master.web | |||||
| import com.ffii.core.response.RecordsRes | import com.ffii.core.response.RecordsRes | ||||
| import com.ffii.core.utils.CriteriaArgsBuilder | import com.ffii.core.utils.CriteriaArgsBuilder | ||||
| import com.ffii.fpsms.modules.common.scheduler.SchedulerService | |||||
| import com.ffii.fpsms.modules.master.entity.ProductionScheduleRepository | 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.DetailedProdScheduleWithLine | ||||
| import com.ffii.fpsms.modules.master.entity.projections.ProdScheduleInfo | import com.ffii.fpsms.modules.master.entity.projections.ProdScheduleInfo | ||||
| @@ -30,6 +31,7 @@ import kotlin.math.abs | |||||
| class ProductionScheduleController( | class ProductionScheduleController( | ||||
| private val productionScheduleService: ProductionScheduleService, | private val productionScheduleService: ProductionScheduleService, | ||||
| private val productionScheduleRepository: ProductionScheduleRepository, | private val productionScheduleRepository: ProductionScheduleRepository, | ||||
| private val schedulerService: SchedulerService, | |||||
| ) { | ) { | ||||
| // @GetMapping | // @GetMapping | ||||
| // fun allItems(): List<Items> { | // fun allItems(): List<Items> { | ||||
| @@ -48,6 +50,16 @@ class ProductionScheduleController( | |||||
| return productionScheduleService.getLatestScheduleAt("rough") | return productionScheduleService.getLatestScheduleAt("rough") | ||||
| } | } | ||||
| @GetMapping("/test1") | |||||
| fun test1(): Any { | |||||
| return schedulerService.getRoughProdSchedule(); | |||||
| } | |||||
| @GetMapping("/test2") | |||||
| fun test2(): Any { | |||||
| return schedulerService.getDetailedProdSchedule(); | |||||
| } | |||||
| @GetMapping("/detail/rough/{id}") | @GetMapping("/detail/rough/{id}") | ||||
| fun getScheduleDetail(@PathVariable id: Long): RoughProdScheduleWithLine { | fun getScheduleDetail(@PathVariable id: Long): RoughProdScheduleWithLine { | ||||
| return productionScheduleService.roughProdScheduleDetail(id) | return productionScheduleService.roughProdScheduleDetail(id) | ||||
| @@ -73,7 +85,15 @@ class ProductionScheduleController( | |||||
| return productionScheduleService.allProdSchedulesByPage(request); | return productionScheduleService.allProdSchedulesByPage(request); | ||||
| } | } | ||||
| @GetMapping("/getRecordByPage/rough") | |||||
| fun allRoughProdSchedulesByPage(@ModelAttribute request: SearchProdScheduleRequest): RecordsRes<ProdScheduleInfo> { | |||||
| return productionScheduleService.allRoughProdSchedulesByPage(request); | |||||
| } | |||||
| @GetMapping("/getRecordByPage/detailed") | |||||
| fun allDetailedProdSchedulesByPage(@ModelAttribute request: SearchProdScheduleRequest): RecordsRes<ProdScheduleInfo> { | |||||
| return productionScheduleService.allDetailedProdSchedulesByPage(request); | |||||
| } | |||||
| @RequestMapping(value = ["/testDetailedSchedule"], method = [RequestMethod.GET]) | @RequestMapping(value = ["/testDetailedSchedule"], method = [RequestMethod.GET]) | ||||
| fun generateDetailSchedule(request: HttpServletRequest?): Int { | fun generateDetailSchedule(request: HttpServletRequest?): Int { | ||||
| @@ -98,7 +118,7 @@ class ProductionScheduleController( | |||||
| //TODO: update this to receive selectedDate and assignDate from schedule | //TODO: update this to receive selectedDate and assignDate from schedule | ||||
| // productionScheduleService.generateDetailedScheduleByDay(1, LocalDateTime.of(2025,6,25,0,0,0)) | // productionScheduleService.generateDetailedScheduleByDay(1, LocalDateTime.of(2025,6,25,0,0,0)) | ||||
| println("genDate: $genDate | today: $today | latestRoughScheduleAt: $latestRoughScheduleAt | assignDate: $assignDate ") | println("genDate: $genDate | today: $today | latestRoughScheduleAt: $latestRoughScheduleAt | assignDate: $assignDate ") | ||||
| productionScheduleService.generateDetailedScheduleByDay(assignDate, latestRoughScheduleAt ?: LocalDateTime.now()) | |||||
| productionScheduleService.generateDetailedScheduleByDay(assignDate, latestRoughScheduleAt ?: LocalDateTime.now(), genDate ?: today) | |||||
| return 200 | return 200 | ||||
| } catch (e: Exception) { | } catch (e: Exception) { | ||||
| throw RuntimeException("Error generate schedule: ${e.message}", e) | throw RuntimeException("Error generate schedule: ${e.message}", e) | ||||
| @@ -4,11 +4,12 @@ import java.time.LocalDate | |||||
| data class SearchProdScheduleRequest ( | data class SearchProdScheduleRequest ( | ||||
| val id: Long?, | val id: Long?, | ||||
| val scheduleAt: String?, | |||||
| val schedulePeriod: String?, | |||||
| val schedulePeriodTo: String?, | |||||
| val scheduleAt: String? = "", | |||||
| val produceAt: String? = "", | |||||
| val schedulePeriod: String? = "", | |||||
| val schedulePeriodTo: String? = "", | |||||
| val totalEstProdCount: Double?, | val totalEstProdCount: Double?, | ||||
| val types: List<String>?, | |||||
| var types: List<String>?, | |||||
| val pageSize: Int?, | val pageSize: Int?, | ||||
| val pageNum: Int?, | val pageNum: Int?, | ||||
| ) | ) | ||||
| @@ -23,6 +23,7 @@ data class PurchaseOrderDataClass( | |||||
| val orderDate: LocalDateTime?, | val orderDate: LocalDateTime?, | ||||
| val estimatedArrivalDate: LocalDateTime?, | val estimatedArrivalDate: LocalDateTime?, | ||||
| val completeDate: LocalDateTime?, | val completeDate: LocalDateTime?, | ||||
| val itemDetail: String, | |||||
| val status: String, | val status: String, | ||||
| val supplier: String?, | val supplier: String?, | ||||
| var escalated: Boolean? | var escalated: Boolean? | ||||
| @@ -65,22 +65,52 @@ open class PurchaseOrderService( | |||||
| // } | // } | ||||
| open fun getPoList(args: MutableMap<String, Any>): List<PurchaseOrderDataClass> { | open fun getPoList(args: MutableMap<String, Any>): List<PurchaseOrderDataClass> { | ||||
| val sql = StringBuilder( | val sql = StringBuilder( | ||||
| " select " | |||||
| + " po.*, " | |||||
| + " s.name as supplier, " | |||||
| + " CASE " | |||||
| + " WHEN sil.purchaseOrderId IS NOT NULL THEN 1 " | |||||
| + " ELSE 0 " | |||||
| + " END AS escalated " | |||||
| + " from purchase_order po " | |||||
| + " left join shop s on s.id = po.supplierId " | |||||
| + " left join ( " | |||||
| + " select " | |||||
| + " sil.purchaseOrderId " | |||||
| + " from stock_in_line sil " | |||||
| + " where sil.status like 'determine%' " | |||||
| + " ) sil on sil.purchaseOrderId = po.id " | |||||
| + " where po.deleted = false " | |||||
| "select * from ( " + | |||||
| "select " + | |||||
| " po.*," + | |||||
| " group_concat(" + | |||||
| " coalesce(i.code, \"N/A\"), " + | |||||
| " '-', " + | |||||
| " coalesce(i.name, \"N/A\")," + | |||||
| " ' ('," + | |||||
| " coalesce(uc.udfudesc, \"N/A\")," + | |||||
| " ')'," + | |||||
| " ': ', " + | |||||
| " coalesce(pol.qty, 0), " + | |||||
| " ' (', " + | |||||
| " coalesce(sil2.sumAcceptedQty, 0)," + | |||||
| " ')' " + | |||||
| " SEPARATOR ','" + | |||||
| " ) as itemDetail," + | |||||
| " s.name as supplier, " + | |||||
| " CASE " + | |||||
| " WHEN sil.purchaseOrderId IS NOT NULL THEN 1 " + | |||||
| " ELSE 0 " + | |||||
| " END AS escalated " + | |||||
| " from purchase_order po" + | |||||
| " left join purchase_order_line pol on pol.purchaseOrderId = po.id" + | |||||
| " left join items i on i.id = pol.itemId" + | |||||
| " left join shop s on s.id = po.supplierId " + | |||||
| " left join ( " + | |||||
| " select " + | |||||
| " sil.purchaseOrderId " + | |||||
| " from stock_in_line sil " + | |||||
| " where sil.status like 'determine%'" + | |||||
| " and sil.deleted = false" + | |||||
| " ) sil on sil.purchaseOrderId = po.id" + | |||||
| " left join ( " + | |||||
| " select " + | |||||
| " sil.purchaseOrderLineId, " + | |||||
| " sum(coalesce(sil.acceptedQty, 0)) as sumAcceptedQty " + | |||||
| " from stock_in_line sil " + | |||||
| " where sil.deleted = false " + | |||||
| " and sil.status = 'completed' " + | |||||
| " group by sil.purchaseOrderLineId " + | |||||
| " ) sil2 on sil2.purchaseOrderLineId = pol.id" + | |||||
| " left join item_uom iu on iu.itemId = pol.itemId and iu.purchaseUnit = true" + | |||||
| " left join uom_conversion uc on uc.id = iu.uomId" + | |||||
| " where po.deleted = false " + | |||||
| " and pol.deleted = false " | |||||
| ) | ) | ||||
| if (args.containsKey("code")){ | if (args.containsKey("code")){ | ||||
| sql.append(" AND po.code like :code "); | sql.append(" AND po.code like :code "); | ||||
| @@ -95,7 +125,24 @@ open class PurchaseOrderService( | |||||
| sql.append(" and sil.purchaseOrderId IS NULL "); | sql.append(" and sil.purchaseOrderId IS NULL "); | ||||
| } | } | ||||
| } | } | ||||
| if (args.containsKey("orderDate")){ | |||||
| sql.append(" AND po.orderDate >= :orderDate "); | |||||
| } | |||||
| if (args.containsKey("orderDateTo")){ | |||||
| sql.append(" AND po.orderDate <= :orderDateTo "); | |||||
| } | |||||
| if (args.containsKey("estimatedArrivalDate")){ | |||||
| sql.append(" AND po.estimatedArrivalDate >= :estimatedArrivalDate "); | |||||
| } | |||||
| if (args.containsKey("estimatedArrivalDateTo")){ | |||||
| sql.append(" AND po.estimatedArrivalDate <= :estimatedArrivalDateTo "); | |||||
| } | |||||
| sql.append(" group by po.id"); | |||||
| sql.append(" order by po.orderDate desc") | sql.append(" order by po.orderDate desc") | ||||
| sql.append(" ) r") | |||||
| if (args.containsKey("itemDetail")){ | |||||
| sql.append(" AND r.itemDetail like :itemDetail "); | |||||
| } | |||||
| val list = jdbcDao.queryForList(sql.toString(), args); | val list = jdbcDao.queryForList(sql.toString(), args); | ||||
| println(list) | println(list) | ||||
| @@ -105,6 +152,7 @@ open class PurchaseOrderService( | |||||
| code = it["code"] as String, | code = it["code"] as String, | ||||
| orderDate = it["orderDate"] as LocalDateTime?, | orderDate = it["orderDate"] as LocalDateTime?, | ||||
| estimatedArrivalDate = it["estimatedArrivalDate"] as LocalDateTime?, | estimatedArrivalDate = it["estimatedArrivalDate"] as LocalDateTime?, | ||||
| itemDetail = it["itemDetail"] as String, | |||||
| completeDate = it["completeDate"] as LocalDateTime?, | completeDate = it["completeDate"] as LocalDateTime?, | ||||
| status = it["status"] as String, | status = it["status"] as String, | ||||
| supplier = it["supplier"] as String?, | supplier = it["supplier"] as String?, | ||||
| @@ -35,6 +35,10 @@ class PurchaseOrderController( | |||||
| .addStringLike("code") | .addStringLike("code") | ||||
| .addString("status") | .addString("status") | ||||
| .addBoolean("escalated") | .addBoolean("escalated") | ||||
| .addDate("orderDate") | |||||
| .addDate("orderDateTo") | |||||
| .addDate("estimatedArrivalDate") | |||||
| .addDate("estimatedArrivalDateTo") | |||||
| .build() | .build() | ||||
| println(criteriaArgs) | println(criteriaArgs) | ||||
| val pageSize = request.getParameter("pageSize")?.toIntOrNull() ?: 10 // Default to 10 if not provided | val pageSize = request.getParameter("pageSize")?.toIntOrNull() ?: 10 // Default to 10 if not provided | ||||
| @@ -0,0 +1,4 @@ | |||||
| -- liquibase formatted sql | |||||
| -- changeset cyril:insert_schedule_setting | |||||
| INSERT INTO `settings` (`name`, `value`, `category`, `type`) VALUES ('SCHEDULE.m18.master', '0 0 1 * * *', 'SCHEDULE', 'string'); | |||||
| @@ -0,0 +1,5 @@ | |||||
| -- liquibase formatted sql | |||||
| -- changeset cyril:update_production_schedule | |||||
| ALTER TABLE `production_schedule` | |||||
| ADD COLUMN `produceAt` DATETIME NULL AFTER `scheduleAt`; | |||||
| @@ -0,0 +1,7 @@ | |||||
| -- liquibase formatted sql | |||||
| -- changeset cyril:insert_schedule_setting | |||||
| INSERT INTO `settings` (`name`, `value`, `category`, `type`) | |||||
| VALUES | |||||
| ('SCHEDULE.prod.rough', '0 0 4 * * Wed', 'SCHEDULE', 'string'), | |||||
| ('SCHEDULE.prod.detailed', '0 0 3 * * *', 'SCHEDULE', 'string'); | |||||