| @@ -24,7 +24,6 @@ import com.ffii.fpsms.modules.purchaseOrder.enums.PurchaseOrderType | |||
| import org.slf4j.Logger | |||
| import org.slf4j.LoggerFactory | |||
| import org.springframework.stereotype.Service | |||
| import java.sql.SQLException | |||
| import java.time.LocalDateTime | |||
| import java.time.format.DateTimeFormatter | |||
| import kotlin.reflect.full.memberProperties | |||
| @@ -225,7 +224,7 @@ open class M18DeliveryOrderService( | |||
| val failDetailList = mutableListOf<Long>() | |||
| val failItemDetailList = mutableListOf<Long>() | |||
| val uomByM18IdCache = mutableMapOf<Long, ItemUom?>() | |||
| val itemIdCache = mutableMapOf<Long, Long?>() | |||
| val itemIdCache = mutableMapOf<Long, Long>() | |||
| val stockUomIdCache = mutableMapOf<Pair<Long, Long>, Long?>() | |||
| val doRefType = "Delivery Order" | |||
| @@ -374,14 +373,10 @@ open class M18DeliveryOrderService( | |||
| // logger.info("${doLineRefType}: Saved M18 Data Log. ID: ${saveM18DeliveryOrderLineLog.id}") | |||
| // logger.info("${doLineRefType}: Finding item...") | |||
| 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 itemId: Long? = itemIdCache[line.proId] | |||
| ?: m18MasterDataService.resolveLocalItemId(line.proId)?.also { | |||
| itemIdCache[line.proId] = it | |||
| } | |||
| } | |||
| val stockUomId: Long? = if (itemId != null) { | |||
| val key = line.proId to line.unitId // safe key | |||
| @@ -393,6 +388,23 @@ open class M18DeliveryOrderService( | |||
| // logger.info("${doLineRefType}: Item ID: ${itemId} | M18 Item ID: ${line.proId}") | |||
| if (itemId == null) { | |||
| failDetailList.add(line.id) | |||
| failItemDetailList.add(line.proId) | |||
| logger.error( | |||
| "${doLineRefType}: Cannot resolve local item for M18 proId=${line.proId}, skipping line ${line.id}" | |||
| ) | |||
| val errorSaveM18DeliveryOrderLineLogRequest = SaveM18DataLogRequest( | |||
| id = saveM18DeliveryOrderLineLog.id, | |||
| dataLog = mutableMapOf( | |||
| "Exception Message" to "Cannot resolve local item for M18 proId=${line.proId}" | |||
| ), | |||
| statusEnum = M18DataLogStatus.FAIL | |||
| ) | |||
| m18DataLogService.saveM18DataLog(errorSaveM18DeliveryOrderLineLogRequest) | |||
| return@forEach | |||
| } | |||
| try { | |||
| // Find the delivery_order_line if exist | |||
| // logger.info("${doLineRefType}: Finding exising delivery order line...") | |||
| @@ -454,7 +466,7 @@ open class M18DeliveryOrderService( | |||
| 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}") | |||
| } catch (e: SQLException) { | |||
| } catch (e: Exception) { | |||
| failDetailList.add(line.id) | |||
| failItemDetailList.add(line.proId) | |||
| // logger.error("${doLineRefType}: Saving Failure!") | |||
| @@ -188,6 +188,13 @@ open class M18MasterDataService( | |||
| ) | |||
| } | |||
| /** Resolve local items.id for an M18 product id; sync from M18 when missing. */ | |||
| open fun resolveLocalItemId(m18ItemId: Long): Long? { | |||
| itemsService.findByM18Id(m18ItemId)?.id?.let { return it } | |||
| saveProduct(m18ItemId)?.id?.let { return it } | |||
| return itemsService.findByM18Id(m18ItemId)?.id | |||
| } | |||
| open fun saveProduct(id: Long): MessageResponse? { | |||
| try { | |||
| ensureCunitSeededForAllIfEmpty() | |||
| @@ -231,9 +238,18 @@ open class M18MasterDataService( | |||
| ) | |||
| val savedItem = itemsService.saveItem(saveItemRequest) | |||
| val localItemId = savedItem.id | |||
| if (localItemId == null) { | |||
| logger.error("saveItem returned null id for M18 item $id (code=${pro.code}): ${savedItem.message}") | |||
| return null | |||
| } | |||
| if (savedItem.errorPosition == "code") { | |||
| logger.error("saveItem duplicate code for M18 item $id (code=${pro.code}): ${savedItem.message}") | |||
| return null | |||
| } | |||
| logger.info("Processing item uom...") | |||
| // Find the item uom that ready to delete (not in m18) | |||
| val existingItemUoms = savedItem.id?.let { itemUomService.findAllByItemsId(it) } | |||
| val existingItemUoms = itemUomService.findAllByItemsId(localItemId) | |||
| val m18ItemUomIds = price?.map { it.id } ?: listOf() | |||
| // Delete the item uom | |||
| @@ -267,7 +283,7 @@ open class M18MasterDataService( | |||
| ) | |||
| val itemUomRequest = ItemUomRequest( | |||
| m18UomId = it.unitId, | |||
| itemId = savedItem.id, | |||
| itemId = localItemId, | |||
| baseUnit = it.basicUnit, | |||
| stockUnit = it.stkUnit, | |||
| pickingUnit = it.pickUnit, | |||
| @@ -284,12 +300,11 @@ open class M18MasterDataService( | |||
| deleted = it.expired || endInstant.isBefore(now) | |||
| ) | |||
| // logger.info("saved item id: ${savedItem.id}") | |||
| itemUomService.saveItemUom(itemUomRequest) | |||
| } | |||
| logger.info("Success (M18 Item): ${id} | ${pro.code} | ${pro.desc}") | |||
| return savedItem | |||
| return savedItem.copy(id = localItemId) | |||
| } else { | |||
| logger.error("Fail Message: ${itemDetail?.messages?.get(0)?.msgDetail}") | |||
| logger.error("Fail: Item ID - ${id} Not Found") | |||
| @@ -404,11 +419,20 @@ open class M18MasterDataService( | |||
| ) | |||
| val savedItem = itemsService.saveItem(saveItemRequest) | |||
| val localItemId = savedItem.id | |||
| if (localItemId == null) { | |||
| failList.add(item.id) | |||
| logger.error("saveItem returned null id for M18 item ${item.id} (code=${pro.code}): ${savedItem.message}") | |||
| return@forEach | |||
| } | |||
| if (savedItem.errorPosition == "code") { | |||
| failList.add(item.id) | |||
| logger.error("saveItem duplicate code for M18 item ${item.id} (code=${pro.code}): ${savedItem.message}") | |||
| return@forEach | |||
| } | |||
| logger.info("Processing item uom...") | |||
| // 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 existingItemUoms = itemUomService.findAllByItemsId(localItemId) | |||
| val m18ItemUomIds = price?.map { it.id } ?: listOf() | |||
| @@ -442,7 +466,7 @@ open class M18MasterDataService( | |||
| val itemUomRequest = ItemUomRequest( | |||
| m18UomId = it.unitId, | |||
| itemId = savedItem.id, | |||
| itemId = localItemId, | |||
| baseUnit = it.basicUnit, | |||
| stockUnit = it.stkUnit, | |||
| pickingUnit = it.pickUnit, | |||
| @@ -439,14 +439,25 @@ open class M18PurchaseOrderService( | |||
| // logger.info("${poLineRefType}: Saved M18 Data Log. ID: ${saveM18PurchaseOrderLineLog.id}") | |||
| // logger.info("${poLineRefType}: Finding item...") | |||
| val item = itemsService.findByM18Id(line.proId) | |||
| val itemId: Long? = if (item == null) { | |||
| m18MasterDataService.saveProduct(line.proId)?.id | |||
| } else { | |||
| item.id | |||
| } | |||
| val itemId: Long? = m18MasterDataService.resolveLocalItemId(line.proId) | |||
| logger.info("${poLineRefType}: Item ID: ${itemId} | M18 Item ID: ${line.proId}") | |||
| if (itemId == null) { | |||
| failDetailList.add(line.id) | |||
| logger.error( | |||
| "${poLineRefType}: Cannot resolve local item for M18 proId=${line.proId}, skipping line ${line.id}" | |||
| ) | |||
| val errorSaveM18PurchaseOrderLineLogRequest = SaveM18DataLogRequest( | |||
| id = saveM18PurchaseOrderLineLog.id, | |||
| dataLog = mutableMapOf( | |||
| "Exception Message" to "Cannot resolve local item for M18 proId=${line.proId}" | |||
| ), | |||
| statusEnum = M18DataLogStatus.FAIL | |||
| ) | |||
| m18DataLogService.saveM18DataLog(errorSaveM18PurchaseOrderLineLogRequest) | |||
| return@forEach | |||
| } | |||
| try { | |||
| // Find the purchase_order_line if exist (stable key: PO + M18 line id) | |||
| // logger.info("${poLineRefType}: Finding exising purchase order line...") | |||
| @@ -647,8 +647,21 @@ open class ItemsService( | |||
| open fun saveItem(request: NewItemRequest): MessageResponse { | |||
| val duplicatedItem = itemsRepository.findByCodeAndTypeAndDeletedFalse(request.code, request.type) | |||
| if (duplicatedItem != null && duplicatedItem.id != request.id) { | |||
| if (request.m18Id != null && request.id == null && duplicatedItem.m18Id == null) { | |||
| duplicatedItem.m18Id = request.m18Id | |||
| duplicatedItem.m18LastModifyDate = request.m18LastModifyDate | |||
| val linked = itemsRepository.saveAndFlush(duplicatedItem) | |||
| return MessageResponse( | |||
| id = linked.id, | |||
| code = linked.code, | |||
| name = linked.name, | |||
| type = linked.type.toString(), | |||
| message = "Linked m18Id to existing item with same code", | |||
| errorPosition = null, | |||
| ) | |||
| } | |||
| return MessageResponse( | |||
| id = request.id, | |||
| id = request.id ?: duplicatedItem.id, | |||
| code = request.code, | |||
| name = request.name, | |||
| type = request.type.toString(), | |||