package com.ffii.fpsms.m18.service import com.ffii.core.utils.JwtTokenUtil 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.utils.CommonUtils import com.ffii.fpsms.m18.web.models.M18TestDoRequest import com.ffii.fpsms.m18.web.models.M18TestPoRequest import com.ffii.fpsms.modules.deliveryOrder.enums.DeliveryOrderLineStatus import com.ffii.fpsms.modules.deliveryOrder.enums.DeliveryOrderStatus import com.ffii.fpsms.modules.deliveryOrder.service.DeliveryOrderLineService import com.ffii.fpsms.modules.deliveryOrder.service.DeliveryOrderService import com.ffii.fpsms.modules.deliveryOrder.web.models.SaveDeliveryOrderLineRequest import com.ffii.fpsms.modules.deliveryOrder.web.models.SaveDeliveryOrderRequest import com.ffii.fpsms.modules.master.entity.Items import com.ffii.fpsms.modules.master.service.ItemUomService import com.ffii.fpsms.modules.master.service.ItemsService import com.ffii.fpsms.modules.master.service.ShopService import com.ffii.fpsms.modules.purchaseOrder.enums.PurchaseOrderType import org.slf4j.Logger import org.slf4j.LoggerFactory import org.springframework.stereotype.Service import java.time.LocalDateTime import kotlin.reflect.full.memberProperties @Service open class M18DeliveryOrderService( val m18Config: M18Config, val apiCallerService: ApiCallerService, val m18DataLogService: M18DataLogService, val deliveryOrderService: DeliveryOrderService, val deliveryOrderLineService: DeliveryOrderLineService, val itemsService: ItemsService, val shopService: ShopService, val itemUomService: ItemUomService, val m18MasterDataService: M18MasterDataService, ) { val commonUtils = CommonUtils() val logger: Logger = LoggerFactory.getLogger(JwtTokenUtil::class.java) val lastModifyDateStart = "2025-05-14 14:00:00" val lastModifyDateEnd = "2025-05-14 14:30:00" // val lastModifyDateConds = // "lastModifyDate=largerOrEqual=${lastModifyDateStart}=and=lastModifyDate=lessOrEqual=${lastModifyDateEnd}" // val lastModifyDate = LocalDateTime.now().minusMinutes(30) // val commonConds = // "(beId=equal=${m18Config.BEID_PF}=or=beId=equal=${m18Config.BEID_PP}=or=beId=equal=${m18Config.BEID_TOA})=and=lastModifyDate=largerOrEqual=${lastModifyDate}" // M18 API // Sharing same API with po val M18_LOAD_PURCHASE_ORDER_API = "/root/api/read/po" val M18_FETCH_PURCHASE_ORDER_LIST_API = "/search/search" // Include shop po open fun getDeliveryOrdersWithType(request: M18TestDoRequest): M18PurchaseOrderListResponseWithType? { val deliveryOrders = M18PurchaseOrderListResponseWithType(mutableListOf()) val lastModifyDateConds = "lastModifyDate=largerOrEqual=${request.modifiedDateFrom ?: lastModifyDateStart}=and=lastModifyDate=lessOrEqual=${request.modifiedDateTo ?: lastModifyDateEnd}" // Shop PO val shopPoBuyers = commonUtils.listToString(listOf(m18Config.BEID_TOA), "beId=equal=", "=or=") val shopPoSupplier = commonUtils.listToString( shopService.findM18VendorIdsByCodeRegexp(m18Config.SHOP_PO_SUPPLIER), "venId=equal=", "=or=" ) val shopPoConds = "(${shopPoBuyers})=and=(${shopPoSupplier})=and=(${lastModifyDateConds})" println("shopPoConds: ${shopPoConds}") val shopPoParams = M18PurchaseOrderListRequest( params = null, conds = shopPoConds ) try { deliveryOrders.valuesWithType += Pair( PurchaseOrderType.SHOP, apiCallerService.get( M18_FETCH_PURCHASE_ORDER_LIST_API, shopPoParams ).block() ) } catch (e: Exception) { logger.error("(Getting Shop Po list) Error on Function - ${e.stackTrace}") logger.error(e.message) } return deliveryOrders } open fun getDeliveryOrder(id: Long): M18PurchaseOrderResponse? { val deliveryOrderParams = M18PurchaseOrderRequest( id = id ) var deliveryOrder: M18PurchaseOrderResponse? = null try { deliveryOrder = apiCallerService.get( M18_LOAD_PURCHASE_ORDER_API, deliveryOrderParams ).block() } catch (e: Exception) { logger.error("(Getting Po Detail) Error on Function - ${e.stackTrace}") logger.error(e.message) } return deliveryOrder } open fun saveDeliveryOrders(request: M18TestDoRequest) { logger.info("--------------------------------------------Start - Saving M18 Delivery Order--------------------------------------------") val deliveryOrdersWithType = getDeliveryOrdersWithType(request) val successList = mutableListOf() val successDetailList = mutableListOf() val failList = mutableListOf() val failDetailList = mutableListOf() val failItemDetailList = mutableListOf() val doRefType = "Delivery Order" val doLineRefType = "Delivery Order Line" if (deliveryOrdersWithType != null) { // Loop for Delivery Orders (values) deliveryOrdersWithType.valuesWithType.forEach { deliveryOrderWithType -> val type = deliveryOrderWithType.first // if success val deliveryOrdersValues = deliveryOrderWithType.second?.values // if fail val deliveryOrdersMessages = deliveryOrderWithType.second?.messages if (deliveryOrdersValues != null) { deliveryOrdersValues.forEach { deliveryOrder -> val deliveryOrderDetail = getDeliveryOrder(deliveryOrder.id) var deliveryOrderId: Long? = null //FP-MTMS // Process for Delivery Order (mainpo) // Assume only one DO in the DO (search by DO ID) val mainpo = deliveryOrderDetail?.data?.mainpo?.get(0) val pot = deliveryOrderDetail?.data?.pot val deliveryOrderLineMessage = deliveryOrderDetail?.messages // delivery_order + m18_data_log table if (mainpo != null) { // Find the latest m18 data log by m18 id & type logger.info("${doRefType}: Finding For Latest M18 Data Log...") val latestDeliveryOrderLog = m18DataLogService.findLatestM18DataLogWithSuccess(deliveryOrder.id, doRefType) logger.info(latestDeliveryOrderLog.toString()) // Save to m18_data_log table logger.info("${doRefType}: Saving for M18 Data Log...") val mainpoJson = mainpo::class.memberProperties.associate { prop -> prop.name to prop.getter.call(mainpo) } .toMutableMap() val saveM18DeliveryOrderLogRequest = SaveM18DataLogRequest( id = null, refType = doRefType, m18Id = deliveryOrder.id, m18LastModifyDate = commonUtils.timestampToLocalDateTime(mainpo.lastModifyDate), // dataLog = mainpoJson, statusEnum = M18DataLogStatus.NOT_PROCESS ) val saveM18DeliveryOrderLog = m18DataLogService.saveM18DataLog(saveM18DeliveryOrderLogRequest) // logger.info("${doRefType}: Saved M18 Data Log. ID: ${saveM18DeliveryOrderLog.id}") try { // Find the delivery_order if exist logger.info("${doRefType}: Finding exising delivery order...") val existingDeliveryOrder = latestDeliveryOrderLog?.id?.let { deliveryOrderService.findByM18DataLogId(it) } logger.info("${doRefType}: Exising delivery order ID: ${existingDeliveryOrder?.id}") // Save to delivery_order table logger.info("${doRefType}: Saving delivery order...") val saveDeliveryOrderRequest = SaveDeliveryOrderRequest( id = existingDeliveryOrder?.id, code = mainpo.code, m18SupplierId = mainpo.venId, m18ShopId = mainpo.virDeptId, m18CurrencyId = mainpo.curId, orderDate = commonUtils.timestampToLocalDateTime(mainpo.tDate), estimatedArrivalDate = commonUtils.timestampToLocalDateTime(mainpo.dDate), completeDate = null, status = DeliveryOrderStatus.PENDING.value, type = type.value, m18DataLogId = saveM18DeliveryOrderLog.id, handlerId = null, m18BeId = mainpo.beId ) val saveDeliveryOrderResponse = deliveryOrderService.saveDeliveryOrder(saveDeliveryOrderRequest) deliveryOrderId = saveDeliveryOrderResponse.id // Update m18_data_log with success val successSaveM18DeliveryOrderLogRequest = SaveM18DataLogRequest( id = saveM18DeliveryOrderLog.id, dataLog = mainpoJson, statusEnum = M18DataLogStatus.SUCCESS ) m18DataLogService.saveM18DataLog(successSaveM18DeliveryOrderLogRequest) // log success info successList.add(deliveryOrder.id) logger.info("${doRefType}: Saved delivery order. ID: ${saveDeliveryOrderResponse.id} | M18 ${doRefType} ID: ${deliveryOrder.id}") } catch (e: Exception) { failList.add(deliveryOrder.id) logger.error("${doRefType}: Saving Failure!") logger.error("Error on Function - ${e.stackTrace} | Type: ${doRefType} | M18 ID: ${deliveryOrder.id} | Different? ${mainpo.id}") logger.error(e.message) val errorSaveM18DeliveryOrderLogRequest = SaveM18DataLogRequest( id = saveM18DeliveryOrderLog.id, dataLog = mutableMapOf(Pair("Exception Message", e.message)), statusEnum = M18DataLogStatus.FAIL ) m18DataLogService.saveM18DataLog(errorSaveM18DeliveryOrderLogRequest) logger.error("${doRefType}: M18 Data Log Updated! Please see the error. ID: ${saveM18DeliveryOrderLogRequest.id}") } // delivery_order_line + m18_data_log // TODO: check deleted po line? if (pot != null) { // Loop for Delivery Order Lines (pot) pot.forEach { line -> // Find the latest m18 data log by m18 id & type logger.info("${doLineRefType}: Finding For Latest M18 Data Log...") val latestDeliveryOrderLineLog = m18DataLogService.findLatestM18DataLogWithSuccess(line.id, doLineRefType) // logger.info("${doLineRefType}: Latest M18 Data Log ID: ${latestDeliveryOrderLineLog?.id}") // Save to m18_data_log table logger.info("${doLineRefType}: Saving for M18 Data Log...") val lineJson = line::class.memberProperties.associate { prop -> prop.name to prop.getter.call( line ) } .toMutableMap() val saveM18DeliveryOrderLineLogRequest = SaveM18DataLogRequest( id = null, refType = doLineRefType, m18Id = line.id, m18LastModifyDate = commonUtils.timestampToLocalDateTime(mainpo.lastModifyDate), // dataLog = lineJson, statusEnum = M18DataLogStatus.NOT_PROCESS ) val saveM18DeliveryOrderLineLog = m18DataLogService.saveM18DataLog(saveM18DeliveryOrderLineLogRequest) // logger.info("${doLineRefType}: Saved M18 Data Log. ID: ${saveM18DeliveryOrderLineLog.id}") logger.info("${doLineRefType}: Finding item...") val item = itemsService.findByM18Id(line.proId) var itemId: Long? = null if (item == null) { itemId = m18MasterDataService.saveProduct(line.proId)?.id } else { itemId = item.id } logger.info("${doLineRefType}: Item ID: ${itemId} | M18 Item ID: ${line.proId}") try { // Find the delivery_order_line if exist logger.info("${doLineRefType}: Finding exising delivery order line...") val existingDeliveryOrderLine = latestDeliveryOrderLineLog?.id?.let { deliveryOrderLineService.findDeliveryOrderLineByM18Id(it) } logger.info("${doLineRefType}: Exising delivery order line ID: ${existingDeliveryOrderLine?.id}") // Save to delivery_order_line table logger.info("${doLineRefType}: Saving delivery order line...") val itemUom = itemId?.let { itemUomService.findSalesUnitByItemId(it) } val saveDeliveryOrderLineRequest = SaveDeliveryOrderLineRequest( id = existingDeliveryOrderLine?.id, itemId = itemId, uomId = itemUom?.uom?.id, deliveryOrderId = deliveryOrderId, qty = line.qty, price = line.amt, // m18CurrencyId = mainpo.curId, status = existingDeliveryOrderLine?.status?.value ?: DeliveryOrderLineStatus.PENDING.value, m18DataLogId = saveM18DeliveryOrderLineLog.id, m18Discount = line.disc, m18Lot = line.lot ) val saveDeliveryOrderLineResponse = deliveryOrderLineService.saveDeliveryOrderLine(saveDeliveryOrderLineRequest) // Update m18_data_log with success val successSaveM18DeliveryOrderLineLogRequest = SaveM18DataLogRequest( id = saveM18DeliveryOrderLineLog.id, dataLog = lineJson, statusEnum = M18DataLogStatus.SUCCESS ) m18DataLogService.saveM18DataLog(successSaveM18DeliveryOrderLineLogRequest) // log success info successDetailList.add(line.id) logger.info("${doLineRefType}: Delivery order ID: ${deliveryOrderId} | M18 ID: ${deliveryOrder.id}") logger.info("${doLineRefType}: Saved delivery order line. ID: ${saveDeliveryOrderLineResponse.id} | M18 Line ID: ${line.id}") } catch (e: Exception) { failDetailList.add(line.id) failItemDetailList.add(line.proId) logger.error("${doLineRefType}: Saving Failure!") logger.error("Error on Function - ${e.stackTrace} | Type: ${doLineRefType} | M18 ID: ${line.id}") logger.error(e.message) val errorSaveM18DeliveryOrderLineLogRequest = SaveM18DataLogRequest( id = saveM18DeliveryOrderLineLog.id, dataLog = mutableMapOf(Pair("Exception Message", e.message)), statusEnum = M18DataLogStatus.FAIL ) m18DataLogService.saveM18DataLog(errorSaveM18DeliveryOrderLineLogRequest) logger.error("${doLineRefType}: M18 Data Log Updated! Please see the error. ID: ${saveM18DeliveryOrderLineLog.id}") } } } else { // pot logger.error("${doLineRefType}: Saving Failure!") val saveM18DeliveryOrderLineLogRequest = SaveM18DataLogRequest( id = null, refType = "${doLineRefType}", m18Id = deliveryOrder.id, m18LastModifyDate = commonUtils.timestampToLocalDateTime(mainpo.lastModifyDate), // dataLog = mutableMapOf(Pair("Error Message", "${doLineRefType} is null")), dataLog = mutableMapOf( Pair( "${doLineRefType} Error Message", "pot is null" ), Pair( "${doLineRefType} Error Code", deliveryOrderLineMessage?.get(0)?.msgCode ?: "No Msg Code from M18" ), Pair( "${doLineRefType} Error Detail", deliveryOrderLineMessage?.get(0)?.msgDetail ?: "No Msg Detail from M18" ), ), statusEnum = M18DataLogStatus.FAIL ) val errorLog = m18DataLogService.saveM18DataLog(saveM18DeliveryOrderLineLogRequest) logger.error("${doLineRefType}: M18 Data Log Updated! Please see the error. ID: ${errorLog.id}") } } else { // mainpo failList.add(deliveryOrder.id) logger.error("${doRefType}: Saving Failure!") val saveM18DataLogRequest = SaveM18DataLogRequest( id = null, refType = "${doRefType}", m18Id = deliveryOrder.id, // m18LastModifyDate = if(mainpo?.lastModifyDate != null) commonUtils.instantToLocalDateTime(mainpo.lastModifyDate) else LocalDateTime.now(), m18LastModifyDate = LocalDateTime.now(), dataLog = mutableMapOf( Pair( "${doRefType} Error", "mainpo is null" ), Pair( "${doRefType} Error Code", deliveryOrdersMessages?.get(0)?.msgCode ?: "No Msg Code from M18" ), Pair( "${doRefType} Error Detail", deliveryOrdersMessages?.get(0)?.msgDetail ?: "No Msg Detail from M18" ), ), statusEnum = M18DataLogStatus.FAIL ) val errorLog = m18DataLogService.saveM18DataLog(saveM18DataLogRequest) logger.error("${doLineRefType}: M18 Data Log Updated! Please see the error. ID: ${errorLog.id}") } } } else { logger.error("${doRefType} List is null. May occur errors.") } } } else { logger.error("${doRefType} List is null. May occur errors.") } // End of save. Check result logger.info("Total Success (${doRefType}) (${successList.size}): $successList") // if (failList.size > 0) { logger.error("Total Fail (${doRefType}) (${failList.size}): $failList") // } logger.info("Total Success (${doLineRefType}) (${successDetailList.size}): $successDetailList") // if (failDetailList.size > 0) { logger.error("Total Fail (${doLineRefType}) (${failDetailList.size}): $failDetailList") logger.error("Total Fail M18 Items (${doLineRefType}) (${failItemDetailList.distinct().size}): ${failItemDetailList.distinct()}") // } logger.info("--------------------------------------------End - Saving M18 Delivery Order--------------------------------------------") } }