diff --git a/src/main/java/com/ffii/fpsms/modules/common/SettingNames.java b/src/main/java/com/ffii/fpsms/modules/common/SettingNames.java index 316b865..61cb2b7 100644 --- a/src/main/java/com/ffii/fpsms/modules/common/SettingNames.java +++ b/src/main/java/com/ffii/fpsms/modules/common/SettingNames.java @@ -26,6 +26,8 @@ public abstract class SettingNames { public static final String SCHEDULE_M18_PO = "SCHEDULE.m18.po"; public static final String SCHEDULE_M18_DO1 = "SCHEDULE.m18.do1"; + /** Saturday-only DO1 time (default 03:10). Mon–Fri & Sun use [SCHEDULE_M18_DO1] time via a second trigger. */ + public static final String SCHEDULE_M18_DO1_SAT = "SCHEDULE.m18.do1.sat"; public static final String SCHEDULE_M18_DO2 = "SCHEDULE.m18.do2"; public static final String SCHEDULE_M18_MASTER = "SCHEDULE.m18.master"; diff --git a/src/main/java/com/ffii/fpsms/modules/common/scheduler/service/SchedulerService.kt b/src/main/java/com/ffii/fpsms/modules/common/scheduler/service/SchedulerService.kt index 3fcc6ce..eacdf67 100644 --- a/src/main/java/com/ffii/fpsms/modules/common/scheduler/service/SchedulerService.kt +++ b/src/main/java/com/ffii/fpsms/modules/common/scheduler/service/SchedulerService.kt @@ -39,6 +39,8 @@ open class SchedulerService( @Value("\${scheduler.m18Units.enabled:true}") val m18UnitsSchedulerEnabled: Boolean, @Value("\${scheduler.m18Units.incrementalLookbackDays:7}") val m18UnitsIncrementalLookbackDays: Int, @Value("\${scheduler.inventoryLotExpiry.enabled:true}") val inventoryLotExpiryEnabled: Boolean, + /** When false (default), M18 PO / DO1 / DO2 / master-data cron jobs are not registered — use true in production only. */ + @Value("\${scheduler.m18Sync.enabled:false}") val m18SyncEnabled: Boolean, val settingsService: SettingsService, /** * Lookback window for GRN code sync: rows with `created` from **start of (today − N days)** through **now**, @@ -63,6 +65,8 @@ open class SchedulerService( var scheduledM18Po: ScheduledFuture<*>? = null var scheduledM18Do1: ScheduledFuture<*>? = null + /** Saturday DO1 run (same [getM18Dos1] as [scheduledM18Do1]). */ + var scheduledM18Do1Sat: ScheduledFuture<*>? = null var scheduledM18Do2: ScheduledFuture<*>? = null @Volatile @@ -110,6 +114,49 @@ open class SchedulerService( ) } + /** + * Same second/minute/hour as [cronExpression] but only Mon–Fri and Sun (Saturday uses [SCHEDULE_M18_DO1_SAT]). + * Expects a 6-field cron. + */ + private fun cronToMonFriSunSchedule(cronExpression: String): String { + val parts = cronExpression.trim().split(Regex("\\s+")) + if (parts.size != 6) { + return cronExpression + } + return "${parts[0]} ${parts[1]} ${parts[2]} ? * MON-FRI,SUN" + } + + /** + * M18 DO1: [SCHEDULE_M18_DO1] applies Mon–Fri & Sun; [SCHEDULE_M18_DO1_SAT] is Saturday only (default 03:10). + */ + fun scheduleM18Do1() { + scheduledM18Do1?.cancel(false) + scheduledM18Do1Sat?.cancel(false) + if (!m18SyncEnabled) { + scheduledM18Do1 = null + scheduledM18Do1Sat = null + logger.info("M18 DO1 schedulers disabled (scheduler.m18Sync.enabled=false)") + return + } + + var cronMain = settingsService.findByName(SettingNames.SCHEDULE_M18_DO1).getOrNull()?.value ?: "0 10 19 * * *" + if (!isValidCronExpression(cronMain)) { + cronMain = "0 10 19 * * *" + } + val weekdaySunCron = cronToMonFriSunSchedule(cronMain) + val mainCron = if (isValidCronExpression(weekdaySunCron)) weekdaySunCron else cronMain + + scheduledM18Do1 = taskScheduler.schedule({ getM18Dos1() }, CronTrigger(mainCron)) + + var cronSat = settingsService.findByName(SettingNames.SCHEDULE_M18_DO1_SAT).getOrNull()?.value ?: "0 10 3 ? * SAT" + if (!isValidCronExpression(cronSat)) { + cronSat = "0 10 3 ? * SAT" + } + scheduledM18Do1Sat = taskScheduler.schedule({ getM18Dos1() }, CronTrigger(cronSat)) + + logger.info("Scheduled M18 DO1: Mon-Fri,Sun -> {} | Saturday -> {}", mainCron, cronSat) + } + // Init Scheduler @PostConstruct fun init() { @@ -142,19 +189,33 @@ open class SchedulerService( } fun scheduleM18Po() { - commonSchedule(scheduledM18Po, SettingNames.SCHEDULE_M18_PO, ::getM18Po) - } - - fun scheduleM18Do1() { - commonSchedule(scheduledM18Do1, SettingNames.SCHEDULE_M18_DO1, ::getM18Dos1) + if (!m18SyncEnabled) { + scheduledM18Po?.cancel(false) + scheduledM18Po = null + logger.info("M18 PO scheduler disabled (scheduler.m18Sync.enabled=false)") + return + } + scheduledM18Po = commonSchedule(scheduledM18Po, SettingNames.SCHEDULE_M18_PO, ::getM18Po) } fun scheduleM18Do2() { - commonSchedule(scheduledM18Do2, SettingNames.SCHEDULE_M18_DO2, ::getM18Dos2) + if (!m18SyncEnabled) { + scheduledM18Do2?.cancel(false) + scheduledM18Do2 = null + logger.info("M18 DO2 scheduler disabled (scheduler.m18Sync.enabled=false)") + return + } + scheduledM18Do2 = commonSchedule(scheduledM18Do2, SettingNames.SCHEDULE_M18_DO2, ::getM18Dos2) } fun scheduleM18MasterData() { - commonSchedule(scheduledM18Master, SettingNames.SCHEDULE_M18_MASTER, ::getM18MasterData) + if (!m18SyncEnabled) { + scheduledM18Master?.cancel(false) + scheduledM18Master = null + logger.info("M18 master-data scheduler disabled (scheduler.m18Sync.enabled=false)") + return + } + scheduledM18Master = commonSchedule(scheduledM18Master, SettingNames.SCHEDULE_M18_MASTER, ::getM18MasterData) } /** diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 92fbf56..d9d9024 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -22,6 +22,8 @@ spring: scheduler: + m18Sync: + enabled: true m18Grn: createEnabled: true postCompletedDnGrn: diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 35b7124..f01703f 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -12,7 +12,10 @@ server: # PostCompletedDn GRN: runs daily at 00:01, processes all POs with receipt date = yesterday. # Set enabled: false to disable. Optional receiptDate: "yyyy-MM-dd" overrides for testing only. # m18Grn.createEnabled: M18 GRN PUT/create — false outside production so UAT/dev never posts GRNs. +# m18Sync: M18 cron jobs for PO, DO1, DO2, master data — false outside production (manual /trigger/* still works). scheduler: + m18Sync: + enabled: false m18Grn: createEnabled: false postCompletedDnGrn: diff --git a/src/main/resources/db/changelog/changes/20260401_do1_sat_setting/01_insert_do1_sat_scheduler.sql b/src/main/resources/db/changelog/changes/20260401_do1_sat_setting/01_insert_do1_sat_scheduler.sql new file mode 100644 index 0000000..a1ef226 --- /dev/null +++ b/src/main/resources/db/changelog/changes/20260401_do1_sat_setting/01_insert_do1_sat_scheduler.sql @@ -0,0 +1,9 @@ +--liquibase formatted sql +--changeset fpsms:20260401_do1_sat_setting + +INSERT INTO `fpsmsdb`.`settings` (`name`, `value`, `category`, `type`) +SELECT 'SCHEDULE.m18.do1.sat', '0 10 3 ? * SAT', 'SCHEDULE', 'string' +FROM DUAL +WHERE NOT EXISTS ( + SELECT 1 FROM `fpsmsdb`.`settings` WHERE `name` = 'SCHEDULE.m18.do1.sat' +);