Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.
 
 
 
 
 

485 řádky
27 KiB

  1. package com.ffii.fpsms.m18.service
  2. import com.ffii.core.utils.JwtTokenUtil
  3. import com.ffii.fpsms.api.service.ApiCallerService
  4. import com.ffii.fpsms.m18.M18Config
  5. import com.ffii.fpsms.m18.enums.M18DataLogStatus
  6. import com.ffii.fpsms.m18.model.*
  7. import com.ffii.fpsms.m18.model.SyncResult
  8. import com.ffii.fpsms.m18.utils.CommonUtils
  9. import com.ffii.fpsms.m18.web.models.M18CommonRequest
  10. import com.ffii.fpsms.modules.deliveryOrder.enums.DeliveryOrderLineStatus
  11. import com.ffii.fpsms.modules.deliveryOrder.enums.DeliveryOrderStatus
  12. import com.ffii.fpsms.modules.deliveryOrder.service.DeliveryOrderLineService
  13. import com.ffii.fpsms.modules.deliveryOrder.service.DeliveryOrderService
  14. import com.ffii.fpsms.modules.deliveryOrder.web.models.SaveDeliveryOrderLineRequest
  15. import com.ffii.fpsms.modules.deliveryOrder.web.models.SaveDeliveryOrderRequest
  16. import com.ffii.fpsms.modules.master.entity.ItemUom
  17. import com.ffii.fpsms.modules.master.service.ItemUomService
  18. import com.ffii.fpsms.modules.master.service.ItemsService
  19. import com.ffii.fpsms.modules.master.service.ShopService
  20. import com.ffii.fpsms.modules.master.web.models.ConvertUomByItemRequest
  21. import com.ffii.fpsms.modules.purchaseOrder.enums.PurchaseOrderType
  22. import org.slf4j.Logger
  23. import org.slf4j.LoggerFactory
  24. import org.springframework.stereotype.Service
  25. import java.sql.SQLException
  26. import java.time.LocalDateTime
  27. import java.time.format.DateTimeFormatter
  28. import kotlin.reflect.full.memberProperties
  29. import kotlin.text.substring
  30. @Service
  31. open class M18DeliveryOrderService(
  32. val m18Config: M18Config,
  33. val apiCallerService: ApiCallerService,
  34. val m18DataLogService: M18DataLogService,
  35. val deliveryOrderService: DeliveryOrderService,
  36. val deliveryOrderLineService: DeliveryOrderLineService,
  37. val itemsService: ItemsService,
  38. val shopService: ShopService,
  39. val itemUomService: ItemUomService,
  40. val m18MasterDataService: M18MasterDataService,
  41. ) {
  42. val commonUtils = CommonUtils()
  43. val logger: Logger = LoggerFactory.getLogger(JwtTokenUtil::class.java)
  44. val lastModifyDateStart = "2025-05-14 14:00:00"
  45. val lastModifyDateEnd = "2025-05-14 14:30:00"
  46. // val lastModifyDateConds =
  47. // "lastModifyDate=largerOrEqual=${lastModifyDateStart}=and=lastModifyDate=lessOrEqual=${lastModifyDateEnd}"
  48. // val lastModifyDate = LocalDateTime.now().minusMinutes(30)
  49. // val commonConds =
  50. // "(beId=equal=${m18Config.BEID_PF}=or=beId=equal=${m18Config.BEID_PP}=or=beId=equal=${m18Config.BEID_TOA})=and=lastModifyDate=largerOrEqual=${lastModifyDate}"
  51. // M18 API
  52. // Sharing same API with po
  53. val M18_LOAD_PURCHASE_ORDER_API = "/root/api/read/po"
  54. val M18_FETCH_PURCHASE_ORDER_LIST_API = "/search/search"
  55. // Include shop po
  56. open fun getDeliveryOrdersWithType(request: M18CommonRequest): M18PurchaseOrderListResponseWithType? {
  57. val deliveryOrders = M18PurchaseOrderListResponseWithType(mutableListOf())
  58. val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
  59. val lastDateConds = if (request.modifiedDateFrom != null && request.modifiedDateTo != null) {
  60. val dateFrom = LocalDateTime.parse(request.modifiedDateFrom, formatter).toString()
  61. val dateTo = LocalDateTime.parse(request.modifiedDateTo, formatter).toString()
  62. "lastModifyDate=largerOrEqual=$dateFrom=and=lastModifyDate=lessOrEqual=$dateTo"
  63. } else {
  64. ""
  65. }
  66. val dDateConds = if (request.dDateFrom != null && request.dDateTo != null) {
  67. val dateFrom = LocalDateTime.parse(request.dDateFrom, formatter).toLocalDate().toString()
  68. val dateTo = LocalDateTime.parse(request.dDateTo, formatter).toLocalDate().toString()
  69. "dDate=largerOrEqual=$dateFrom=and=dDate=lessOrEqual=$dateTo"
  70. } else {
  71. ""
  72. }
  73. val dDateEqualConds = if (request.dDateEqual != null) {
  74. val dDateEqual = LocalDateTime.parse(request.dDateEqual, formatter).toLocalDate().toString()
  75. "dDate=equal=$dDateEqual"
  76. } else {
  77. ""
  78. }
  79. // Shop PO
  80. val shopPoBuyers = commonUtils.listToString(listOf(m18Config.BEID_TOA), "beId=equal=", "=or=")
  81. val shopPoSupplier = commonUtils.listToString(
  82. shopService.findM18VendorIdsByCodeRegexp(m18Config.SHOP_PO_SUPPLIER),
  83. "venId=equal=",
  84. "=or="
  85. )
  86. var shopPoConds = "(${shopPoBuyers})=and=(${shopPoSupplier})"
  87. if (request.modifiedDateFrom != null && request.modifiedDateTo != null) {
  88. shopPoConds += "=and=(${lastDateConds})"
  89. }
  90. if (request.dDateFrom != null && request.dDateTo != null) {
  91. shopPoConds += "=and=(${dDateConds})"
  92. }
  93. if (request.dDateEqual != null) {
  94. shopPoConds += "=and=(${dDateEqualConds})"
  95. }
  96. logger.info("shopPoConds: ${shopPoConds}")
  97. val shopPoParams = M18PurchaseOrderListRequest(
  98. params = null,
  99. conds = shopPoConds
  100. )
  101. try {
  102. deliveryOrders.valuesWithType += Pair(
  103. PurchaseOrderType.SHOP, apiCallerService.get<M18PurchaseOrderListResponse, M18PurchaseOrderListRequest>(
  104. M18_FETCH_PURCHASE_ORDER_LIST_API,
  105. shopPoParams
  106. ).block()
  107. )
  108. } catch (e: Exception) {
  109. logger.error("(Getting Shop Po list) Error on Function - ${e.stackTrace}")
  110. logger.error(e.message)
  111. }
  112. deliveryOrders.query = shopPoConds
  113. return deliveryOrders
  114. }
  115. open fun getDeliveryOrder(id: Long): M18PurchaseOrderResponse? {
  116. val deliveryOrderParams = M18PurchaseOrderRequest(
  117. id = id
  118. )
  119. var deliveryOrder: M18PurchaseOrderResponse? = null
  120. try {
  121. deliveryOrder = apiCallerService.get<M18PurchaseOrderResponse, M18PurchaseOrderRequest>(
  122. M18_LOAD_PURCHASE_ORDER_API,
  123. deliveryOrderParams
  124. ).block()
  125. } catch (e: Exception) {
  126. logger.error("(Getting Po Detail) Error on Function - ${e.stackTrace}")
  127. logger.error(e.message)
  128. }
  129. return deliveryOrder
  130. }
  131. open fun saveDeliveryOrders(request: M18CommonRequest) : SyncResult{
  132. logger.info("--------------------------------------------Start - Saving M18 Delivery Order--------------------------------------------")
  133. val deliveryOrdersWithType = getDeliveryOrdersWithType(request)
  134. val successList = mutableListOf<Long>()
  135. val successDetailList = mutableListOf<Long>()
  136. val failList = mutableListOf<Long>()
  137. val failDetailList = mutableListOf<Long>()
  138. val failItemDetailList = mutableListOf<Long>()
  139. val uomByM18IdCache = mutableMapOf<Long, ItemUom?>()
  140. val itemIdCache = mutableMapOf<Long, Long?>()
  141. val stockUomIdCache = mutableMapOf<Pair<Long, Long>, Long?>()
  142. val doRefType = "Delivery Order"
  143. val doLineRefType = "Delivery Order Line"
  144. if (deliveryOrdersWithType != null) {
  145. // Loop for Delivery Orders (values)
  146. deliveryOrdersWithType.valuesWithType.forEach { deliveryOrderWithType ->
  147. val type = deliveryOrderWithType.first
  148. // if success
  149. val deliveryOrdersValues = deliveryOrderWithType.second?.values
  150. // if fail
  151. val deliveryOrdersMessages = deliveryOrderWithType.second?.messages
  152. if (deliveryOrdersValues != null) {
  153. deliveryOrdersValues.forEach { deliveryOrder ->
  154. val deliveryOrderDetail = getDeliveryOrder(deliveryOrder.id)
  155. var deliveryOrderId: Long? = null //FP-MTMS
  156. // Process for Delivery Order (mainpo)
  157. // Assume only one DO in the DO (search by DO ID)
  158. val mainpo = deliveryOrderDetail?.data?.mainpo?.get(0)
  159. // logger.info("deliveryOrderDetail: data is null? ${deliveryOrderDetail?.data == null} | mainpo is null? ${deliveryOrderDetail?.data?.mainpo == null} | get(0) is null? ${deliveryOrderDetail?.data?.mainpo?.get(0) == null}")
  160. val pot = deliveryOrderDetail?.data?.pot
  161. val deliveryOrderLineMessage = deliveryOrderDetail?.messages
  162. // delivery_order + m18_data_log table
  163. if (mainpo != null) {
  164. // Find the latest m18 data log by m18 id & type
  165. // logger.info("${doRefType}: Finding For Latest M18 Data Log...")
  166. val latestDeliveryOrderLog =
  167. m18DataLogService.findLatestM18DataLogWithSuccess(deliveryOrder.id, doRefType)
  168. //logger.info(latestDeliveryOrderLog.toString())
  169. // Save to m18_data_log table
  170. // logger.info("${doRefType}: Saving for M18 Data Log...")
  171. val mainpoJson =
  172. mainpo::class.memberProperties.associate { prop -> prop.name to prop.getter.call(mainpo) }
  173. .toMutableMap()
  174. val saveM18DeliveryOrderLogRequest = SaveM18DataLogRequest(
  175. id = null,
  176. refType = doRefType,
  177. m18Id = deliveryOrder.id,
  178. m18LastModifyDate = commonUtils.timestampToLocalDateTime(mainpo.lastModifyDate),
  179. // dataLog = mainpoJson,
  180. statusEnum = M18DataLogStatus.NOT_PROCESS
  181. )
  182. val saveM18DeliveryOrderLog =
  183. m18DataLogService.saveM18DataLog(saveM18DeliveryOrderLogRequest)
  184. // logger.info("${doRefType}: Saved M18 Data Log. ID: ${saveM18DeliveryOrderLog.id}")
  185. try {
  186. // Find the delivery_order if exist
  187. // logger.info("${doRefType}: Finding exising delivery order...")
  188. val existingDeliveryOrder =
  189. latestDeliveryOrderLog?.id?.let { deliveryOrderService.findByM18DataLogId(it) }
  190. // logger.info("${doRefType}: Exising delivery order ID: ${existingDeliveryOrder?.id}")
  191. // Save to delivery_order table
  192. // logger.info("${doRefType}: Saving delivery order...")
  193. val saveDeliveryOrderRequest = SaveDeliveryOrderRequest(
  194. id = existingDeliveryOrder?.id,
  195. code = mainpo.code,
  196. m18SupplierId = mainpo.venId,
  197. m18ShopId = mainpo.virDeptId,
  198. m18CurrencyId = mainpo.curId,
  199. orderDate = commonUtils.timestampToLocalDateTime(mainpo.tDate),
  200. estimatedArrivalDate = commonUtils.timestampToLocalDateTime(mainpo.dDate),
  201. completeDate = null,
  202. status = DeliveryOrderStatus.PENDING.value,
  203. type = type.value,
  204. m18DataLogId = saveM18DeliveryOrderLog.id,
  205. handlerId = null,
  206. m18BeId = mainpo.beId,
  207. deleted = mainpo.udfIsVoid == true
  208. )
  209. val saveDeliveryOrderResponse =
  210. deliveryOrderService.saveDeliveryOrder(saveDeliveryOrderRequest)
  211. deliveryOrderId = saveDeliveryOrderResponse.id
  212. // Update m18_data_log with success
  213. val successSaveM18DeliveryOrderLogRequest = SaveM18DataLogRequest(
  214. id = saveM18DeliveryOrderLog.id,
  215. dataLog = mainpoJson,
  216. statusEnum = M18DataLogStatus.SUCCESS
  217. )
  218. m18DataLogService.saveM18DataLog(successSaveM18DeliveryOrderLogRequest)
  219. // log success info
  220. successList.add(deliveryOrder.id)
  221. logger.info("${doRefType}: Saved delivery order. ID: ${saveDeliveryOrderResponse.id} | M18 ${doRefType} ID: ${deliveryOrder.id}")
  222. } catch (e: Exception) {
  223. failList.add(deliveryOrder.id)
  224. // logger.error("${doRefType}: Saving Failure!")
  225. logger.error("Error on Function - ${e.stackTrace} | Type: ${doRefType} | M18 ID: ${deliveryOrder.id} | Different? ${mainpo.id}")
  226. logger.error(e.message)
  227. val errorSaveM18DeliveryOrderLogRequest = SaveM18DataLogRequest(
  228. id = saveM18DeliveryOrderLog.id,
  229. dataLog = mutableMapOf(Pair("Exception Message", e.message)),
  230. statusEnum = M18DataLogStatus.FAIL
  231. )
  232. m18DataLogService.saveM18DataLog(errorSaveM18DeliveryOrderLogRequest)
  233. // logger.error("${doRefType}: M18 Data Log Updated! Please see the error. ID: ${saveM18DeliveryOrderLogRequest.id}")
  234. }
  235. // delivery_order_line + m18_data_log
  236. // TODO: check deleted po line?
  237. if (pot != null) {
  238. // Loop for Delivery Order Lines (pot)
  239. pot.forEach { line ->
  240. // Find the latest m18 data log by m18 id & type
  241. // logger.info("${doLineRefType}: Finding For Latest M18 Data Log...")
  242. val latestDeliveryOrderLineLog =
  243. m18DataLogService.findLatestM18DataLogWithSuccess(line.id, doLineRefType)
  244. // logger.info("${doLineRefType}: Latest M18 Data Log ID: ${latestDeliveryOrderLineLog?.id}")
  245. // Save to m18_data_log table
  246. // logger.info("${doLineRefType}: Saving for M18 Data Log...")
  247. val lineJson =
  248. line::class.memberProperties.associate { prop ->
  249. prop.name to prop.getter.call(
  250. line
  251. )
  252. }
  253. .toMutableMap()
  254. val saveM18DeliveryOrderLineLogRequest = SaveM18DataLogRequest(
  255. id = null,
  256. refType = doLineRefType,
  257. m18Id = line.id,
  258. m18LastModifyDate = commonUtils.timestampToLocalDateTime(mainpo.lastModifyDate),
  259. // dataLog = lineJson,
  260. statusEnum = M18DataLogStatus.NOT_PROCESS
  261. )
  262. val saveM18DeliveryOrderLineLog =
  263. m18DataLogService.saveM18DataLog(saveM18DeliveryOrderLineLogRequest)
  264. // logger.info("${doLineRefType}: Saved M18 Data Log. ID: ${saveM18DeliveryOrderLineLog.id}")
  265. // logger.info("${doLineRefType}: Finding item...")
  266. val itemId: Long? = itemIdCache.getOrPut(line.proId) {
  267. val item = itemsService.findByM18Id(line.proId)
  268. if (item == null) {
  269. m18MasterDataService.saveProduct(line.proId)?.id
  270. } else {
  271. item.id
  272. }
  273. }
  274. val stockUomId: Long? = if (itemId != null) {
  275. val key = line.proId to line.unitId // safe key
  276. stockUomIdCache.getOrPut(key) {
  277. val uom = itemUomService.findByM18Id(line.unitId)
  278. itemUomService.findStockUnitByItemId(itemId)?.uom?.id
  279. }
  280. } else null
  281. // logger.info("${doLineRefType}: Item ID: ${itemId} | M18 Item ID: ${line.proId}")
  282. try {
  283. // Find the delivery_order_line if exist
  284. // logger.info("${doLineRefType}: Finding exising delivery order line...")
  285. val existingDeliveryOrderLine = latestDeliveryOrderLineLog?.id?.let {
  286. deliveryOrderLineService.findDeliveryOrderLineByM18Id(it)
  287. }
  288. // logger.info("${doLineRefType}: Exising delivery order line ID: ${existingDeliveryOrderLine?.id}")
  289. // Save to delivery_order_line table
  290. // logger.info("${doLineRefType}: Saving delivery order line...")
  291. val itemUom = uomByM18IdCache.getOrPut(line.unitId) {
  292. itemUomService.findByM18Id(line.unitId)
  293. }
  294. val saveDeliveryOrderLineRequest = SaveDeliveryOrderLineRequest(
  295. id = existingDeliveryOrderLine?.id,
  296. itemId = itemId,
  297. uomIdM18 = itemUom?.uom?.id,
  298. uomId= stockUomId,
  299. deliveryOrderId = deliveryOrderId,
  300. qtyM18 = line.qty,
  301. qty = itemUomService.convertQtyToStockQty(itemId?:0, itemUom?.uom?.id?: 0, line.qty),
  302. up = line.up,
  303. price = line.amt,
  304. // m18CurrencyId = mainpo.curId,
  305. status = existingDeliveryOrderLine?.status?.value
  306. ?: DeliveryOrderLineStatus.PENDING.value,
  307. m18DataLogId = saveM18DeliveryOrderLineLog.id,
  308. m18Discount = line.disc,
  309. m18Lot = line.lot
  310. )
  311. val saveDeliveryOrderLineResponse =
  312. deliveryOrderLineService.saveDeliveryOrderLine(saveDeliveryOrderLineRequest)
  313. // Update m18_data_log with success
  314. val successSaveM18DeliveryOrderLineLogRequest = SaveM18DataLogRequest(
  315. id = saveM18DeliveryOrderLineLog.id,
  316. dataLog = lineJson,
  317. statusEnum = M18DataLogStatus.SUCCESS
  318. )
  319. m18DataLogService.saveM18DataLog(successSaveM18DeliveryOrderLineLogRequest)
  320. // log success info
  321. successDetailList.add(line.id)
  322. // logger.info("${doLineRefType}: Delivery order ID: ${deliveryOrderId} | M18 ID: ${deliveryOrder.id}")
  323. //logger.info("${doLineRefType}: Saved delivery order line. ID: ${saveDeliveryOrderLineResponse.id} | M18 Line ID: ${line.id} | Delivery order ID: ${deliveryOrderId} | M18 ID: ${deliveryOrder.id}")
  324. } catch (e: SQLException) {
  325. failDetailList.add(line.id)
  326. failItemDetailList.add(line.proId)
  327. // logger.error("${doLineRefType}: Saving Failure!")
  328. logger.error("Error on Function - ${e.stackTrace} | Type: ${doLineRefType} | M18 ID: ${line.id}")
  329. logger.error(e.message)
  330. val errorSaveM18DeliveryOrderLineLogRequest = SaveM18DataLogRequest(
  331. id = saveM18DeliveryOrderLineLog.id,
  332. dataLog = mutableMapOf(Pair("Exception Message", e.message)),
  333. statusEnum = M18DataLogStatus.FAIL
  334. )
  335. m18DataLogService.saveM18DataLog(errorSaveM18DeliveryOrderLineLogRequest)
  336. // logger.error("${doLineRefType}: M18 Data Log Updated! Please see the error. ID: ${saveM18DeliveryOrderLineLog.id}")
  337. }
  338. }
  339. } else {
  340. // pot
  341. // logger.error("${doLineRefType}: Saving Failure!")
  342. val saveM18DeliveryOrderLineLogRequest = SaveM18DataLogRequest(
  343. id = null,
  344. refType = "${doLineRefType}",
  345. m18Id = deliveryOrder.id,
  346. m18LastModifyDate = commonUtils.timestampToLocalDateTime(mainpo.lastModifyDate),
  347. // dataLog = mutableMapOf(Pair("Error Message", "${doLineRefType} is null")),
  348. dataLog = mutableMapOf(
  349. Pair(
  350. "${doLineRefType} Error Message",
  351. "pot is null"
  352. ),
  353. Pair(
  354. "${doLineRefType} Error Code",
  355. deliveryOrderLineMessage?.get(0)?.msgCode ?: "No Msg Code from M18"
  356. ),
  357. Pair(
  358. "${doLineRefType} Error Detail",
  359. deliveryOrderLineMessage?.get(0)?.msgDetail ?: "No Msg Detail from M18"
  360. ),
  361. ),
  362. statusEnum = M18DataLogStatus.FAIL
  363. )
  364. val errorLog = m18DataLogService.saveM18DataLog(saveM18DeliveryOrderLineLogRequest)
  365. logger.error("${doLineRefType}: M18 Data Log Updated! Please see the error. ID: ${errorLog.id}")
  366. }
  367. } else {
  368. // mainpo
  369. failList.add(deliveryOrder.id)
  370. // logger.error("${doRefType}: Saving Failure!")
  371. val saveM18DataLogRequest = SaveM18DataLogRequest(
  372. id = null,
  373. refType = "${doRefType}",
  374. m18Id = deliveryOrder.id,
  375. // m18LastModifyDate = if(mainpo?.lastModifyDate != null) commonUtils.instantToLocalDateTime(mainpo.lastModifyDate) else LocalDateTime.now(),
  376. m18LastModifyDate = LocalDateTime.now(),
  377. dataLog = mutableMapOf(
  378. Pair(
  379. "${doRefType} Error",
  380. "mainpo is null"
  381. ),
  382. Pair(
  383. "${doRefType} Error Code",
  384. deliveryOrdersMessages?.get(0)?.msgCode ?: "No Msg Code from M18"
  385. ),
  386. Pair(
  387. "${doRefType} Error Detail",
  388. deliveryOrdersMessages?.get(0)?.msgDetail ?: "No Msg Detail from M18"
  389. ),
  390. ),
  391. statusEnum = M18DataLogStatus.FAIL
  392. )
  393. val errorLog = m18DataLogService.saveM18DataLog(saveM18DataLogRequest)
  394. logger.error("${doLineRefType}: M18 Data Log Updated! Please see the error. ID: ${errorLog.id}")
  395. }
  396. }
  397. } else {
  398. logger.error("${doRefType} List is null. May occur errors.")
  399. }
  400. }
  401. } else {
  402. logger.error("${doRefType} List is null. May occur errors.")
  403. }
  404. // End of save. Check result
  405. logger.info("Total Success (${doRefType}) (${successList.size})")
  406. logger.error("Total Fail (${doRefType}) (${failList.size}): $failList")
  407. logger.info("Total Success (${doLineRefType}) (${successDetailList.size})")
  408. logger.error("Total Fail (${doLineRefType}) (${failDetailList.size}): $failDetailList")
  409. val feeMarked = deliveryOrderLineService.markDeletedLinesWithFeeItems()
  410. if (feeMarked > 0) {
  411. logger.info("Marked $feeMarked DO line(s) as deleted (isFee items).")
  412. }
  413. logger.info("--------------------------------------------End - Saving M18 Delivery Order--------------------------------------------")
  414. return SyncResult(
  415. totalProcessed = successList.size + failList.size,
  416. totalSuccess = successList.size,
  417. totalFail = failList.size,
  418. query = deliveryOrdersWithType?.query ?: ""
  419. )
  420. }
  421. }