| @@ -14,6 +14,7 @@ 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.ItemUom | |||
| import com.ffii.fpsms.modules.master.service.ItemUomService | |||
| import com.ffii.fpsms.modules.master.service.ItemsService | |||
| import com.ffii.fpsms.modules.master.service.ShopService | |||
| @@ -106,7 +107,7 @@ open class M18DeliveryOrderService( | |||
| shopPoConds += "=and=(${dDateEqualConds})" | |||
| } | |||
| println("shopPoConds: ${shopPoConds}") | |||
| logger.info("shopPoConds: ${shopPoConds}") | |||
| val shopPoParams = M18PurchaseOrderListRequest( | |||
| params = null, | |||
| @@ -159,6 +160,9 @@ open class M18DeliveryOrderService( | |||
| val failList = mutableListOf<Long>() | |||
| val failDetailList = mutableListOf<Long>() | |||
| val failItemDetailList = mutableListOf<Long>() | |||
| val uomByM18IdCache = mutableMapOf<Long, ItemUom?>() | |||
| val itemIdCache = mutableMapOf<Long, Long?>() | |||
| val stockUomIdCache = mutableMapOf<Pair<Long, Long>, Long?>() | |||
| val doRefType = "Delivery Order" | |||
| val doLineRefType = "Delivery Order Line" | |||
| @@ -191,7 +195,7 @@ open class M18DeliveryOrderService( | |||
| // logger.info("${doRefType}: Finding For Latest M18 Data Log...") | |||
| val latestDeliveryOrderLog = | |||
| m18DataLogService.findLatestM18DataLogWithSuccess(deliveryOrder.id, doRefType) | |||
| logger.info(latestDeliveryOrderLog.toString()) | |||
| //logger.info(latestDeliveryOrderLog.toString()) | |||
| // Save to m18_data_log table | |||
| // logger.info("${doRefType}: Saving for M18 Data Log...") | |||
| val mainpoJson = | |||
| @@ -305,14 +309,23 @@ open class M18DeliveryOrderService( | |||
| // 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 | |||
| val itemId: Long? = itemIdCache.getOrPut(line.proId) { | |||
| val item = itemsService.findByM18Id(line.proId) | |||
| if (item == null) { | |||
| m18MasterDataService.saveProduct(line.proId)?.id | |||
| } else { | |||
| item.id | |||
| } | |||
| } | |||
| val stockUomId: Long? = if (itemId != null) { | |||
| val key = line.proId to line.unitId // safe key | |||
| stockUomIdCache.getOrPut(key) { | |||
| val uom = itemUomService.findByM18Id(line.unitId) | |||
| itemUomService.findStockUnitByItemId(itemId)?.uom?.id | |||
| } | |||
| } else null | |||
| // logger.info("${doLineRefType}: Item ID: ${itemId} | M18 Item ID: ${line.proId}") | |||
| try { | |||
| @@ -325,12 +338,15 @@ open class M18DeliveryOrderService( | |||
| // Save to delivery_order_line table | |||
| // logger.info("${doLineRefType}: Saving delivery order line...") | |||
| val itemUom = itemUomService.findByM18Id(line.unitId) | |||
| val itemUom = uomByM18IdCache.getOrPut(line.unitId) { | |||
| itemUomService.findByM18Id(line.unitId) | |||
| } | |||
| val saveDeliveryOrderLineRequest = SaveDeliveryOrderLineRequest( | |||
| id = existingDeliveryOrderLine?.id, | |||
| itemId = itemId, | |||
| uomIdM18 = itemUom?.uom?.id, | |||
| uomId= itemUomService.findStockUnitByItemId(itemId?: 0)?.uom?.id, | |||
| uomId= stockUomId, | |||
| deliveryOrderId = deliveryOrderId, | |||
| qtyM18 = line.qty, | |||
| qty = itemUomService.convertQtyToStockQty(itemId?:0, itemUom?.uom?.id?: 0, line.qty), | |||
| @@ -359,7 +375,7 @@ open class M18DeliveryOrderService( | |||
| // 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} | Delivery order ID: ${deliveryOrderId} | M18 ID: ${deliveryOrder.id}") | |||
| //logger.info("${doLineRefType}: Saved delivery order line. ID: ${saveDeliveryOrderLineResponse.id} | M18 Line ID: ${line.id} | Delivery order ID: ${deliveryOrderId} | M18 ID: ${deliveryOrder.id}") | |||
| } catch (e: SQLException) { | |||
| failDetailList.add(line.id) | |||
| failItemDetailList.add(line.proId) | |||
| @@ -17,6 +17,7 @@ import java.math.BigDecimal | |||
| import java.time.LocalDateTime | |||
| import java.time.format.DateTimeFormatter | |||
| import com.ffii.fpsms.m18.model.SyncResult | |||
| import com.ffii.fpsms.modules.master.entity.Items | |||
| import java.time.Instant | |||
| import java.time.LocalDate | |||
| import java.time.ZoneId | |||
| @@ -233,30 +234,38 @@ open class M18MasterDataService( | |||
| } | |||
| } | |||
| open fun saveProducts(request: M18CommonRequest) : SyncResult{ | |||
| open fun saveProducts(request: M18CommonRequest): SyncResult { | |||
| logger.info("--------------------------------------------Start - Saving M18 Products / Materials--------------------------------------------") | |||
| val items = getProducts(request) | |||
| val exampleProducts = listOf<Long>(10946L, 3825L) | |||
| // ── New: cache for findByM18Id ───────────────────────────────────────── | |||
| // M18 item.id → internal Item entity (or just the id if you only need id) | |||
| val itemCache = mutableMapOf<Long, Items?>() | |||
| val successList = mutableListOf<Long>() | |||
| val failList = mutableListOf<Long>() | |||
| val values = items?.values?.sortedBy { it.id } | |||
| if (values != null) { | |||
| values.forEach { item -> | |||
| // if (item.id in exampleProducts) { | |||
| // if (item.id in exampleProducts) { // keep your debug filter if needed | |||
| try { | |||
| val itemDetail = getProduct(item.id) | |||
| val pro = itemDetail?.data?.pro?.get(0) | |||
| val price = itemDetail?.data?.price | |||
| if (itemDetail != null && pro != null) { | |||
| val existingItem = itemsService.findByM18Id(item.id) | |||
| // ── Use cache instead of direct call ──────────────────────── | |||
| val existingItem = itemCache.getOrPut(item.id) { | |||
| itemsService.findByM18Id(item.id) | |||
| } | |||
| val saveItemRequest = NewItemRequest( | |||
| code = pro.code, | |||
| name = pro.desc, | |||
| // type = if (pro.seriesId == m18Config.SERIESID_PF) ProductType.MATERIAL | |||
| // else ItemType.PRODUCT, | |||
| type = when (pro.udfProducttype) { | |||
| M18ItemType.CONSUMABLES.type -> ItemType.CONSUMABLES.type | |||
| M18ItemType.NONCONSUMABLES.type -> ItemType.NONCONSUMABLES.type | |||
| @@ -287,18 +296,19 @@ open class M18MasterDataService( | |||
| val savedItem = itemsService.saveItem(saveItemRequest) | |||
| logger.info("Processing item uom...") | |||
| // Find the item uom that ready to delete (not in m18) | |||
| // Optional: cache findAllByItemsId if you think it might be called multiple times | |||
| // (usually not needed here because each savedItem.id is unique) | |||
| val existingItemUoms = savedItem.id?.let { itemUomService.findAllByItemsId(it) } | |||
| val m18ItemUomIds = price?.map { it.id } ?: listOf() | |||
| // Delete the item uom | |||
| // Delete old UOMs not present in M18 | |||
| logger.info("Deleting item uom...") | |||
| // logger.info("Item Uom: ${existingItemUoms?.map { it.m18Id }}") | |||
| // logger.info("M18: ${m18ItemUomIds}") | |||
| existingItemUoms?.filter { it.m18Id !in m18ItemUomIds }?.mapNotNull { it.id } | |||
| ?.let { itemUomService.deleteItemUoms(it) } | |||
| // Update the item uom | |||
| // Update / create UOMs from M18 | |||
| logger.info("Updating item uom...") | |||
| price?.forEach { | |||
| val endMillis = it.endDate | |||
| @@ -319,11 +329,9 @@ open class M18MasterDataService( | |||
| m18LastModifyDate = commonUtils.timestampToLocalDateTime(pro.lastModifyDate), | |||
| ratioD = it.ratioD, | |||
| ratioN = it.ratioN, | |||
| //deleted = it.expired | |||
| deleted = endInstant.isBefore(now) | |||
| ) | |||
| // logger.info("saved item id: ${savedItem.id}") | |||
| itemUomService.saveItemUom(itemUomRequest) | |||
| } | |||
| @@ -340,13 +348,13 @@ open class M18MasterDataService( | |||
| logger.error("Fail Message: ${e.message}") | |||
| logger.error("Fail Count ${failList.size}: Item ID - ${item.id}") | |||
| } | |||
| // } // end of exampleProducts filter | |||
| } | |||
| } else { | |||
| logger.error("Items List is null. May occur errors.") | |||
| } | |||
| logger.info("Total Success (${successList.size})") | |||
| if (failList.size > 0) { | |||
| logger.error("Total Fail (${failList.size}): $failList") | |||
| } | |||
| @@ -106,51 +106,51 @@ class M18TestController ( | |||
| @GetMapping("/product/{id}") | |||
| fun m18Product(@PathVariable id: Long) { | |||
| logger.info("Access token: ${m18Config.ACCESS_TOKEN}") | |||
| //logger.info("Access token: ${m18Config.ACCESS_TOKEN}") | |||
| m18MasterDataService.saveProduct(id) | |||
| } | |||
| @PostMapping("/vendor") | |||
| fun m18Vendor(@Valid @RequestBody request: M18CommonRequest) { | |||
| logger.info("Access token: ${m18Config.ACCESS_TOKEN}") | |||
| //logger.info("Access token: ${m18Config.ACCESS_TOKEN}") | |||
| m18MasterDataService.saveVendors(request) | |||
| } | |||
| @PostMapping("/unit") | |||
| fun m18Unit(@Valid @RequestBody request: M18CommonRequest) { | |||
| logger.info("Access token: ${m18Config.ACCESS_TOKEN}") | |||
| //logger.info("Access token: ${m18Config.ACCESS_TOKEN}") | |||
| m18MasterDataService.saveUnits(request) | |||
| } | |||
| @PostMapping("/currency") | |||
| fun m18Currency(@Valid @RequestBody request: M18CommonRequest) { | |||
| logger.info("Access token: ${m18Config.ACCESS_TOKEN}") | |||
| //logger.info("Access token: ${m18Config.ACCESS_TOKEN}") | |||
| m18MasterDataService.saveCurrencies(request) | |||
| } | |||
| @PostMapping("/bom") | |||
| fun m18Bom(@Valid @RequestBody request: M18CommonRequest) { | |||
| logger.info("Access token: ${m18Config.ACCESS_TOKEN}") | |||
| //logger.info("Access token: ${m18Config.ACCESS_TOKEN}") | |||
| m18MasterDataService.saveBoms(request) | |||
| } | |||
| @PostMapping("/businessUnit") | |||
| fun m18BusinessUnit(@Valid @RequestBody request: M18CommonRequest) { | |||
| logger.info("Access token: ${m18Config.ACCESS_TOKEN}") | |||
| //logger.info("Access token: ${m18Config.ACCESS_TOKEN}") | |||
| m18MasterDataService.saveBusinessUnits(request) | |||
| } | |||
| // --------------------------------------------- Purchase Order --------------------------------------------- /// | |||
| @PostMapping("/po") | |||
| fun m18PO(@Valid @RequestBody request: M18CommonRequest) { | |||
| logger.info("Access token: ${m18Config.ACCESS_TOKEN}") | |||
| //logger.info("Access token: ${m18Config.ACCESS_TOKEN}") | |||
| m18PurchaseOrderService.savePurchaseOrders(request) | |||
| } | |||
| // --------------------------------------------- Delivery Order --------------------------------------------- /// | |||
| @PostMapping("/do") | |||
| fun m18DO(@Valid @RequestBody request: M18CommonRequest) { | |||
| logger.info("Access token: ${m18Config.ACCESS_TOKEN}") | |||
| //logger.info("Access token: ${m18Config.ACCESS_TOKEN}") | |||
| m18DeliveryOrderService.saveDeliveryOrders(request) | |||
| } | |||
| @@ -0,0 +1,37 @@ | |||
| spring: | |||
| datasource: | |||
| jdbc-url: jdbc:mysql://127.0.0.1:3306/fpsmsdb?useUnicode=true&characterEncoding=UTF8&serverTimezone=GMT%2B8 | |||
| username: root | |||
| password: secret | |||
| m18: | |||
| config: | |||
| grant-type: password | |||
| client-id: M2Y1OGYxMmQtZDRiOS00OTA4LTgyNTktZDRkNzEzNWVkMzRm | |||
| client-secret: N2IzMjY5ZTEtZDIyYy00M2FlLWJhZjktZWU0ODBhYzAyNDA3 | |||
| username: MTMS2 | |||
| password: ea5bfbd761dd1b97bc08354d66169155f11ddaf1 | |||
| base-url: https://toa.m18saas.com/jsf/rfws | |||
| base-url-uat: https://toauat.m18saas.com/jsf/rfws | |||
| base-password: 1648414877 | |||
| supplier: | |||
| shop-po: P06, P07 | |||
| oem-po: T62 | |||
| supplier-not: | |||
| material-po: P06, P07 | |||
| beId: | |||
| toa: 1 | |||
| pf: 27 | |||
| pp: 29 | |||
| seriesId: | |||
| pp: 26 | |||
| pf: 33 | |||
| fa: 2 | |||
| fb: 3 | |||
| fc: 4 | |||
| fd: 5 | |||
| ff: 6 | |||
| sc: 27 | |||
| se: 28 | |||
| sf: 70 | |||
| sr: 29 | |||