|
- 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.model.*
- import com.ffii.fpsms.m18.utils.CommonUtils
- import com.ffii.fpsms.m18.web.models.M18CommonRequest
- import com.ffii.fpsms.modules.master.entity.UomConversion
- import com.ffii.fpsms.modules.master.enums.ShopType
- import com.ffii.fpsms.modules.master.service.*
- import com.ffii.fpsms.modules.master.web.models.*
- import org.slf4j.Logger
- import org.slf4j.LoggerFactory
- import org.springframework.stereotype.Service
- 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
-
- @Service
- open class M18MasterDataService(
- val m18Config: M18Config,
- val apiCallerService: ApiCallerService,
- val itemsService: ItemsService,
- val shopService: ShopService,
- val uomConversionService: UomConversionService,
- val currencyService: CurrencyService,
- val itemUomService: ItemUomService,
- val bomService: BomService,
- val bomMaterialService: BomMaterialService,
- val m18CunitService: M18CunitService,
- ) {
-
- val logger: Logger = LoggerFactory.getLogger(JwtTokenUtil::class.java)
- val commonUtils = CommonUtils()
- val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
- private val one = BigDecimal.ONE
- private val ratio2268 = BigDecimal("2268")
- private val ratio5 = BigDecimal("5")
-
- // M18 Conditions
- // val lastModifyDate = LocalDate.now().minusDays(1)
- // val lastModifyDateConds = "lastModifyDate=largerThan=$lastModifyDate"
- val seriesIdList =
- listOf(m18Config.SERIESID_SC, m18Config.SERIESID_SE, m18Config.SERIESID_SF, m18Config.SERIESID_SR)
- val seriesIdConds =
- "(" + commonUtils.listToString(seriesIdList.filterNotNull(), "seriesId=unequal=", "=or=") + ")"
- val beIdList = listOf(m18Config.BEID_PF, m18Config.BEID_PP, m18Config.BEID_TOA)
- val beIdConds = "(" + commonUtils.listToString(beIdList.filterNotNull(), "beId=equal=", "=or=") + ")"
- // val beIdConds = commonUtils.BEID_CONDS
-
- // M18 API
- val M18_COMMON_FETCH_LIST_API = "/search/search"
- val M18_COMMON_LOAD_LINE_API = "/root/api/read"
-
- val M18_LOAD_PRODUCT_API = "${M18_COMMON_LOAD_LINE_API}/${StSearchType.PRODUCT.value}"
- val M18_LOAD_VENDOR_API = "${M18_COMMON_LOAD_LINE_API}/${StSearchType.VENDOR.value}"
- val M18_LOAD_UNIT_API = "${M18_COMMON_LOAD_LINE_API}/${StSearchType.UNIT.value}"
- val M18_LOAD_CURRENCY_API = "${M18_COMMON_LOAD_LINE_API}/${StSearchType.CURRENCY.value}"
- val M18_LOAD_BOM_API = "${M18_COMMON_LOAD_LINE_API}/${StSearchType.BOM.value}"
- val M18_LOAD_BUSINESS_UNIT_API = "${M18_COMMON_LOAD_LINE_API}/${StSearchType.BUSINESS_UNIT.value}" // for shop po?
-
- private fun isBothOne(ratioN: BigDecimal?, ratioD: BigDecimal?): Boolean {
- return ratioN?.compareTo(one) == 0 && ratioD?.compareTo(one) == 0
- }
-
- private fun isPair2268And5(ratioN: BigDecimal?, ratioD: BigDecimal?): Boolean {
- return ratioN?.compareTo(ratio2268) == 0 && ratioD?.compareTo(ratio5) == 0
- }
-
- /**
- * Problematic ratio cases that should be corrected by m18_cunit:
- * 1) uomId=4 and ratioN/ratioD are not both 1
- * 2) uomId=785 and ratioN/ratioD do not contain 453
- */
- private fun shouldSyncRatioFromCunit(unitId: Long, ratioN: BigDecimal?, ratioD: BigDecimal?): Boolean {
- val caseUom4 = unitId == 4L && !isBothOne(ratioN, ratioD)
- val caseUom785 = unitId == 785L && !isPair2268And5(ratioN, ratioD)
- return caseUom4 || caseUom785
- }
-
- /**
- * Same rule as [shouldSyncRatioFromCunit], but based on local uom_conversion.id.
- */
- private fun shouldSyncRatioFromCunitByLocalUomId(localUomId: Long?, ratioN: BigDecimal?, ratioD: BigDecimal?): Boolean {
- if (localUomId == null) return false
- val caseUom4 = localUomId == 4L && !isBothOne(ratioN, ratioD)
- val caseUom785 = localUomId == 785L && !isPair2268And5(ratioN, ratioD)
- return caseUom4 || caseUom785
- }
-
- /**
- * Safety net: when m18_cunit has no rows, force a full unit sync first so cunit ratios are available.
- */
- open fun ensureCunitSeededForAllIfEmpty() {
- val cunitRows = m18CunitService.countActiveRows()
- if (cunitRows > 0L) return
- logger.warn("m18_cunit has 0 rows; triggering full unit sync first to seed cunit data for all units.")
- val result = saveUnits(M18CommonRequest())
- logger.warn(
- "m18_cunit seed sync finished: processed=${result.totalProcessed}, " +
- "success=${result.totalSuccess}, fail=${result.totalFail}"
- )
- }
-
- // --------------------------------------------- Common Function --------------------------------------------- ///
- private inline fun <reified T : Any> getList(
- stSearch: String?,
- params: String? = null,
- conds: String? = null,
- request: M18CommonRequest,
- ): T? {
- val lastModifyDateFromConds = request.modifiedDateFrom?.let { "lastModifyDate=largerOrEqual=${it}" }
- val lastModifyDateToConds = request.modifiedDateTo?.let{ "lastModifyDate=lessOrEqual=${it}" }
- val haveFromAndTo = lastModifyDateFromConds != null && lastModifyDateToConds != null
- val finalConds = if (lastModifyDateFromConds == null && lastModifyDateToConds == null) {
- conds
- } else {
- conds + "=and=(${lastModifyDateFromConds ?: ""}${if(haveFromAndTo) "=and=" else ""}${lastModifyDateToConds ?: ""})"
- }
-
- val request = M18CommonListRequest(
- stSearch = stSearch,
- params = params,
- conds = finalConds
- )
-
- val response = apiCallerService.get<T, M18CommonListRequest>(
- M18_COMMON_FETCH_LIST_API,
- request
- ).block()
-
- return response
- }
-
- private inline fun <reified T : Any> getLine(
- id: Long,
- params: String?,
- api: String,
- ): T? {
- val request = M18CommonLineRequest(
- id = id,
- params = params,
- )
-
- val response = apiCallerService.get<T, M18CommonLineRequest>(
- api,
- request
- ).block()
-
- return response
- }
-
- // --------------------------------------------- Product --------------------------------------------- ///
- open fun getProducts(request: M18CommonRequest): M18ProductListResponse? {
- // seems no beId
- return getList<M18ProductListResponse>(
- stSearch = StSearchType.PRODUCT.value,
- params = null,
- conds = seriesIdConds,
- request = request
- )
- // val itemsParams = M18CommonListRequest(
- // stSearch = StSearchType.PRODUCT.value,
- // params = null,
- // conds = seriesIdConds
- // )
- //
- // val items = apiCallerService.get<M18ProductListResponse, M18CommonListRequest>(
- // M18_COMMON_FETCH_LIST_API,
- // itemsParams
- // ).block()
- //
- // return items
- }
-
- open fun getProduct(id: Long): M18ProductResponse? {
- logger.info("M18 Product ID: $id")
- return getLine<M18ProductResponse>(
- id = id,
- params = null,
- api = M18_LOAD_PRODUCT_API
- )
- }
-
- /** 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
- }
-
- private fun mapM18ProductType(udfProducttype: String?): String {
- return when (udfProducttype) {
- M18ItemType.CONSUMABLES.type -> ItemType.CONSUMABLES.type
- M18ItemType.NONCONSUMABLES.type -> ItemType.NONCONSUMABLES.type
- M18ItemType.FG.type -> ItemType.FG.type
- M18ItemType.SFG.type -> ItemType.SFG.type
- M18ItemType.ITEM.type -> ItemType.ITEM.type
- else -> ItemType.MATERIAL.type
- }
- }
-
- open fun saveProduct(id: Long): MessageResponse? {
- try {
- ensureCunitSeededForAllIfEmpty()
- val itemDetail = getProduct(id)
- val pro = itemDetail?.data?.pro?.get(0)
- val price = itemDetail?.data?.price
-
- if (itemDetail != null && pro != null) {
- val mappedType = mapM18ProductType(pro.udfProducttype)
- val existingItem = itemsService.findByM18Id(id)
- val saveItemRequest = NewItemRequest(
- code = pro.code,
- name = pro.desc,
- // type = if (pro.seriesId == m18Config.SERIESID_PF) ProductType.MATERIAL
- // else ItemType.PRODUCT,
- type = mappedType,
- id = existingItem?.id,
- description = pro.desc,
- remarks = null,
- shelfLife = null,
- countryOfOrigin = null,
- maxQty = null,
- m18Id = id,
- m18LastModifyDate = commonUtils.timestampToLocalDateTime(pro.lastModifyDate),
- qcCategoryId = null,
- store_id = null,
- warehouse = null,
- area = null,
- slot = null,
- LocationCode = null,
- isEgg = null,
- isFee = null,
- isBag = null,
- qcType = null,
- )
-
- 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
- }
- if (ItemM18IdRemapSupport.isM18IdLinkOnly(existingItem == null, savedItem.message)) {
- logger.warn(
- "Linked M18 product id=$id code=${pro.code} to existing local item id=$localItemId; skip UOM rebuild"
- )
- return savedItem.copy(id = localItemId)
- }
- logger.info("Processing item uom...")
- // Find the item uom that ready to delete (not in m18)
- val existingItemUoms = itemUomService.findAllByItemsId(localItemId)
- val m18ItemUomIds = price?.map { it.id } ?: listOf()
-
- // Delete the item uom
- 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
- logger.info("Updating item uom...")
- val hasBaseUom4 = price?.any { p ->
- p.basicUnit && (uomConversionService.findByM18Id(p.unitId)?.id == 4L)
- } == true
- val forceCunitForAllRows = hasBaseUom4 && (price?.any { p ->
- val localBaseId = uomConversionService.findByM18Id(p.unitId)?.id
- shouldSyncRatioFromCunitByLocalUomId(localBaseId, p.ratioN, p.ratioD)
- } == true)
- price?.forEach {
- val endMillis = it.endDate
- val endInstant = Instant.ofEpochMilli(endMillis)
- val now = Instant.now()
- val localUomId = uomConversionService.findByM18Id(it.unitId)?.id
- val keepOriginalItemRatio = forceCunitForAllRows
- val useUnitRatios = forceCunitForAllRows
- val unitRatios = if (useUnitRatios) m18CunitService.resolveRatiosByM18UnitId(it.unitId) else null
- logger.info(
- "ItemUom ratio apply (single): itemM18Id=$id m18UomId=${it.unitId} localUomId=$localUomId " +
- "hasBaseUom4=$hasBaseUom4 forceCunitForAllRows=$forceCunitForAllRows useCunit=$useUnitRatios oldRatioN=${it.ratioN} oldRatioD=${it.ratioD} " +
- "cunitRatioN=${unitRatios?.ratioN} cunitRatioD=${unitRatios?.ratioD}"
- )
- val itemUomRequest = ItemUomRequest(
- m18UomId = it.unitId,
- itemId = localItemId,
- baseUnit = it.basicUnit,
- stockUnit = it.stkUnit,
- pickingUnit = it.pickUnit,
- salesUnit = it.saleUnit,
- purchaseUnit = it.purUnit,
- price = null,
- currencyId = null,
- m18Id = it.id,
- m18LastModifyDate = commonUtils.timestampToLocalDateTime(pro.lastModifyDate),
- ratioD = if (useUnitRatios) (unitRatios?.ratioD ?: it.ratioD) else it.ratioD,
- ratioN = if (useUnitRatios) (unitRatios?.ratioN ?: it.ratioN) else it.ratioN,
- itemRatioD = if (keepOriginalItemRatio) it.ratioD else null,
- itemRatioN = if (keepOriginalItemRatio) it.ratioN else null,
- deleted = it.expired || endInstant.isBefore(now)
- )
-
- itemUomService.saveItemUom(itemUomRequest)
- }
-
- logger.info("Success (M18 Item): ${id} | ${pro.code} | ${pro.desc}")
- return savedItem.copy(id = localItemId)
- } else {
- logger.error("Fail Message: ${itemDetail?.messages?.get(0)?.msgDetail}")
- logger.error("Fail: Item ID - ${id} Not Found")
- return null
- }
- } catch (e: Exception) {
- logger.error("Exception")
- logger.error("Fail Message: ${e.message}")
- logger.error("Fail: Item ID - ${id}")
- return null
- }
- }
-
- /** Sync one product/material from M18 by item code (search list, then load line — same idea as PO/DO by code). */
- open fun saveProductByCode(code: String): SyncResult {
- val trimmed = code.trim()
- if (trimmed.isEmpty()) {
- return SyncResult(totalProcessed = 1, totalSuccess = 0, totalFail = 1, query = "empty code")
- }
- ensureCunitSeededForAllIfEmpty()
- val fromLocal = itemsService.findByCode(trimmed)?.m18Id
- val m18Id = fromLocal ?: run {
- val conds = "(code=equal=$trimmed)"
- val listResponse = try {
- getList<M18ProductListResponse>(
- stSearch = StSearchType.PRODUCT.value,
- params = null,
- conds = conds,
- request = M18CommonRequest(),
- )
- } catch (e: Exception) {
- logger.error("(saveProductByCode) M18 search failed: ${e.message}", e)
- null
- }
- listResponse?.values?.firstOrNull()?.id
- }
- if (m18Id == null) {
- return SyncResult(
- totalProcessed = 1,
- totalSuccess = 0,
- totalFail = 1,
- query = "code=equal=$trimmed",
- )
- }
- val result = saveProduct(m18Id)
- return if (result != null) {
- SyncResult(totalProcessed = 1, totalSuccess = 1, totalFail = 0, query = "code=equal=$trimmed")
- } else {
- SyncResult(totalProcessed = 1, totalSuccess = 0, totalFail = 1, query = "code=equal=$trimmed")
- }
- }
-
- open fun saveProducts(request: M18CommonRequest): SyncResult {
- logger.info("--------------------------------------------Start - Saving M18 Products / Materials--------------------------------------------")
- ensureCunitSeededForAllIfEmpty()
-
- 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) { // 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 mappedType = mapM18ProductType(pro.udfProducttype)
- // ── 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 = mappedType,
- id = existingItem?.id,
- description = pro.desc,
- remarks = null,
- shelfLife = null,
- countryOfOrigin = null,
- maxQty = null,
- m18Id = item.id,
- m18LastModifyDate = commonUtils.timestampToLocalDateTime(pro.lastModifyDate),
- qcCategoryId = null,
- store_id = null,
- warehouse = null,
- area = null,
- slot = null,
- LocationCode = null,
- isEgg = null,
- isFee = null,
- isBag = null,
- qcType = null
- )
-
- 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
- }
- if (ItemM18IdRemapSupport.isM18IdLinkOnly(existingItem == null, savedItem.message)) {
- logger.warn(
- "Linked M18 product id=${item.id} code=${pro.code} to existing local item id=$localItemId; skip UOM rebuild"
- )
- successList.add(item.id)
- return@forEach
- }
- logger.info("Processing item uom...")
-
- val existingItemUoms = itemUomService.findAllByItemsId(localItemId)
-
- val m18ItemUomIds = price?.map { it.id } ?: listOf()
-
- // Delete old UOMs not present in M18
- logger.info("Deleting item uom...")
- existingItemUoms?.filter { it.m18Id !in m18ItemUomIds }?.mapNotNull { it.id }
- ?.let { itemUomService.deleteItemUoms(it) }
-
- // Update / create UOMs from M18
- logger.info("Updating item uom...")
- val hasBaseUom4 = price?.any { p ->
- p.basicUnit && (uomConversionService.findByM18Id(p.unitId)?.id == 4L)
- } == true
- val forceCunitForAllRows = hasBaseUom4 && (price?.any { p ->
- val localBaseId = uomConversionService.findByM18Id(p.unitId)?.id
- shouldSyncRatioFromCunitByLocalUomId(localBaseId, p.ratioN, p.ratioD)
- } == true)
- price?.forEach {
- val endMillis = it.endDate
- val endInstant = Instant.ofEpochMilli(endMillis)
- val now = Instant.now()
- val localUomId = uomConversionService.findByM18Id(it.unitId)?.id
- val keepOriginalItemRatio = forceCunitForAllRows
- val useUnitRatios = forceCunitForAllRows
- val unitRatios = if (useUnitRatios) m18CunitService.resolveRatiosByM18UnitId(it.unitId) else null
- logger.info(
- "ItemUom ratio apply (bulk): itemM18Id=${item.id} m18UomId=${it.unitId} localUomId=$localUomId " +
- "hasBaseUom4=$hasBaseUom4 forceCunitForAllRows=$forceCunitForAllRows useCunit=$useUnitRatios oldRatioN=${it.ratioN} oldRatioD=${it.ratioD} " +
- "cunitRatioN=${unitRatios?.ratioN} cunitRatioD=${unitRatios?.ratioD}"
- )
-
- val itemUomRequest = ItemUomRequest(
- m18UomId = it.unitId,
- itemId = localItemId,
- baseUnit = it.basicUnit,
- stockUnit = it.stkUnit,
- pickingUnit = it.pickUnit,
- salesUnit = it.saleUnit,
- purchaseUnit = it.purUnit,
- price = null,
- currencyId = null,
- m18Id = it.id,
- m18LastModifyDate = commonUtils.timestampToLocalDateTime(pro.lastModifyDate),
- ratioD = if (useUnitRatios) (unitRatios?.ratioD ?: it.ratioD) else it.ratioD,
- ratioN = if (useUnitRatios) (unitRatios?.ratioN ?: it.ratioN) else it.ratioN,
- itemRatioD = if (keepOriginalItemRatio) it.ratioD else null,
- itemRatioN = if (keepOriginalItemRatio) it.ratioN else null,
- deleted = it.expired || endInstant.isBefore(now)
- )
-
- itemUomService.saveItemUom(itemUomRequest)
- }
-
- successList.add(item.id)
- logger.info("Success Count ${successList.size}: ${item.id} | ${pro.code} | ${pro.desc}")
- } else {
- failList.add(item.id)
- logger.error("Fail Message: ${itemDetail?.messages?.get(0)?.msgDetail}")
- logger.error("Fail Count ${failList.size}: Item ID - ${item.id} Not Found")
- }
- } catch (e: Exception) {
- failList.add(item.id)
- logger.error("Exception")
- 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")
- }
-
- logger.info("--------------------------------------------End - Saving M18 Products / Materials--------------------------------------------")
-
- return SyncResult(
- totalProcessed = successList.size + failList.size,
- totalSuccess = successList.size,
- totalFail = failList.size
- )
- }
-
- /**
- * Targeted re-sync for locally identified problematic item_uom ratios:
- * - uomId = 4 and ratioN != 1
- * - uomId = 785 and ratioN != 453
- *
- * Uses local item_uom + item.m18Id to fetch only affected products from M18 and re-save their item_uom.
- */
- open fun resyncProblemItemUomsFromM18(): SyncResult {
- logger.info("--------------------------------------------Start - Re-sync Problem Item UOMs--------------------------------------------")
- val m18Ids = itemUomService.findDistinctProblemItemM18IdsForUomResync().distinct().sorted()
- val successList = mutableListOf<Long>()
- val failList = mutableListOf<Long>()
- logger.warn("Problem item_uom selector found ${m18Ids.size} item(s) for re-sync.")
- if (m18Ids.isNotEmpty()) {
- logger.warn("Problem item m18Id sample (up to 20): ${m18Ids.take(20)}")
- }
-
- m18Ids.forEach { m18ItemId ->
- try {
- logger.warn("Re-sync item_uom: calling M18 product API for id=$m18ItemId")
- val result = saveProduct(m18ItemId)
- if (result != null) {
- successList.add(m18ItemId)
- logger.warn("Re-sync item_uom: M18 API sync success for id=$m18ItemId")
- } else {
- failList.add(m18ItemId)
- logger.warn("Re-sync item_uom: M18 API sync returned null for id=$m18ItemId")
- }
- } catch (e: Exception) {
- failList.add(m18ItemId)
- logger.error("Re-sync problem item uom failed for m18ItemId=$m18ItemId: ${e.message}", e)
- }
- }
-
- logger.info("Problem item_uom re-sync done. total=${m18Ids.size}, success=${successList.size}, fail=${failList.size}")
- logger.info("--------------------------------------------End - Re-sync Problem Item UOMs--------------------------------------------")
- return SyncResult(
- totalProcessed = m18Ids.size,
- totalSuccess = successList.size,
- totalFail = failList.size
- )
- }
-
- // --------------------------------------------- Vendor --------------------------------------------- ///
- open fun getVendors(request: M18CommonRequest): M18VendorListResponse? {
- return getList<M18VendorListResponse>(
- stSearch = StSearchType.VENDOR.value,
- params = null,
- conds = beIdConds,
- request = request
- )
- }
-
- open fun getVendor(id: Long): M18VendorResponse? {
- logger.info("M18 Vendor ID: $id")
- return getLine<M18VendorResponse>(
- id = id,
- params = null,
- api = M18_LOAD_VENDOR_API
- )
- }
-
- open fun saveVendors(request: M18CommonRequest) : SyncResult{
- logger.info("--------------------------------------------Start - Saving M18 Vendors--------------------------------------------")
- val vendors = getVendors(request)
- val exampleVendors = listOf<Long>(191L)
-
- val successList = mutableListOf<Long>()
- val failList = mutableListOf<Long>()
- val values = vendors?.values?.sortedBy { it.id }
-
- if (values != null) {
- values.forEach { vendor ->
- // if (vendor.id in exampleVendors) {
- try {
- val vendorDetail = getVendor(vendor.id)
-
- if (vendorDetail != null && vendorDetail.data?.ven != null) {
- val ven = vendorDetail.data.ven[0]
-
- val saveShopRequest = SaveShopRequest(
- id = null,
- code = ven.code,
- name = ven.descZhTW.ifEmpty { ven.descZhCN.ifEmpty { ven.desc } },
- brNo = null,
- contactNo = ven.tel,
- contactEmail = ven.email,
- contactName = null,
- addr1 = ven.ad1,
- addr2 = ven.ad2,
- addr3 = ven.ad3,
- addr4 = ven.ad4,
- district = null,
- type = ShopType.SUPPLIER.value,
- m18Id = vendor.id,
- m18LastModifyDate = commonUtils.timestampToLocalDateTime(ven.lastModifyDate)
- )
-
- shopService.saveShop(saveShopRequest)
- successList.add(vendor.id)
- logger.info("Success Count ${successList.size}: ${vendor.id} | ${ven.code} | ${ven.descZhTW.ifEmpty { ven.descZhCN.ifEmpty { ven.desc } }}")
- } else {
- failList.add(vendor.id)
- logger.error("Fail Message: ${vendorDetail?.messages?.get(0)?.msgDetail}")
- logger.error("Fail Count ${failList.size}: Vendor ID - ${vendor.id} Not Found")
- }
- } catch (e: Exception) {
- failList.add(vendor.id)
- logger.error("Exception")
- logger.error("Fail Message: ${e.message}")
- logger.error("Fail Count ${failList.size}: Vendor ID - ${vendor.id}")
- }
- // }
- }
- } else {
- logger.error("Vendor List is null. May occur errors.")
- }
-
- logger.info("Total Success (${successList.size})")
-
- if (failList.size > 0) {
- logger.error("Total Fail (${failList.size}): $failList")
- }
- logger.info("--------------------------------------------End - Saving M18 Vendors--------------------------------------------")
-
- return SyncResult(
- totalProcessed = successList.size + failList.size,
- totalSuccess = successList.size,
- totalFail = failList.size
- )
- }
-
- // --------------------------------------------- Unit (UoM) --------------------------------------------- ///
- open fun getUnits(request: M18CommonRequest): M18UnitListResponse? {
- // seems no beId
- return getList<M18UnitListResponse>(
- stSearch = StSearchType.UNIT.value,
- params = null,
- conds = null,
- request = request
- )
- }
-
- open fun getUnit(id: Long): M18UnitResponse? {
- logger.info("M18 Unit ID: $id")
- return getLine<M18UnitResponse>(
- id = id,
- params = null,
- api = M18_LOAD_UNIT_API
- )
- }
-
- open fun saveUnits(request: M18CommonRequest): SyncResult {
- logger.info("--------------------------------------------Start - Saving M18 Units--------------------------------------------")
- val units = getUnits(request)
-
- val successTransformList = mutableListOf<Long>()
- val successSaveList = mutableListOf<Long>()
- val failTransformList = mutableListOf<Long>()
- val failSaveList = mutableListOf<Long>()
-
- val values = units?.values?.sortedBy { it.id }
- if (values != null) {
- val finalUnitList = arrayListOf<UomConversion>()
-
- // transform unit
- values.forEach { value ->
- try {
- val unitDetail = getUnit(value.id)
-
- if (unitDetail != null && unitDetail.data?.unit != null) {
- val unit = unitDetail.data.unit[0]
- try {
- m18CunitService.replaceForUnit(unit.id, unitDetail.data!!)
- } catch (e: Exception) {
- logger.error("M18 cunit save failed for unit ${unit.id}: ${e.message}", e)
- }
- val tempObject = UomConversionService.BomObject().apply {
- code = unit.code
- udfudesc = unit.udfudesc
- udfShortDesc = unit.udfShortDesc
- lastModifyDate = commonUtils.timestampToLocalDateTime(unit.lastModifyDate).format(formatter)
- id = unit.id
- }
- finalUnitList += uomConversionService.transformItem(tempObject)
- successTransformList += unit.id
- logger.info("Transform Success (M18): ${unit.id}")
- } else {
- failTransformList.add(value.id)
- logger.error("Fail Message: ${unitDetail?.messages?.get(0)?.msgDetail}")
- logger.error("Fail Count ${failTransformList.size}: Unit ID - ${value.id} Not Found")
- }
- } catch (e: Exception) {
- failTransformList.add(value.id)
- logger.error("Transform Exception")
- logger.error("Transform Fail Message: ${e.message}")
- logger.error("Transform Fail Count ${failTransformList.size}: Unit ID - ${value.id}")
- }
- }
-
- uomConversionService.calculateSizeInGram(finalUnitList)
-
- finalUnitList.forEach {
- try {
- uomConversionService.saveUomConversion(it)
- successSaveList += it.m18Id
- logger.info("Save Success (M18): ${it.m18Id}")
- } catch (e: Exception) {
- failSaveList.add(it.m18Id)
- logger.error("Save Exception")
- logger.error("Save Fail Message: ${e.message}")
- logger.error("Save Fail Count ${failTransformList.size}: Unit ID - ${it.m18Id}")
- }
- }
- } else {
- logger.error("Unit List is null. May occur errors.")
- }
-
- logger.info("Total Transform Success (${successTransformList.size})")
- logger.info("Total Save Success (${successSaveList.size})")
-
- if (failTransformList.size > 0) {
- logger.error("Total Transform Fail (${failTransformList.size}): $failTransformList")
- }
-
- if (failSaveList.size > 0) {
- logger.error("Total Save Fail (${failSaveList.size}): $failSaveList")
- }
- logger.info("--------------------------------------------End - Saving M18 Units--------------------------------------------")
- val processed = values?.size ?: 0
- val failed = failTransformList.size + failSaveList.size
- return SyncResult(
- totalProcessed = processed,
- totalSuccess = successSaveList.size,
- totalFail = failed,
- query = "stSearch=${StSearchType.UNIT.value}",
- )
- }
-
- // --------------------------------------------- Currency --------------------------------------------- ///
- open fun getCurrencies(request: M18CommonRequest): M18CurrencyListResponse? {
- return getList<M18CurrencyListResponse>(
- stSearch = StSearchType.CURRENCY.value,
- params = null,
- conds = null,
- request = request
- )
- }
-
- open fun getCurrency(id: Long): M18CurrencyResponse? {
- logger.info("M18 Currency ID: $id")
- return getLine<M18CurrencyResponse>(
- id = id,
- params = null,
- api = M18_LOAD_CURRENCY_API
- )
- }
-
- open fun saveCurrencies(request: M18CommonRequest) : SyncResult{
- logger.info("--------------------------------------------Start - Saving M18 Currencies--------------------------------------------")
- val currencies = getCurrencies(request)
-
- val successList = mutableListOf<Long>()
- val failList = mutableListOf<Long>()
-
- val values = currencies?.values?.sortedBy { it.id }
- if (values != null) {
- // save currency
- values.forEach { currency ->
- try {
- val currencyRequest = SaveCurrencyRequest(
- id = null,
- code = currency.code,
- name = currency.sym,
- description = currency.curDesc,
- m18Id = currency.id,
- m18LastModifyDate = LocalDateTime.parse(currency.lastModifyDate, formatter)
- )
-
- currencyService.saveCurrency(currencyRequest)
- successList += currency.id
- logger.info("Save Success (M18): ${currency.id}")
- } catch (e: Exception) {
- failList += currency.id
- logger.error("Exception")
- logger.error("Fail Message: ${e.message}")
- logger.error("Fail Count ${failList.size}: Unit ID - ${currency.id}")
- }
- }
- } else {
- logger.error("Currency List is null. May occur errors.")
- }
-
- logger.info("Total Save Success (${successList.size})")
-
- if (failList.size > 0) {
- logger.error("Total Fail (${failList.size}): $failList")
- }
-
- logger.info("--------------------------------------------End - Saving Currencies--------------------------------------------")
-
- return SyncResult(
- totalProcessed = successList.size + failList.size,
- totalSuccess = successList.size,
- totalFail = failList.size
- )
- }
-
- // --------------------------------------------- Bom --------------------------------------------- ///
- open fun getBoms(request: M18CommonRequest): M18BomListResponse? {
- return getList<M18BomListResponse>(
- stSearch = StSearchType.BOM.value,
- params = null,
- conds = beIdConds,
- request = request
- )
- }
-
- open fun getBom(id: Long): M18BomResponse? {
- logger.info("M18 Bom ID: $id")
- return getLine<M18BomResponse>(
- id = id,
- params = null,
- api = M18_LOAD_BOM_API
- )
- }
-
- open fun saveBoms(request: M18CommonRequest) {
- logger.info("--------------------------------------------Start - Saving M18 Boms--------------------------------------------")
- val boms = getBoms(request)
-
- val successList = mutableListOf<Long>()
- val successDetailList = mutableListOf<Long>()
- val failList = mutableListOf<Long>()
- val failDetailList = mutableListOf<Pair<Long, MutableList<Long>>>()
- var failDetailCount = 0
-
- val values = boms?.values?.sortedBy { it.id }
- if (values != null) {
- values.forEach { bom ->
- try {
- val bomDetail = getBom(bom.id)
- val bomUdfBomForShop = bomDetail?.data?.udfbomforshop?.get(0)
- val bomUdfProduct = bomDetail?.data?.udfproduct
-
- logger.info(bomUdfBomForShop.toString())
- logger.info(bomUdfProduct.toString())
- if (bomUdfBomForShop != null && bomUdfProduct != null) {
- // Save Bom
- val saveBomRequest = SaveBomRequest(
- // itemId = itemsService.findByNameAndM18UomId(bomUdfBomForShop.desc, bomUdfBomForShop.udfUnit)?.id,
- code = bomUdfBomForShop.code,
- name = bomUdfBomForShop.desc,
- description = bomUdfBomForShop.desc,
- outputQty = if (bomUdfBomForShop.udfHarvest.trim().toBigDecimalOrNull() != null) bomUdfBomForShop.udfHarvest.trim().toBigDecimal() else BigDecimal(0),
- outputQtyUom = bomUdfBomForShop.udfHarvestUnit,
- yield = bomUdfBomForShop.udfYieldratePP,
- m18UomId = bomUdfBomForShop.udfUnit,
- m18Id = bomUdfBomForShop.id,
- m18LastModifyDate = commonUtils.timestampToLocalDateTime(bomUdfBomForShop.lastModifyDate)
- )
- val bomId = bomService.saveBom(saveBomRequest).id
- successList += bom.id
-
- // Save Bom Material
- logger.info("Start saving bom material...")
- val tempFailList = mutableListOf<Long>()
- bomUdfProduct.forEach { bomMaterial ->
- try {
- val saveBomMaterialRequest = SaveBomMaterialRequest(
- m18ItemId = bomMaterial.udfProduct,
- itemName = bomMaterial.udfIngredients,
- qty = bomMaterial.udfqty,
- m18UomId = bomMaterial.udfpurchaseUnit,
- uomName = bomMaterial.udfBaseUnit,
- bomId = bomId,
- m18Id = bomMaterial.id,
- m18LastModifyDate = commonUtils.timestampToLocalDateTime(bomUdfBomForShop.lastModifyDate)
- )
- bomMaterialService.saveBomMaterial(saveBomMaterialRequest)
-
- successDetailList += bomMaterial.id
- } catch (e: Exception) {
- tempFailList += bomMaterial.id
- logger.error("(Bom Material) Exception")
- logger.error("(Bom Material) Fail Message: ${e.message}")
- logger.error("(Bom Material) Fail Count ${++failDetailCount}: Bom Material ID - ${bomMaterial.id} | Bom ID - ${bom.id}")
- }
- }
- failDetailList += Pair(bom.id, tempFailList)
- logger.info("Save Success (M18): ${bom.id}")
- } else {
- failList.add(bom.id)
- logger.error("(Bom) Fail Message: ${bomDetail?.messages?.get(0)?.msgDetail}")
- logger.error("(Bom) Fail Count ${failList.size}: Bom ID - ${bom.id} Not Found")
- }
- } catch (e: Exception) {
- failList += bom.id
- logger.error("(Bom) Exception")
- logger.error("(Bom) Fail Message: ${e.message}")
- logger.error("(Bom) Fail Count ${failList.size}: Bom ID - ${bom.id}")
- }
- }
- } else {
- logger.error("Currency List is null. May occur errors.")
- }
-
- logger.info("Total Bom Save Success (${successList.size})")
- logger.info("Total Bom Save Detail Success (${successDetailList.size})")
-
- if (failList.size > 0) {
- logger.error("Total Bom Fail (${failList.size}): $failList")
- }
-
- if (failDetailCount > 0) {
- logger.error("Total Bom Detail Fail (${failDetailCount}): $failDetailList")
- }
-
- logger.info("--------------------------------------------End - Saving Boms--------------------------------------------")
- }
-
- // --------------------------------------------- Business Unit (Shop) --------------------------------------------- ///
- open fun getBusinessUnits(request: M18CommonRequest): M18BusinessUnitListResponse? {
- // seems no beId
- return getList<M18BusinessUnitListResponse>(
- stSearch = StSearchType.BUSINESS_UNIT.value,
- params = null,
- // conds = beIdConds
- request = request
- )
- }
-
- open fun getBusinessUnit(id: Long): M18BusinessUnitResponse? {
- logger.info("M18 Business Unit ID: $id")
- return getLine<M18BusinessUnitResponse>(
- id = id,
- params = null,
- api = M18_LOAD_BUSINESS_UNIT_API
- )
- }
-
- open fun saveBusinessUnits(request: M18CommonRequest) : SyncResult {
- logger.info("--------------------------------------------Start - Saving M18 Business Units (Shops)--------------------------------------------")
- val businessUnits = getBusinessUnits(request)
-
- val successList = mutableListOf<Long>()
- val failList = mutableListOf<Long>()
- val values = businessUnits?.values?.sortedBy { it.id }
- val busMessages = if(businessUnits?.messages?.isNotEmpty() == true) businessUnits.messages[0] else null
-
- if (values != null) {
- values.forEach { businessUnit ->
- // if (vendor.id in exampleVendors) {
- try {
- val businessUnitDetail = getBusinessUnit(businessUnit.id)
- val virdept = businessUnitDetail?.data?.virdept?.get(0)
- val buMessages = if(businessUnitDetail?.messages?.isNotEmpty() == true) businessUnitDetail?.messages[0] else null
-
- if (virdept != null) {
-
- val saveShopRequest = SaveShopRequest(
- id = null,
- code = virdept.code,
- name = virdept.descZhTW.ifEmpty { virdept.descZhCN.ifEmpty { virdept.desc } },
- brNo = null,
- contactNo = virdept.tel,
- contactEmail = virdept.email,
- contactName = null,
- addr1 = virdept.addr.ifEmpty { virdept.addr_en },
- addr2 = virdept.addr2.ifEmpty { virdept.addr2_en },
- addr3 = virdept.addr3.ifEmpty { virdept.addr3_en },
- addr4 = null,
- district = null,
- type = ShopType.SHOP.value,
- m18Id = businessUnit.id,
- m18LastModifyDate = commonUtils.timestampToLocalDateTime(virdept.lastModifyDate)
- )
-
- shopService.saveShop(saveShopRequest)
- successList.add(businessUnit.id)
- logger.info("Success Count ${successList.size}: ${businessUnit.id} | ${virdept.code} | ${virdept.descZhTW.ifEmpty { virdept.descZhCN.ifEmpty { virdept.desc } }}")
- } else {
- failList.add(businessUnit.id)
- logger.error("(Business Unit) Fail Message: ${buMessages?.msgDetail}")
- logger.error("(Business Unit) Fail Count ${failList.size}: Business Unit ID - ${businessUnit.id} Not Found")
- }
- } catch (e: Exception) {
- failList.add(businessUnit.id)
- logger.error("(Business Unit) Exception")
- logger.error("(Business Unit) Fail Message: ${e.message}")
- logger.error("(Business Unit) Fail Count ${failList.size}: Business Unit ID - ${businessUnit.id}")
- }
- // }
- }
- } else {
- logger.error("(Business Unit) Business Unit List is null. May occur errors.")
- logger.error("(Business Unit)) Fail Message: ${busMessages?.msgDetail}")
- logger.error("(Business Unit) Business Unit List is null. May occur errors.")
- }
-
- logger.info("Total Success (${successList.size})")
-
- if (failList.size > 0) {
- logger.error("Total Fail (${failList.size}): $failList")
- }
- logger.info("--------------------------------------------End - Saving M18 Business Units--------------------------------------------")
-
- return SyncResult(
- totalProcessed = successList.size + failList.size,
- totalSuccess = successList.size,
- totalFail = failList.size
- )
- }
- }
|