diff --git a/src/main/java/com/ffii/fpsms/m18/entity/M18GoodsReceiptNoteLogRepository.kt b/src/main/java/com/ffii/fpsms/m18/entity/M18GoodsReceiptNoteLogRepository.kt index 9f8a741..9bb6c6d 100644 --- a/src/main/java/com/ffii/fpsms/m18/entity/M18GoodsReceiptNoteLogRepository.kt +++ b/src/main/java/com/ffii/fpsms/m18/entity/M18GoodsReceiptNoteLogRepository.kt @@ -11,7 +11,8 @@ interface M18GoodsReceiptNoteLogRepository : AbstractRepository= :start AND l.created < :end + AND l.created >= :start AND l.created <= :end """ ) fun findNeedingGrnCode( diff --git a/src/main/java/com/ffii/fpsms/m18/service/M18GrnCodeSyncService.kt b/src/main/java/com/ffii/fpsms/m18/service/M18GrnCodeSyncService.kt index 0e4c972..594e426 100644 --- a/src/main/java/com/ffii/fpsms/m18/service/M18GrnCodeSyncService.kt +++ b/src/main/java/com/ffii/fpsms/m18/service/M18GrnCodeSyncService.kt @@ -5,7 +5,9 @@ import org.slf4j.LoggerFactory import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional import java.time.LocalDate +import java.time.LocalDateTime import java.time.ZoneId +import java.time.ZonedDateTime /** * Fills [com.ffii.fpsms.m18.entity.M18GoodsReceiptNoteLog.grnCode] by calling M18 GET /root/api/read/an?id=... @@ -18,16 +20,37 @@ open class M18GrnCodeSyncService( private val logger = LoggerFactory.getLogger(M18GrnCodeSyncService::class.java) /** - * For today's [created] window, backfill grn_code for rows with m18_record_id but empty grn_code. + * Backfill `grn_code` for rows created in **[start of calendar day (today − syncOffsetDays), now]** (inclusive), + * with `m18_record_id` set and `grn_code` empty. + * + * Example: `syncOffsetDays = 4` → `created` from 4 days ago 00:00:00 up to **now**. + * Example: `syncOffsetDays = 0` → `created` from **today** 00:00:00 up to **now**. + * * One M18 read per distinct [m18_record_id]; all matching log rows get the same code. */ @Transactional + open fun syncGrnCodesForLookback(syncOffsetDays: Int, zoneId: ZoneId = ZoneId.systemDefault()): Int { + val days = syncOffsetDays.coerceAtLeast(0) + val nowZ = ZonedDateTime.now(zoneId) + val startLdt = nowZ.toLocalDate().minusDays(days.toLong()).atStartOfDay(zoneId).toLocalDateTime() + val endLdt = nowZ.toLocalDateTime() + return syncGrnCodesInWindow(startLdt, endLdt, "lookback syncOffsetDays=$days") + } + + /** + * Single calendar day window: [day 00:00, next day 00:00) — useful for manual/testing. + */ + @Transactional open fun syncGrnCodesForDate(day: LocalDate, zoneId: ZoneId = ZoneId.systemDefault()): Int { - val start = day.atStartOfDay(zoneId).toLocalDateTime() - val end = day.plusDays(1).atStartOfDay(zoneId).toLocalDateTime() - val rows = m18GoodsReceiptNoteLogRepository.findNeedingGrnCode(start, end) + val startLdt = day.atStartOfDay(zoneId).toLocalDateTime() + val endLdt = day.plusDays(1).atStartOfDay(zoneId).toLocalDateTime().minusNanos(1) + return syncGrnCodesInWindow(startLdt, endLdt, "single day=$day") + } + + private fun syncGrnCodesInWindow(startLdt: LocalDateTime, endLdt: LocalDateTime, label: String): Int { + val rows = m18GoodsReceiptNoteLogRepository.findNeedingGrnCode(startLdt, endLdt) if (rows.isEmpty()) { - logger.info("[M18GrnCodeSync] No GRN log rows need grn_code for day=$day") + logger.info("[M18GrnCodeSync] No GRN log rows need grn_code ($label, window=[$startLdt, $endLdt])") return 0 } val byRecord = rows.groupBy { it.m18RecordId } @@ -36,7 +59,7 @@ open class M18GrnCodeSyncService( val rid = recordId ?: continue val code = m18GoodsReceiptNoteService.readAnCodeByRecordId(rid) if (code.isNullOrBlank()) { - logger.warn("[M18GrnCodeSync] No code from M18 for m18_record_id=$rid (day=$day)") + logger.warn("[M18GrnCodeSync] No code from M18 for m18_record_id=$rid ($label)") continue } for (log in list) { @@ -49,6 +72,6 @@ open class M18GrnCodeSyncService( return updated } - /** Convenience: sync for calendar "today" in the default timezone. */ - open fun syncGrnCodesForToday(): Int = syncGrnCodesForDate(LocalDate.now()) + /** Convenience: lookback 0 days = from start of today to now. */ + open fun syncGrnCodesForToday(): Int = syncGrnCodesForLookback(0) } 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 5070013..4d48e54 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 @@ -32,7 +32,10 @@ open class SchedulerService( @Value("\${scheduler.postCompletedDnGrn.enabled:true}") val postCompletedDnGrnEnabled: Boolean, @Value("\${scheduler.postCompletedDnGrn.receiptDate:}") val postCompletedDnGrnReceiptDate: String, @Value("\${scheduler.grnCodeSync.enabled:true}") val grnCodeSyncEnabled: Boolean, - /** 0 = calendar day of run; 1 = previous day (typical for 1:20 AM job to backfill yesterday's GRNs). */ + /** + * Lookback window for GRN code sync: rows with `created` from **start of (today − N days)** through **now**, + * missing `grn_code`. Example: 4 = from 4 days ago 00:00 to now. + */ @Value("\${scheduler.grnCodeSync.syncOffsetDays:0}") val grnCodeSyncSyncOffsetDays: Int, val settingsService: SettingsService, val taskScheduler: TaskScheduler, @@ -149,7 +152,7 @@ open class SchedulerService( else try { LocalDate.parse(s) } catch (e: Exception) { LocalDate.now().minusDays(1) } } - /** Backfill grn_code from M18 read/an for today's logs missing code. Default 1:20 AM daily. Set scheduler.grnCodeSync.enabled=false to disable. */ + /** Backfill grn_code from M18 read/an for logs in [today−syncOffsetDays 00:00, now] missing code. Set scheduler.grnCodeSync.enabled=false to disable. */ fun scheduleGrnCodeSync() { if (!grnCodeSyncEnabled) { scheduledGrnCodeSync?.cancel(false) @@ -160,16 +163,15 @@ open class SchedulerService( commonSchedule( scheduledGrnCodeSync, SettingNames.SCHEDULE_GRN_CODE_SYNC, - "0 29 1 * * *", + "0 10 1 * * *", { syncGrnCodesFromM18() }, ) } open fun syncGrnCodesFromM18() { try { - val day = java.time.LocalDate.now().minusDays(grnCodeSyncSyncOffsetDays.toLong().coerceAtLeast(0)) - val updated = m18GrnCodeSyncService.syncGrnCodesForDate(day) - logger.info("Scheduler - M18 GRN code sync done for date=$day, rows updated=$updated") + val updated = m18GrnCodeSyncService.syncGrnCodesForLookback(grnCodeSyncSyncOffsetDays) + logger.info("Scheduler - M18 GRN code sync done (syncOffsetDays=${grnCodeSyncSyncOffsetDays}), rows updated=$updated") } catch (e: Exception) { logger.error("Scheduler - M18 GRN code sync failed: ${e.message}", e) } @@ -186,7 +188,7 @@ open class SchedulerService( commonSchedule( scheduledPostCompletedDnGrn, SettingNames.SCHEDULE_POST_COMPLETED_DN_GRN, - "0 57 0 * * *", + "0 1 0 * * *", { getPostCompletedDnAndProcessGrn(receiptDate = getPostCompletedDnGrnReceiptDate()) } ) } diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 9f299f1..0f7d713 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -24,7 +24,7 @@ scheduler: enabled: true grnCodeSync: enabled: true - syncOffsetDays: 0 # 0 = m18_goods_receipt_note_log rows created today; use 1 for "yesterday" if you prefer night backfill + syncOffsetDays: 10 # from (today − 10) 00:00 to now, rows missing grn_code m18: config: diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 6b1177c..1e8b8f6 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -17,7 +17,8 @@ scheduler: # receiptDate: # leave unset for production (uses yesterday) grnCodeSync: enabled: false # set true in prod; backfills grn_code from M18 GET /root/api/read/an - syncOffsetDays: 0 # 0=today, 1=yesterday (use 1 for 1:20 AM job to backfill prior day) + # Lookback: created from start of (today − N days) through now, missing grn_code. E.g. 4 = last 4 days + today. + syncOffsetDays: 0 spring: servlet: