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ů.

M18DeliveryOrderService.kt 35 KiB

před 1 rokem
před 1 rokem
před 1 měsícem
před 1 měsícem
před 1 měsícem
před 1 měsícem
před 1 měsícem
před 1 měsícem
před 1 měsícem
před 1 měsícem
před 11 měsíci
před 11 měsíci
před 11 měsíci
před 11 měsíci
před 11 měsíci
před 11 měsíci
před 11 měsíci
před 11 měsíci
před 11 měsíci
před 11 měsíci
před 11 měsíci
před 11 měsíci
před 11 měsíci
před 11 měsíci
před 1 rokem
před 11 měsíci
před 11 měsíci
před 11 měsíci
před 11 měsíci
před 11 měsíci
před 11 měsíci
před 1 měsícem
před 11 měsíci
před 1 měsícem
před 1 měsícem
před 1 měsícem
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632
  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.entity.DeliveryOrderRepository
  15. import com.ffii.fpsms.modules.deliveryOrder.web.models.SaveDeliveryOrderLineRequest
  16. import com.ffii.fpsms.modules.deliveryOrder.web.models.SaveDeliveryOrderRequest
  17. import com.ffii.fpsms.modules.master.entity.ItemUom
  18. import com.ffii.fpsms.modules.master.service.ItemUomService
  19. import com.ffii.fpsms.modules.master.service.ItemsService
  20. import com.ffii.fpsms.modules.master.service.ShopService
  21. import com.ffii.fpsms.modules.master.web.models.ConvertUomByItemRequest
  22. import com.ffii.fpsms.modules.purchaseOrder.enums.PurchaseOrderType
  23. import org.slf4j.Logger
  24. import org.slf4j.LoggerFactory
  25. import org.springframework.stereotype.Service
  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 deliveryOrderRepository: DeliveryOrderRepository,
  37. val deliveryOrderLineService: DeliveryOrderLineService,
  38. val itemsService: ItemsService,
  39. val shopService: ShopService,
  40. val itemUomService: ItemUomService,
  41. val m18MasterDataService: M18MasterDataService,
  42. ) {
  43. val commonUtils = CommonUtils()
  44. val logger: Logger = LoggerFactory.getLogger(JwtTokenUtil::class.java)
  45. val lastModifyDateStart = "2025-05-14 14:00:00"
  46. val lastModifyDateEnd = "2025-05-14 14:30:00"
  47. // val lastModifyDateConds =
  48. // "lastModifyDate=largerOrEqual=${lastModifyDateStart}=and=lastModifyDate=lessOrEqual=${lastModifyDateEnd}"
  49. // val lastModifyDate = LocalDateTime.now().minusMinutes(30)
  50. // val commonConds =
  51. // "(beId=equal=${m18Config.BEID_PF}=or=beId=equal=${m18Config.BEID_PP}=or=beId=equal=${m18Config.BEID_TOA})=and=lastModifyDate=largerOrEqual=${lastModifyDate}"
  52. // M18 API
  53. // Sharing same API with po
  54. val M18_LOAD_PURCHASE_ORDER_API = "/root/api/read/po"
  55. val M18_FETCH_PURCHASE_ORDER_LIST_API = "/search/search"
  56. // Include shop po
  57. open fun getDeliveryOrdersWithType(request: M18CommonRequest): M18PurchaseOrderListResponseWithType? {
  58. val deliveryOrders = M18PurchaseOrderListResponseWithType(mutableListOf())
  59. val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
  60. val lastDateConds = if (request.modifiedDateFrom != null && request.modifiedDateTo != null) {
  61. val dateFrom = LocalDateTime.parse(request.modifiedDateFrom, formatter).toString()
  62. val dateTo = LocalDateTime.parse(request.modifiedDateTo, formatter).toString()
  63. "lastModifyDate=largerOrEqual=$dateFrom=and=lastModifyDate=lessOrEqual=$dateTo"
  64. } else {
  65. ""
  66. }
  67. val dDateConds = if (request.dDateFrom != null && request.dDateTo != null) {
  68. val dateFrom = LocalDateTime.parse(request.dDateFrom, formatter).toLocalDate().toString()
  69. val dateTo = LocalDateTime.parse(request.dDateTo, formatter).toLocalDate().toString()
  70. "dDate=largerOrEqual=$dateFrom=and=dDate=lessOrEqual=$dateTo"
  71. } else {
  72. ""
  73. }
  74. val dDateEqualConds = if (request.dDateEqual != null) {
  75. val dDateEqual = LocalDateTime.parse(request.dDateEqual, formatter).toLocalDate().toString()
  76. "dDate=equal=$dDateEqual"
  77. } else {
  78. ""
  79. }
  80. // Shop PO
  81. val shopPoBuyers = commonUtils.listToString(listOf(m18Config.BEID_TOA), "beId=equal=", "=or=")
  82. val shopPoSupplier = commonUtils.listToString(
  83. shopService.findM18VendorIdsByCodeRegexp(m18Config.SHOP_PO_SUPPLIER),
  84. "venId=equal=",
  85. "=or="
  86. )
  87. var shopPoConds = "(${shopPoBuyers})=and=(${shopPoSupplier})"
  88. if (request.modifiedDateFrom != null && request.modifiedDateTo != null) {
  89. shopPoConds += "=and=(${lastDateConds})"
  90. }
  91. if (request.dDateFrom != null && request.dDateTo != null) {
  92. shopPoConds += "=and=(${dDateConds})"
  93. }
  94. if (request.dDateEqual != null) {
  95. shopPoConds += "=and=(${dDateEqualConds})"
  96. }
  97. logger.info("shopPoConds: ${shopPoConds}")
  98. val shopPoParams = M18PurchaseOrderListRequest(
  99. params = null,
  100. conds = shopPoConds
  101. )
  102. try {
  103. deliveryOrders.valuesWithType += Pair(
  104. PurchaseOrderType.SHOP, apiCallerService.get<M18PurchaseOrderListResponse, M18PurchaseOrderListRequest>(
  105. M18_FETCH_PURCHASE_ORDER_LIST_API,
  106. shopPoParams
  107. ).block()
  108. )
  109. } catch (e: Exception) {
  110. logger.error("(Getting Shop Po list) Error on Function - ${e.stackTrace}")
  111. logger.error(e.message)
  112. }
  113. deliveryOrders.query = shopPoConds
  114. return deliveryOrders
  115. }
  116. open fun getDeliveryOrder(id: Long): M18PurchaseOrderResponse? {
  117. val deliveryOrderParams = M18PurchaseOrderRequest(
  118. id = id
  119. )
  120. var deliveryOrder: M18PurchaseOrderResponse? = null
  121. try {
  122. deliveryOrder = apiCallerService.get<M18PurchaseOrderResponse, M18PurchaseOrderRequest>(
  123. M18_LOAD_PURCHASE_ORDER_API,
  124. deliveryOrderParams
  125. ).block()
  126. } catch (e: Exception) {
  127. logger.error("(Getting Po Detail) Error on Function - ${e.stackTrace}")
  128. logger.error(e.message)
  129. }
  130. return deliveryOrder
  131. }
  132. open fun saveDeliveryOrders(request: M18CommonRequest, skipExistingDo: Boolean = false): SyncResult {
  133. val deliveryOrdersWithType = getDeliveryOrdersWithType(request)
  134. return saveDeliveryOrdersWithPreparedList(
  135. deliveryOrdersWithType,
  136. syncisExtra = false,
  137. skipExistingDo = skipExistingDo,
  138. )
  139. }
  140. /**
  141. * Sync a single M18 shop PO / delivery order by document [code], same search pattern as
  142. * [com.ffii.fpsms.m18.service.M18PurchaseOrderService.savePurchaseOrderByCode].
  143. *
  144. * @param isExtraSync when true, persist local `delivery_order.isExtra=true` (manual DO(加單) sync).
  145. * No M18-side "加單" filtering is used.
  146. * @param newOnly when true, skip if a non-deleted local DO already exists with the same `code`.
  147. */
  148. open fun saveDeliveryOrderByCode(
  149. code: String,
  150. isExtraSync: Boolean = false,
  151. newOnly: Boolean = false,
  152. ): SyncResult {
  153. if (newOnly && deliveryOrderRepository.existsByCodeAndDeletedIsFalse(code)) {
  154. return SyncResult(
  155. totalProcessed = 1,
  156. totalSuccess = 0,
  157. totalFail = 0,
  158. query = "skipped (newOnly=true): delivery_order.code already exists: $code",
  159. )
  160. }
  161. val conds = "(code=equal=$code)"
  162. val searchRequest = M18PurchaseOrderListRequest(
  163. stSearch = "po",
  164. params = null,
  165. conds = conds
  166. )
  167. val doListResponse = try {
  168. apiCallerService.get<M18PurchaseOrderListResponse, M18PurchaseOrderListRequest>(
  169. M18_FETCH_PURCHASE_ORDER_LIST_API,
  170. searchRequest
  171. ).block()
  172. } catch (e: Exception) {
  173. logger.error("(Getting DO list By Code) Error on Function - ${e.stackTrace}")
  174. logger.error(e.message)
  175. null
  176. }
  177. val doValues = doListResponse?.values
  178. if (doValues.isNullOrEmpty()) {
  179. return SyncResult(
  180. totalProcessed = 1,
  181. totalSuccess = 0,
  182. totalFail = 1,
  183. query = conds
  184. )
  185. }
  186. val prepared = M18PurchaseOrderListResponseWithType(
  187. valuesWithType = mutableListOf(Pair(PurchaseOrderType.SHOP, doListResponse)),
  188. query = conds
  189. )
  190. return saveDeliveryOrdersWithPreparedList(prepared, syncisExtra = isExtraSync, skipExistingDo = newOnly)
  191. }
  192. private fun saveDeliveryOrdersWithPreparedList(
  193. deliveryOrdersWithType: M18PurchaseOrderListResponseWithType?,
  194. syncisExtra: Boolean = false,
  195. skipExistingDo: Boolean = false,
  196. ): SyncResult {
  197. logger.info("--------------------------------------------Start - Saving M18 Delivery Order--------------------------------------------")
  198. if (skipExistingDo) {
  199. logger.info("skipExistingDo=true — local delivery orders will not be updated")
  200. }
  201. val successList = mutableListOf<Long>()
  202. val skippedList = mutableListOf<Long>()
  203. val successDetailList = mutableListOf<Long>()
  204. val failList = mutableListOf<Long>()
  205. val failDetailList = mutableListOf<Long>()
  206. val failItemDetailList = mutableListOf<Long>()
  207. val uomByM18IdCache = mutableMapOf<Long, ItemUom?>()
  208. val itemIdCache = mutableMapOf<Long, Long>()
  209. val stockUomIdCache = mutableMapOf<Pair<Long, Long>, Long?>()
  210. val doRefType = "Delivery Order"
  211. val doLineRefType = "Delivery Order Line"
  212. if (deliveryOrdersWithType != null) {
  213. // Loop for Delivery Orders (values)
  214. deliveryOrdersWithType.valuesWithType.forEach { deliveryOrderWithType ->
  215. val type = deliveryOrderWithType.first
  216. // if success
  217. val deliveryOrdersValues = deliveryOrderWithType.second?.values
  218. // if fail
  219. val deliveryOrdersMessages = deliveryOrderWithType.second?.messages
  220. if (deliveryOrdersValues != null) {
  221. deliveryOrdersValues.forEach { deliveryOrder ->
  222. if (skipExistingDo) {
  223. val latestDeliveryOrderLog =
  224. m18DataLogService.findLatestM18DataLogWithSuccess(deliveryOrder.id, doRefType)
  225. val existingByM18 = latestDeliveryOrderLog?.id?.let {
  226. deliveryOrderService.findByM18DataLogId(it)
  227. }
  228. if (existingByM18 != null && existingByM18.deleted != true) {
  229. logger.info(
  230. "${doRefType}: skipExistingDo — skipping M18 id=${deliveryOrder.id} " +
  231. "code=${existingByM18.code} localId=${existingByM18.id} status=${existingByM18.status}"
  232. )
  233. skippedList.add(deliveryOrder.id)
  234. return@forEach
  235. }
  236. }
  237. val deliveryOrderDetail = getDeliveryOrder(deliveryOrder.id)
  238. var deliveryOrderId: Long? = null //FP-MTMS
  239. // Process for Delivery Order (mainpo)
  240. // Assume only one DO in the DO (search by DO ID)
  241. val mainpo = deliveryOrderDetail?.data?.mainpo?.get(0)
  242. // 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}")
  243. val pot = deliveryOrderDetail?.data?.pot
  244. val deliveryOrderLineMessage = deliveryOrderDetail?.messages
  245. // delivery_order + m18_data_log table
  246. if (mainpo != null) {
  247. if (skipExistingDo && deliveryOrderRepository.existsByCodeAndDeletedIsFalse(mainpo.code)) {
  248. logger.info(
  249. "${doRefType}: skipExistingDo — skipping M18 id=${deliveryOrder.id} code=${mainpo.code} (local DO exists by code)"
  250. )
  251. skippedList.add(deliveryOrder.id)
  252. return@forEach
  253. }
  254. // Find the latest m18 data log by m18 id & type
  255. // logger.info("${doRefType}: Finding For Latest M18 Data Log...")
  256. val latestDeliveryOrderLog =
  257. m18DataLogService.findLatestM18DataLogWithSuccess(deliveryOrder.id, doRefType)
  258. //logger.info(latestDeliveryOrderLog.toString())
  259. // Save to m18_data_log table
  260. // logger.info("${doRefType}: Saving for M18 Data Log...")
  261. val mainpoJson =
  262. mainpo::class.memberProperties.associate { prop -> prop.name to prop.getter.call(mainpo) }
  263. .toMutableMap()
  264. val saveM18DeliveryOrderLogRequest = SaveM18DataLogRequest(
  265. id = null,
  266. refType = doRefType,
  267. m18Id = deliveryOrder.id,
  268. m18LastModifyDate = commonUtils.timestampToLocalDateTime(mainpo.lastModifyDate),
  269. // dataLog = mainpoJson,
  270. statusEnum = M18DataLogStatus.NOT_PROCESS
  271. )
  272. val saveM18DeliveryOrderLog =
  273. m18DataLogService.saveM18DataLog(saveM18DeliveryOrderLogRequest)
  274. // logger.info("${doRefType}: Saved M18 Data Log. ID: ${saveM18DeliveryOrderLog.id}")
  275. try {
  276. // Find the delivery_order if exist
  277. // logger.info("${doRefType}: Finding exising delivery order...")
  278. val existingDeliveryOrder =
  279. latestDeliveryOrderLog?.id?.let { deliveryOrderService.findByM18DataLogId(it) }
  280. // logger.info("${doRefType}: Exising delivery order ID: ${existingDeliveryOrder?.id}")
  281. // Save to delivery_order table
  282. // logger.info("${doRefType}: Saving delivery order...")
  283. val saveDeliveryOrderRequest = SaveDeliveryOrderRequest(
  284. id = existingDeliveryOrder?.id,
  285. code = mainpo.code,
  286. m18SupplierId = mainpo.venId,
  287. m18ShopId = mainpo.virDeptId,
  288. m18CurrencyId = mainpo.curId,
  289. orderDate = commonUtils.timestampToLocalDateTime(mainpo.tDate),
  290. estimatedArrivalDate = commonUtils.timestampToLocalDateTime(mainpo.dDate),
  291. completeDate = null,
  292. status = DeliveryOrderStatus.PENDING.value,
  293. type = type.value,
  294. m18DataLogId = saveM18DeliveryOrderLog.id,
  295. handlerId = null,
  296. m18BeId = mainpo.beId,
  297. deleted = mainpo.udfIsVoid == true,
  298. isExtra = syncisExtra,
  299. )
  300. val saveDeliveryOrderResponse =
  301. deliveryOrderService.saveDeliveryOrder(saveDeliveryOrderRequest)
  302. deliveryOrderId = saveDeliveryOrderResponse.id
  303. // Update m18_data_log with success
  304. val successSaveM18DeliveryOrderLogRequest = SaveM18DataLogRequest(
  305. id = saveM18DeliveryOrderLog.id,
  306. dataLog = mainpoJson,
  307. statusEnum = M18DataLogStatus.SUCCESS
  308. )
  309. m18DataLogService.saveM18DataLog(successSaveM18DeliveryOrderLogRequest)
  310. // log success info
  311. successList.add(deliveryOrder.id)
  312. logger.info("${doRefType}: Saved delivery order. ID: ${saveDeliveryOrderResponse.id} | M18 ${doRefType} ID: ${deliveryOrder.id}")
  313. } catch (e: Exception) {
  314. failList.add(deliveryOrder.id)
  315. // logger.error("${doRefType}: Saving Failure!")
  316. logger.error("Error on Function - ${e.stackTrace} | Type: ${doRefType} | M18 ID: ${deliveryOrder.id} | Different? ${mainpo.id}")
  317. logger.error(e.message)
  318. val errorSaveM18DeliveryOrderLogRequest = SaveM18DataLogRequest(
  319. id = saveM18DeliveryOrderLog.id,
  320. dataLog = mutableMapOf(Pair("Exception Message", e.message)),
  321. statusEnum = M18DataLogStatus.FAIL
  322. )
  323. m18DataLogService.saveM18DataLog(errorSaveM18DeliveryOrderLogRequest)
  324. // logger.error("${doRefType}: M18 Data Log Updated! Please see the error. ID: ${saveM18DeliveryOrderLogRequest.id}")
  325. }
  326. // delivery_order_line + m18_data_log
  327. if (pot != null) {
  328. val m18LineIds = pot.map { it.id }.toSet()
  329. // Loop for Delivery Order Lines (pot)
  330. pot.forEach { line ->
  331. // Find the latest m18 data log by m18 id & type
  332. // logger.info("${doLineRefType}: Finding For Latest M18 Data Log...")
  333. val latestDeliveryOrderLineLog =
  334. m18DataLogService.findLatestM18DataLogWithSuccess(line.id, doLineRefType)
  335. // logger.info("${doLineRefType}: Latest M18 Data Log ID: ${latestDeliveryOrderLineLog?.id}")
  336. // Save to m18_data_log table
  337. // logger.info("${doLineRefType}: Saving for M18 Data Log...")
  338. val lineJson =
  339. line::class.memberProperties.associate { prop ->
  340. prop.name to prop.getter.call(
  341. line
  342. )
  343. }
  344. .toMutableMap()
  345. val saveM18DeliveryOrderLineLogRequest = SaveM18DataLogRequest(
  346. id = null,
  347. refType = doLineRefType,
  348. m18Id = line.id,
  349. m18LastModifyDate = commonUtils.timestampToLocalDateTime(mainpo.lastModifyDate),
  350. // dataLog = lineJson,
  351. statusEnum = M18DataLogStatus.NOT_PROCESS
  352. )
  353. val saveM18DeliveryOrderLineLog =
  354. m18DataLogService.saveM18DataLog(saveM18DeliveryOrderLineLogRequest)
  355. // logger.info("${doLineRefType}: Saved M18 Data Log. ID: ${saveM18DeliveryOrderLineLog.id}")
  356. // logger.info("${doLineRefType}: Finding item...")
  357. val itemId: Long? = itemIdCache[line.proId]
  358. ?: m18MasterDataService.resolveLocalItemId(line.proId)?.also {
  359. itemIdCache[line.proId] = it
  360. }
  361. val stockUomId: Long? = if (itemId != null) {
  362. val key = line.proId to line.unitId // safe key
  363. stockUomIdCache.getOrPut(key) {
  364. val uom = itemUomService.findByM18Id(line.unitId)
  365. itemUomService.findStockUnitByItemId(itemId)?.uom?.id
  366. }
  367. } else null
  368. // logger.info("${doLineRefType}: Item ID: ${itemId} | M18 Item ID: ${line.proId}")
  369. if (itemId == null) {
  370. failDetailList.add(line.id)
  371. failItemDetailList.add(line.proId)
  372. logger.error(
  373. "${doLineRefType}: Cannot resolve local item for M18 proId=${line.proId}, skipping line ${line.id}"
  374. )
  375. val errorSaveM18DeliveryOrderLineLogRequest = SaveM18DataLogRequest(
  376. id = saveM18DeliveryOrderLineLog.id,
  377. dataLog = mutableMapOf(
  378. "Exception Message" to "Cannot resolve local item for M18 proId=${line.proId}"
  379. ),
  380. statusEnum = M18DataLogStatus.FAIL
  381. )
  382. m18DataLogService.saveM18DataLog(errorSaveM18DeliveryOrderLineLogRequest)
  383. return@forEach
  384. }
  385. try {
  386. // Find the delivery_order_line if exist
  387. // logger.info("${doLineRefType}: Finding exising delivery order line...")
  388. val existingDeliveryOrderLine = latestDeliveryOrderLineLog?.id?.let {
  389. deliveryOrderLineService.findDeliveryOrderLineByM18Id(it)
  390. }
  391. // logger.info("${doLineRefType}: Exising delivery order line ID: ${existingDeliveryOrderLine?.id}")
  392. // Save to delivery_order_line table
  393. // logger.info("${doLineRefType}: Saving delivery order line...")
  394. val itemUom = uomByM18IdCache.getOrPut(line.unitId) {
  395. itemUomService.findByM18Id(line.unitId)
  396. }
  397. val m18UomId = itemUom?.uom?.id
  398. val sourceQty = line.qty
  399. val stockQty =
  400. if (itemId != null && m18UomId != null && m18UomId == stockUomId) {
  401. // M18 line unit is already the stock unit — skip ratio conversion
  402. // (avoids bad qty when item_uom ratioN/ratioD hold spec numbers like 350g).
  403. sourceQty
  404. } else if (itemId != null && m18UomId != null) {
  405. itemUomService.convertQtyToStockQty(itemId, m18UomId, sourceQty)
  406. } else {
  407. sourceQty
  408. }
  409. val saveDeliveryOrderLineRequest = SaveDeliveryOrderLineRequest(
  410. id = existingDeliveryOrderLine?.id,
  411. itemId = itemId,
  412. uomIdM18 = m18UomId,
  413. uomId= stockUomId,
  414. deliveryOrderId = deliveryOrderId,
  415. qtyM18 = sourceQty,
  416. qty = stockQty,
  417. up = line.up,
  418. price = line.amt,
  419. // m18CurrencyId = mainpo.curId,
  420. status = existingDeliveryOrderLine?.status?.value
  421. ?: DeliveryOrderLineStatus.PENDING.value,
  422. m18DataLogId = saveM18DeliveryOrderLineLog.id,
  423. m18Discount = line.disc,
  424. m18Lot = line.lot
  425. )
  426. val saveDeliveryOrderLineResponse =
  427. deliveryOrderLineService.saveDeliveryOrderLine(saveDeliveryOrderLineRequest)
  428. // Update m18_data_log with success
  429. val successSaveM18DeliveryOrderLineLogRequest = SaveM18DataLogRequest(
  430. id = saveM18DeliveryOrderLineLog.id,
  431. dataLog = lineJson,
  432. statusEnum = M18DataLogStatus.SUCCESS
  433. )
  434. m18DataLogService.saveM18DataLog(successSaveM18DeliveryOrderLineLogRequest)
  435. // log success info
  436. successDetailList.add(line.id)
  437. // logger.info("${doLineRefType}: Delivery order ID: ${deliveryOrderId} | M18 ID: ${deliveryOrder.id}")
  438. //logger.info("${doLineRefType}: Saved delivery order line. ID: ${saveDeliveryOrderLineResponse.id} | M18 Line ID: ${line.id} | Delivery order ID: ${deliveryOrderId} | M18 ID: ${deliveryOrder.id}")
  439. } catch (e: Exception) {
  440. failDetailList.add(line.id)
  441. failItemDetailList.add(line.proId)
  442. // logger.error("${doLineRefType}: Saving Failure!")
  443. logger.error("Error on Function - ${e.stackTrace} | Type: ${doLineRefType} | M18 ID: ${line.id}")
  444. logger.error(e.message)
  445. val errorSaveM18DeliveryOrderLineLogRequest = SaveM18DataLogRequest(
  446. id = saveM18DeliveryOrderLineLog.id,
  447. dataLog = mutableMapOf(Pair("Exception Message", e.message)),
  448. statusEnum = M18DataLogStatus.FAIL
  449. )
  450. m18DataLogService.saveM18DataLog(errorSaveM18DeliveryOrderLineLogRequest)
  451. // logger.error("${doLineRefType}: M18 Data Log Updated! Please see the error. ID: ${saveM18DeliveryOrderLineLog.id}")
  452. }
  453. }
  454. if (deliveryOrderId != null) {
  455. val markedDeleted = deliveryOrderLineService.markDeletedLinesMissingFromM18(
  456. deliveryOrderId = deliveryOrderId,
  457. existingM18LineIds = m18LineIds,
  458. m18RefType = doLineRefType
  459. )
  460. if (markedDeleted > 0) {
  461. logger.info("${doLineRefType}: Marked ${markedDeleted} stale line(s) as deleted for deliveryOrderId=${deliveryOrderId}")
  462. }
  463. val dupesRemoved = deliveryOrderLineService.markDuplicateDeliveryOrderLinesForSameM18LineId(
  464. deliveryOrderId = deliveryOrderId,
  465. existingM18LineIds = m18LineIds,
  466. m18RefType = doLineRefType
  467. )
  468. if (dupesRemoved > 0) {
  469. logger.info("${doLineRefType}: Marked ${dupesRemoved} duplicate line(s) as deleted (same M18 line id) for deliveryOrderId=${deliveryOrderId}")
  470. }
  471. }
  472. } else {
  473. // pot
  474. // logger.error("${doLineRefType}: Saving Failure!")
  475. val saveM18DeliveryOrderLineLogRequest = SaveM18DataLogRequest(
  476. id = null,
  477. refType = "${doLineRefType}",
  478. m18Id = deliveryOrder.id,
  479. m18LastModifyDate = commonUtils.timestampToLocalDateTime(mainpo.lastModifyDate),
  480. // dataLog = mutableMapOf(Pair("Error Message", "${doLineRefType} is null")),
  481. dataLog = mutableMapOf(
  482. Pair(
  483. "${doLineRefType} Error Message",
  484. "pot is null"
  485. ),
  486. Pair(
  487. "${doLineRefType} Error Code",
  488. deliveryOrderLineMessage?.get(0)?.msgCode ?: "No Msg Code from M18"
  489. ),
  490. Pair(
  491. "${doLineRefType} Error Detail",
  492. deliveryOrderLineMessage?.get(0)?.msgDetail ?: "No Msg Detail from M18"
  493. ),
  494. ),
  495. statusEnum = M18DataLogStatus.FAIL
  496. )
  497. val errorLog = m18DataLogService.saveM18DataLog(saveM18DeliveryOrderLineLogRequest)
  498. logger.error("${doLineRefType}: M18 Data Log Updated! Please see the error. ID: ${errorLog.id}")
  499. }
  500. } else {
  501. // mainpo
  502. failList.add(deliveryOrder.id)
  503. // logger.error("${doRefType}: Saving Failure!")
  504. val saveM18DataLogRequest = SaveM18DataLogRequest(
  505. id = null,
  506. refType = "${doRefType}",
  507. m18Id = deliveryOrder.id,
  508. // m18LastModifyDate = if(mainpo?.lastModifyDate != null) commonUtils.instantToLocalDateTime(mainpo.lastModifyDate) else LocalDateTime.now(),
  509. m18LastModifyDate = LocalDateTime.now(),
  510. dataLog = mutableMapOf(
  511. Pair(
  512. "${doRefType} Error",
  513. "mainpo is null"
  514. ),
  515. Pair(
  516. "${doRefType} Error Code",
  517. deliveryOrdersMessages?.get(0)?.msgCode ?: "No Msg Code from M18"
  518. ),
  519. Pair(
  520. "${doRefType} Error Detail",
  521. deliveryOrdersMessages?.get(0)?.msgDetail ?: "No Msg Detail from M18"
  522. ),
  523. ),
  524. statusEnum = M18DataLogStatus.FAIL
  525. )
  526. val errorLog = m18DataLogService.saveM18DataLog(saveM18DataLogRequest)
  527. logger.error("${doLineRefType}: M18 Data Log Updated! Please see the error. ID: ${errorLog.id}")
  528. }
  529. }
  530. } else {
  531. logger.error("${doRefType} List is null. May occur errors.")
  532. }
  533. }
  534. } else {
  535. logger.error("${doRefType} List is null. May occur errors.")
  536. }
  537. // End of save. Check result
  538. logger.info("Total Success (${doRefType}) (${successList.size})")
  539. logger.error("Total Fail (${doRefType}) (${failList.size}): $failList")
  540. if (skippedList.isNotEmpty()) {
  541. logger.info("Total Skipped (${doRefType}) (${skippedList.size}): $skippedList")
  542. }
  543. logger.info("Total Success (${doLineRefType}) (${successDetailList.size})")
  544. logger.error("Total Fail (${doLineRefType}) (${failDetailList.size}): $failDetailList")
  545. // Fee cleanup for DO lines (currently disabled; keep for future use)
  546. // val feeMarked = deliveryOrderLineService.markDeletedLinesWithFeeItems()
  547. // if (feeMarked > 0) {
  548. // logger.info("Marked $feeMarked DO line(s) as deleted (isFee items).")
  549. // }
  550. logger.info("--------------------------------------------End - Saving M18 Delivery Order--------------------------------------------")
  551. val skippedSuffix = if (skippedList.isNotEmpty()) " | skipped=${skippedList.size}" else ""
  552. return SyncResult(
  553. totalProcessed = successList.size + failList.size + skippedList.size,
  554. totalSuccess = successList.size,
  555. totalFail = failList.size,
  556. query = (deliveryOrdersWithType?.query ?: "") + skippedSuffix,
  557. )
  558. }
  559. }