| @@ -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<Long>() | |||
| @@ -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<SchedulerSyncLog, Long> { | |||
| fun findTop20ByOrderByEndTimeDesc(): List<SchedulerSyncLog> | |||
| } | |||
| @@ -52,7 +52,8 @@ data class M18PurchaseOrderPot ( | |||
| /** Purchase Order List Response */ | |||
| data class M18PurchaseOrderListResponseWithType ( | |||
| var valuesWithType: MutableList<Pair<PurchaseOrderType, M18PurchaseOrderListResponse?>> | |||
| var valuesWithType: MutableList<Pair<PurchaseOrderType, M18PurchaseOrderListResponse?>>, | |||
| var query: String? = null, | |||
| ) | |||
| data class M18PurchaseOrderListResponse ( | |||
| @@ -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 | |||
| ) | |||
| @@ -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 ?: "" | |||
| ) | |||
| } | |||
| } | |||
| @@ -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<Long>(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<Long>(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 | |||
| ) | |||
| } | |||
| } | |||
| @@ -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<Long>(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 ?: "" | |||
| ) | |||
| } | |||
| } | |||
| @@ -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)}") | |||
| } | |||
| } | |||
| @@ -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" | |||
| } | |||
| } | |||
| @@ -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; | |||
| @@ -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`; | |||
| @@ -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'); | |||