diff --git a/src/main/java/com/ffii/fpsms/m18/entity/SchedulerSyncLog.kt b/src/main/java/com/ffii/fpsms/m18/entity/SchedulerSyncLog.kt new file mode 100644 index 0000000..5c4abe5 --- /dev/null +++ b/src/main/java/com/ffii/fpsms/m18/entity/SchedulerSyncLog.kt @@ -0,0 +1,36 @@ +package com.ffii.fpsms.m18.entity + +import com.ffii.core.entity.BaseEntity +import jakarta.persistence.* +import java.time.LocalDateTime + +@Entity +@Table(name = "scheduler_sync_log") +open class SchedulerSyncLog( + @Column + open var syncType: String? = null, // PO, DO1, DO2, MASTER_DATA + + @Column + open var status: String = "", // SUCCESS, FAILED, PARTIAL + + @Column + open var recordsProcessed: Int = 0, + + @Column + open var recordsSaved: Int = 0, + + @Column + open var recordsFailed: Int = 0, + + @Column(columnDefinition = "TEXT") + open var errorMessage: String? = null, + + @Column(columnDefinition = "TEXT") + open var query: String? = null, + + @Column + open var startTime: LocalDateTime = LocalDateTime.now(), + + @Column + open var endTime: LocalDateTime = LocalDateTime.now() +) : BaseEntity() \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/m18/entity/SchedulerSyncLogRepository.kt b/src/main/java/com/ffii/fpsms/m18/entity/SchedulerSyncLogRepository.kt new file mode 100644 index 0000000..adb1861 --- /dev/null +++ b/src/main/java/com/ffii/fpsms/m18/entity/SchedulerSyncLogRepository.kt @@ -0,0 +1,10 @@ +package com.ffii.fpsms.m18.entity + +import com.ffii.core.support.AbstractRepository +import com.ffii.fpsms.m18.enums.M18DataLogStatus +import org.springframework.stereotype.Repository + +@Repository +interface SchedulerSyncLogRepository : AbstractRepository { + fun findTop20ByOrderByEndTimeDesc(): List +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/m18/model/M18PurchaseOrderResponse.kt b/src/main/java/com/ffii/fpsms/m18/model/M18PurchaseOrderResponse.kt index 0edec13..37e3ef2 100644 --- a/src/main/java/com/ffii/fpsms/m18/model/M18PurchaseOrderResponse.kt +++ b/src/main/java/com/ffii/fpsms/m18/model/M18PurchaseOrderResponse.kt @@ -52,7 +52,8 @@ data class M18PurchaseOrderPot ( /** Purchase Order List Response */ data class M18PurchaseOrderListResponseWithType ( - var valuesWithType: MutableList> + var valuesWithType: MutableList>, + var query: String? = null, ) data class M18PurchaseOrderListResponse ( diff --git a/src/main/java/com/ffii/fpsms/m18/model/SyncResult.kt b/src/main/java/com/ffii/fpsms/m18/model/SyncResult.kt new file mode 100644 index 0000000..4c4d334 --- /dev/null +++ b/src/main/java/com/ffii/fpsms/m18/model/SyncResult.kt @@ -0,0 +1,10 @@ +package com.ffii.fpsms.m18.model + +import java.time.LocalDateTime + +data class SyncResult( + val totalProcessed: Int, + val totalSuccess: Int, + val totalFail: Int, + val query: String? = null +) \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/m18/service/M18DeliveryOrderService.kt b/src/main/java/com/ffii/fpsms/m18/service/M18DeliveryOrderService.kt index d6cf5b9..5b25387 100644 --- a/src/main/java/com/ffii/fpsms/m18/service/M18DeliveryOrderService.kt +++ b/src/main/java/com/ffii/fpsms/m18/service/M18DeliveryOrderService.kt @@ -5,6 +5,7 @@ import com.ffii.fpsms.api.service.ApiCallerService import com.ffii.fpsms.m18.M18Config import com.ffii.fpsms.m18.enums.M18DataLogStatus import com.ffii.fpsms.m18.model.* +import com.ffii.fpsms.m18.model.SyncResult import com.ffii.fpsms.m18.utils.CommonUtils import com.ffii.fpsms.m18.web.models.M18CommonRequest import com.ffii.fpsms.modules.deliveryOrder.enums.DeliveryOrderLineStatus @@ -121,6 +122,8 @@ open class M18DeliveryOrderService( logger.error("(Getting Shop Po list) Error on Function - ${e.stackTrace}") logger.error(e.message) } + + deliveryOrders.query = shopPoConds return deliveryOrders } @@ -145,7 +148,7 @@ open class M18DeliveryOrderService( return deliveryOrder } - open fun saveDeliveryOrders(request: M18CommonRequest) { + open fun saveDeliveryOrders(request: M18CommonRequest) : SyncResult{ logger.info("--------------------------------------------Start - Saving M18 Delivery Order--------------------------------------------") val deliveryOrdersWithType = getDeliveryOrdersWithType(request) @@ -451,5 +454,12 @@ open class M18DeliveryOrderService( // logger.error("Total Fail M18 Items (${doLineRefType}) (${failItemDetailList.distinct().size}): ${failItemDetailList.distinct()}") // } logger.info("--------------------------------------------End - Saving M18 Delivery Order--------------------------------------------") + + return SyncResult( + totalProcessed = successList.size + failList.size, + totalSuccess = successList.size, + totalFail = failList.size, + query = deliveryOrdersWithType?.query ?: "" + ) } } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/m18/service/M18MasterDataService.kt b/src/main/java/com/ffii/fpsms/m18/service/M18MasterDataService.kt index e7c3144..ca4ad53 100644 --- a/src/main/java/com/ffii/fpsms/m18/service/M18MasterDataService.kt +++ b/src/main/java/com/ffii/fpsms/m18/service/M18MasterDataService.kt @@ -16,6 +16,7 @@ import org.springframework.stereotype.Service import java.math.BigDecimal import java.time.LocalDateTime import java.time.format.DateTimeFormatter +import com.ffii.fpsms.m18.model.SyncResult @Service open class M18MasterDataService( @@ -174,7 +175,7 @@ open class M18MasterDataService( isEgg = null, isFee = null, isBag = null, - qcType = null + qcType = null, ) val savedItem = itemsService.saveItem(saveItemRequest) @@ -229,7 +230,7 @@ open class M18MasterDataService( } } - open fun saveProducts(request: M18CommonRequest) { + open fun saveProducts(request: M18CommonRequest) : SyncResult{ logger.info("--------------------------------------------Start - Saving M18 Products / Materials--------------------------------------------") val items = getProducts(request) val exampleProducts = listOf(10946L, 3825L) @@ -278,7 +279,7 @@ open class M18MasterDataService( isEgg = null, isFee = null, isBag = null, - qcType = null + qcType = null, ) val savedItem = itemsService.saveItem(saveItemRequest) @@ -343,6 +344,12 @@ open class M18MasterDataService( } logger.info("--------------------------------------------End - Saving M18 Products / Materials--------------------------------------------") + + return SyncResult( + totalProcessed = successList.size + failList.size, + totalSuccess = successList.size, + totalFail = failList.size + ) } // --------------------------------------------- Vendor --------------------------------------------- /// @@ -364,7 +371,7 @@ open class M18MasterDataService( ) } - open fun saveVendors(request: M18CommonRequest) { + open fun saveVendors(request: M18CommonRequest) : SyncResult{ logger.info("--------------------------------------------Start - Saving M18 Vendors--------------------------------------------") val vendors = getVendors(request) val exampleVendors = listOf(191L) @@ -426,6 +433,12 @@ open class M18MasterDataService( logger.error("Total Fail (${failList.size}): $failList") } logger.info("--------------------------------------------End - Saving M18 Vendors--------------------------------------------") + + return SyncResult( + totalProcessed = successList.size + failList.size, + totalSuccess = successList.size, + totalFail = failList.size + ) } // --------------------------------------------- Unit (UoM) --------------------------------------------- /// @@ -541,7 +554,7 @@ open class M18MasterDataService( ) } - open fun saveCurrencies(request: M18CommonRequest) { + open fun saveCurrencies(request: M18CommonRequest) : SyncResult{ logger.info("--------------------------------------------Start - Saving M18 Currencies--------------------------------------------") val currencies = getCurrencies(request) @@ -583,6 +596,12 @@ open class M18MasterDataService( } logger.info("--------------------------------------------End - Saving Currencies--------------------------------------------") + + return SyncResult( + totalProcessed = successList.size + failList.size, + totalSuccess = successList.size, + totalFail = failList.size + ) } // --------------------------------------------- Bom --------------------------------------------- /// @@ -718,7 +737,7 @@ open class M18MasterDataService( ) } - open fun saveBusinessUnits(request: M18CommonRequest) { + open fun saveBusinessUnits(request: M18CommonRequest) : SyncResult { logger.info("--------------------------------------------Start - Saving M18 Business Units (Shops)--------------------------------------------") val businessUnits = getBusinessUnits(request) @@ -783,5 +802,11 @@ open class M18MasterDataService( logger.error("Total Fail (${failList.size}): $failList") } logger.info("--------------------------------------------End - Saving M18 Business Units--------------------------------------------") + + return SyncResult( + totalProcessed = successList.size + failList.size, + totalSuccess = successList.size, + totalFail = failList.size + ) } } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/m18/service/M18PurchaseOrderService.kt b/src/main/java/com/ffii/fpsms/m18/service/M18PurchaseOrderService.kt index 7374c5c..a847b6f 100644 --- a/src/main/java/com/ffii/fpsms/m18/service/M18PurchaseOrderService.kt +++ b/src/main/java/com/ffii/fpsms/m18/service/M18PurchaseOrderService.kt @@ -25,6 +25,7 @@ import org.springframework.stereotype.Service import java.time.LocalDateTime import kotlin.reflect.full.memberProperties import java.time.format.DateTimeFormatter +import com.ffii.fpsms.m18.model.SyncResult @Service open class M18PurchaseOrderService( @@ -60,8 +61,8 @@ open class M18PurchaseOrderService( val purchaseOrders = M18PurchaseOrderListResponseWithType(mutableListOf()) val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss") val lastDateConds = if (request.modifiedDateFrom != null && request.modifiedDateTo != null) { - val dateFrom = LocalDateTime.parse(request.modifiedDateFrom, formatter).toLocalDate().toString() - val dateTo = LocalDateTime.parse(request.modifiedDateTo, formatter).toLocalDate().toString() + val dateFrom = LocalDateTime.parse(request.modifiedDateFrom, formatter).toString() + val dateTo = LocalDateTime.parse(request.modifiedDateTo, formatter).toString() "lastModifyDate=largerOrEqual=$dateFrom=and=lastModifyDate=lessOrEqual=$dateTo" } else { @@ -126,6 +127,8 @@ open class M18PurchaseOrderService( logger.error(e.message) } + purchaseOrders.query = materialPoConds + // Shop PO /* val shopPoBuyers = commonUtils.listToString(listOf(m18Config.BEID_TOA), "beId=equal=", "=or=") val shopPoSupplier = commonUtils.listToString( @@ -201,7 +204,7 @@ open class M18PurchaseOrderService( return purchaseOrder } - open fun savePurchaseOrders(request: M18CommonRequest) { + open fun savePurchaseOrders(request: M18CommonRequest) : SyncResult{ logger.info("--------------------------------------------Start - Saving M18 Purchase Order--------------------------------------------") val purchaseOrdersWithType = getPurchaseOrdersWithType(request) val examplePurchaseOrders = listOf(4764034L) @@ -504,5 +507,12 @@ open class M18PurchaseOrderService( logger.error("Total Fail (${poLineRefType}) (${failDetailList.size}): $failDetailList") // } logger.info("--------------------------------------------End - Saving M18 Purchase Order--------------------------------------------") + + return SyncResult( + totalProcessed = successList.size + failList.size, + totalSuccess = successList.size, + totalFail = failList.size, + query = purchaseOrdersWithType?.query ?: "" + ) } } \ No newline at end of file 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 6684bc1..3e31a35 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 @@ -5,6 +5,9 @@ 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.m18.entity.SchedulerSyncLog +import com.ffii.fpsms.m18.entity.SchedulerSyncLogRepository +import com.ffii.fpsms.m18.model.SyncResult import com.ffii.fpsms.modules.common.SettingNames import com.ffii.fpsms.modules.master.service.ProductionScheduleService import com.ffii.fpsms.modules.settings.service.SettingsService @@ -28,11 +31,12 @@ open class SchedulerService( val m18PurchaseOrderService: M18PurchaseOrderService, val m18DeliveryOrderService: M18DeliveryOrderService, val m18MasterDataService: M18MasterDataService, + val schedulerSyncLogRepository: SchedulerSyncLogRepository, ) { var logger: Logger = LoggerFactory.getLogger(JwtTokenUtil::class.java) val dataStringFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd") val dateTimeStringFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss") - val defaultCronExpression = "0 0 2 * * *"; + val defaultCronExpression = "0 0 2 31 2 *"; //@Volatile var scheduledM18Po: ScheduledFuture<*>? = null @@ -79,6 +83,7 @@ open class SchedulerService( @PostConstruct fun init() { //scheduleM18PoTask(); + scheduleM18Po(); scheduleM18Do1(); scheduleM18Do2(); scheduleM18MasterData(); @@ -98,9 +103,14 @@ open class SchedulerService( // --------------------------- M18 --------------------------- // fun scheduleM18PoTask() { + //this is going to delete commonSchedule(scheduledM18Po, SettingNames.SCHEDULE_M18_PO, ::getM18Pos) } + fun scheduleM18Po() { + commonSchedule(scheduledM18Po, SettingNames.SCHEDULE_M18_PO, ::getM18Po) + } + fun scheduleM18Do1() { commonSchedule(scheduledM18Do1, SettingNames.SCHEDULE_M18_DO1, ::getM18Dos1) } @@ -189,6 +199,27 @@ open class SchedulerService( // logger.info("yesterday: ${yesterday.format(dataStringFormat)}") } + open fun getM18Po() { + logger.info("PO Scheduler") + val currentTime = LocalDateTime.now() + val today = currentTime.toLocalDate().atStartOfDay() + val TenDaysLater = today.plusDays(10L) + + var requestDO = M18CommonRequest( + dDateTo = TenDaysLater.format(dateTimeStringFormat), + dDateFrom = today.format(dateTimeStringFormat) + ) + + val result = m18PurchaseOrderService.savePurchaseOrders(requestDO); + + saveSyncLog( + type = "PO", + status = "SUCCESS", + result = result, + start = currentTime + ) + } + open fun getM18Dos1() { logger.info("DO Scheduler 1 - DO") val currentTime = LocalDateTime.now() @@ -200,7 +231,30 @@ open class SchedulerService( dDateFrom = twoDaysLater.format(dateTimeStringFormat) ) - m18DeliveryOrderService.saveDeliveryOrders(requestDO); + val result = m18DeliveryOrderService.saveDeliveryOrders(requestDO); + + saveSyncLog( + type = "DO1", + status = "SUCCESS", + result = result, + start = currentTime + ) + + } + + private fun saveSyncLog(type: String, status: String, result: SyncResult? = null, error: String? = null, start: LocalDateTime) { + val log = SchedulerSyncLog( + syncType = type, + status = status, + recordsProcessed = result?.totalProcessed ?: 0, + recordsSaved = result?.totalSuccess ?: 0, + recordsFailed = result?.totalFail ?: 0, + errorMessage = error, + query = result?.query ?: "", + startTime = start, + endTime = LocalDateTime.now() + ) + schedulerSyncLogRepository.save(log) } open fun getM18Dos2() { @@ -226,12 +280,19 @@ open class SchedulerService( modifiedDateFrom = ysdNight.format(dateTimeStringFormat) // 2026-01-17 19:00:00 ) - m18DeliveryOrderService.saveDeliveryOrders(requestDO) + val result = m18DeliveryOrderService.saveDeliveryOrders(requestDO) + + saveSyncLog( + type = "DO2", + status = "SUCCESS", + result = result, + start = currentTime + ) } open fun getM18MasterData() { logger.info("Daily Scheduler - Master Data") - val currentTime = LocalDateTime.now() + var currentTime = LocalDateTime.now() val today = currentTime.toLocalDate().atStartOfDay() val yesterday = today.minusDays(1L) val request = M18CommonRequest( @@ -239,12 +300,42 @@ open class SchedulerService( modifiedDateFrom = yesterday.format(dataStringFormat) ) m18MasterDataService.saveUnits(request) - m18MasterDataService.saveProducts(request) + + val resultProducts = m18MasterDataService.saveProducts(request) + saveSyncLog( + type = "Products", + status = "SUCCESS", + result = resultProducts, + start = currentTime + ) // m18MasterDataService.saveBoms(request) - m18MasterDataService.saveVendors(request) - m18MasterDataService.saveBusinessUnits(request) - m18MasterDataService.saveCurrencies(request) + currentTime = LocalDateTime.now() + val resultVendors = m18MasterDataService.saveVendors(request) + saveSyncLog( + type = "Vendors", + status = "SUCCESS", + result = resultVendors, + start = currentTime + ) + + val resultBusinessUnits = m18MasterDataService.saveBusinessUnits(request) + saveSyncLog( + type = "BusinessUnits", + status = "SUCCESS", + result = resultBusinessUnits, + start = currentTime + ) + + val resultCurrencies = m18MasterDataService.saveCurrencies(request) + saveSyncLog( + type = "Currencies", + status = "SUCCESS", + result = resultCurrencies, + start = currentTime + ) // logger.info("today: ${today.format(dataStringFormat)}") // logger.info("yesterday: ${yesterday.format(dataStringFormat)}") + + } } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/common/scheduler/web/SchedulerController.kt b/src/main/java/com/ffii/fpsms/modules/common/scheduler/web/SchedulerController.kt index 54d0ff8..0c0ea4a 100644 --- a/src/main/java/com/ffii/fpsms/modules/common/scheduler/web/SchedulerController.kt +++ b/src/main/java/com/ffii/fpsms/modules/common/scheduler/web/SchedulerController.kt @@ -9,6 +9,7 @@ import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RequestParam import org.springframework.web.bind.annotation.RestController + @RestController @RequestMapping("/scheduler") class SchedulerController( @@ -27,4 +28,34 @@ class SchedulerController( settingsService.update(SettingNames.SCHEDULE_M18_PO, newCron); schedulerService.scheduleM18PoTask() } + + @GetMapping("/trigger/po") + fun triggerPo(): String { + schedulerService.getM18Po() + return "M18 PO Sync Triggered Successfully" + } + + @GetMapping("/trigger/do1") + fun triggerDo1(): String { + schedulerService.getM18Dos1() + return "M18 DO1 Sync Triggered Successfully" + } + + @GetMapping("/trigger/do2") + fun triggerDo2(): String { + schedulerService.getM18Dos2() + return "M18 DO2 Sync Triggered Successfully" + } + + @GetMapping("/trigger/master-data") + fun triggerMasterData(): String { + schedulerService.getM18MasterData() + return "M18 Master Data Sync Triggered Successfully" + } + + @GetMapping("/refresh-cron") + fun refreshCron(): String { + schedulerService.init() + return "Cron Schedules Refreshed from Database" + } } \ No newline at end of file diff --git a/src/main/resources/db/changelog/changes/20260119_fai/01_cheduler_log.sql b/src/main/resources/db/changelog/changes/20260119_fai/01_cheduler_log.sql new file mode 100644 index 0000000..429a464 --- /dev/null +++ b/src/main/resources/db/changelog/changes/20260119_fai/01_cheduler_log.sql @@ -0,0 +1,24 @@ +--liquibase formatted sql +--changeset author:create_scheduler_log + +CREATE TABLE IF NOT EXISTS `scheduler_sync_log` ( + `id` INT NOT NULL AUTO_INCREMENT, + `created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + `createdBy` VARCHAR(30) DEFAULT NULL, + `version` INT NOT NULL DEFAULT '0', + `modified` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + `modifiedBy` VARCHAR(30) DEFAULT NULL, + `deleted` TINYINT(1) NOT NULL DEFAULT '0', + + `syncType` VARCHAR(50), + `status` VARCHAR(20), + `recordsProcessed` INT, + `recordsSaved` INT, + `recordsFailed` INT, + `errorMessage` VARCHAR(1024), + + `startTime` DATETIME, + `endTime` DATETIME, + + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; \ No newline at end of file diff --git a/src/main/resources/db/changelog/changes/20260119_fai/02_cheduler_log query copy.sql b/src/main/resources/db/changelog/changes/20260119_fai/02_cheduler_log query copy.sql new file mode 100644 index 0000000..8d64207 --- /dev/null +++ b/src/main/resources/db/changelog/changes/20260119_fai/02_cheduler_log query copy.sql @@ -0,0 +1,5 @@ +--liquibase formatted sql +--changeset author:add_scheduler_log query + +ALTER TABLE `fpsmsdb`.`scheduler_sync_log` +ADD COLUMN `query` VARCHAR(2000) NULL AFTER `endTime`; diff --git a/src/main/resources/db/changelog/changes/20260119_fai/03_clear_local_scheduler_settings.sql b/src/main/resources/db/changelog/changes/20260119_fai/03_clear_local_scheduler_settings.sql new file mode 100644 index 0000000..86d43d4 --- /dev/null +++ b/src/main/resources/db/changelog/changes/20260119_fai/03_clear_local_scheduler_settings.sql @@ -0,0 +1,4 @@ +--liquibase formatted sql +--changeset author:clear settings scheduler + +delete from settings where name in ('SCHEDULE.m18.po', 'SCHEDULE.m18.master','SCHEDULE.prod.rough','SCHEDULE.prod.detailed','SCHEDULE.m18.do1','SCHEDULE.m18.do2'); \ No newline at end of file