Sfoglia il codice sorgente

[Update M18 API Related + Purchase Order + Currency + Item UoM + Shop + Qc Item]

create_edit_user
cyril.tsui 3 mesi fa
parent
commit
96474569bd
41 ha cambiato i file con 1147 aggiunte e 407 eliminazioni
  1. +2
    -3
      src/main/java/com/ffii/fpsms/api/service/ApiCallerService.kt
  2. +41
    -28
      src/main/java/com/ffii/fpsms/m18/M18Config.kt
  3. +13
    -31
      src/main/java/com/ffii/fpsms/m18/model/M18MasterDataRequest.kt
  4. +83
    -8
      src/main/java/com/ffii/fpsms/m18/model/M18MasterDataResponse.kt
  5. +17
    -4
      src/main/java/com/ffii/fpsms/m18/model/M18PurchaseOrderResponse.kt
  6. +289
    -114
      src/main/java/com/ffii/fpsms/m18/service/M18MasterDataService.kt
  7. +261
    -152
      src/main/java/com/ffii/fpsms/m18/service/M18PurchaseOrderService.kt
  8. +1
    -1
      src/main/java/com/ffii/fpsms/m18/service/M18TokenService.kt
  9. +2
    -2
      src/main/java/com/ffii/fpsms/m18/utils/CommonUtils.kt
  10. +33
    -3
      src/main/java/com/ffii/fpsms/m18/web/M18TestController.kt
  11. +34
    -0
      src/main/java/com/ffii/fpsms/modules/master/entity/Currency.kt
  12. +11
    -0
      src/main/java/com/ffii/fpsms/modules/master/entity/CurrencyRepository.kt
  13. +55
    -0
      src/main/java/com/ffii/fpsms/modules/master/entity/ItemUom.kt
  14. +18
    -0
      src/main/java/com/ffii/fpsms/modules/master/entity/ItemUomRespository.kt
  15. +3
    -1
      src/main/java/com/ffii/fpsms/modules/master/entity/Items.kt
  16. +13
    -0
      src/main/java/com/ffii/fpsms/modules/master/entity/ShopRepository.kt
  17. +3
    -0
      src/main/java/com/ffii/fpsms/modules/master/entity/UomConversionRepository.kt
  18. +5
    -0
      src/main/java/com/ffii/fpsms/modules/master/entity/projections/SearchId.kt
  19. +33
    -0
      src/main/java/com/ffii/fpsms/modules/master/service/CurrencyService.kt
  20. +57
    -0
      src/main/java/com/ffii/fpsms/modules/master/service/ItemUomService.kt
  21. +11
    -0
      src/main/java/com/ffii/fpsms/modules/master/service/ShopService.kt
  22. +5
    -1
      src/main/java/com/ffii/fpsms/modules/master/service/UomConversionService.kt
  23. +1
    -42
      src/main/java/com/ffii/fpsms/modules/master/web/QcItemController.kt
  24. +12
    -0
      src/main/java/com/ffii/fpsms/modules/master/web/models/CurrencyRequest.kt
  25. +27
    -0
      src/main/java/com/ffii/fpsms/modules/master/web/models/ItemUomRequest.kt
  26. +7
    -1
      src/main/java/com/ffii/fpsms/modules/purchaseOrder/entity/PurchaseOrder.kt
  27. +8
    -5
      src/main/java/com/ffii/fpsms/modules/purchaseOrder/entity/PurchaseOrderLine.kt
  28. +6
    -0
      src/main/java/com/ffii/fpsms/modules/purchaseOrder/enums/PurchaseOrderEnum.kt
  29. +15
    -0
      src/main/java/com/ffii/fpsms/modules/purchaseOrder/enums/PurchaseOrderEnumConverter.kt
  30. +0
    -2
      src/main/java/com/ffii/fpsms/modules/purchaseOrder/enums/PurchaseOrderLineEnum.kt
  31. +0
    -0
      src/main/java/com/ffii/fpsms/modules/purchaseOrder/enums/PurchaseOrderLineEnumConverter.kt
  32. +8
    -5
      src/main/java/com/ffii/fpsms/modules/purchaseOrder/service/PurchaseOrderLineService.kt
  33. +3
    -0
      src/main/java/com/ffii/fpsms/modules/purchaseOrder/service/PurchaseOrderService.kt
  34. +2
    -2
      src/main/java/com/ffii/fpsms/modules/purchaseOrder/web/model/SavePurchaseOrderLineRequest.kt
  35. +1
    -2
      src/main/java/com/ffii/fpsms/modules/purchaseOrder/web/model/SavePurchaseOrderLineResponse.kt
  36. +1
    -0
      src/main/java/com/ffii/fpsms/modules/purchaseOrder/web/model/SavePurchaseOrderRequest.kt
  37. +5
    -0
      src/main/resources/application.yml
  38. +19
    -0
      src/main/resources/db/changelog/changes/20250512_01_cyril/01_create_currency_table.sql
  39. +28
    -0
      src/main/resources/db/changelog/changes/20250512_01_cyril/02_create_item_uom_table.sql
  40. +8
    -0
      src/main/resources/db/changelog/changes/20250512_01_cyril/03_update_purchase_order_line.sql
  41. +6
    -0
      src/main/resources/db/changelog/changes/20250512_01_cyril/04_update_purchase_order.sql

+ 2
- 3
src/main/java/com/ffii/fpsms/api/service/ApiCallerService.kt Vedi File

@@ -22,13 +22,12 @@ import org.springframework.web.reactive.function.client.awaitBody

@Service
open class ApiCallerService(
@Value("\${m18.config.base-url}") private val baseUrl: String,
open val m18Config: M18Config
val m18Config: M18Config
) {
val logger: Logger = LoggerFactory.getLogger(JwtTokenUtil::class.java)

val webClient: WebClient = WebClient.builder()
.baseUrl(baseUrl)
.baseUrl(m18Config.BASE_URL)
.defaultHeaders { headers ->
headers.set(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
// headers.set(HttpHeaders.AUTHORIZATION, "Bearer ${m18Config.ACCESS_TOKEN}")


+ 41
- 28
src/main/java/com/ffii/fpsms/m18/M18Config.kt Vedi File

@@ -10,48 +10,61 @@ import org.springframework.context.annotation.Configuration
open class M18Config {

// Account
@Value("\${m18.config.grant-type}")
lateinit var GRANT_TYPE: String;
// @Value("\${m18.config.grant-type}")
var GRANT_TYPE: String = "password";

@Value("\${m18.config.client-id}")
lateinit var CLIENT_ID: String;
// @Value("\${m18.config.client-id}")
var CLIENT_ID: String = "M2Y1OGYxMmQtZDRiOS00OTA4LTgyNTktZDRkNzEzNWVkMzRm";

@Value("\${m18.config.client-secret}")
lateinit var CLIENT_SECRET: String;
// @Value("\${m18.config.client-secret}")
var CLIENT_SECRET: String = "M2Y2YjQzYzQtZTc2Mi00OTFhLTkwYmItYmJhMzFjZjEyYmY5";

@Value("\${m18.config.username}")
lateinit var USERNAME: String;
// @Value("\${m18.config.username}")
var USERNAME: String = "testingMTMS";

@Value("\${m18.config.password}")
lateinit var PASSWORD: String;
// @Value("\${m18.config.password}")
var PASSWORD: String = "db25f2fc14cd2d2b1e7af307241f548fb03c312a";

var BASE_URL: String = "http://16.162.251.126/jsf/rfws"

// Supplier
// @Value("\${m18.config.supplier-not.material-po}")
var MATERIAL_PO_SUPPLIER_NOT: List<String> = listOf("P06", "P07");

// @Value("\${m18.config.supplier.shop-po}")
var SHOP_PO_SUPPLIER: List<String> = listOf("P06", "P07");

// @Value("\${m18.config.supplier.oem-po}")
var OEM_PO_SUPPLIER: List<String> = listOf("T62");

// Series
@Value("\${m18.config.seriesId.pp}")
var SERIESID_PP: Long? = null;
// @Value("\${m18.config.seriesId.pp}")
var SERIESID_PP: Long = 26;

// @Value("\${m18.config.seriesId.pf}")
var SERIESID_PF: Long = 33;

@Value("\${m18.config.seriesId.pf}")
var SERIESID_PF: Long? = null;
// @Value("\${m18.config.seriesId.sc}")
var SERIESID_SC: Long = 27;

@Value("\${m18.config.seriesId.sc}")
var SERIESID_SC: Long? = null;
// @Value("\${m18.config.seriesId.se}")
var SERIESID_SE: Long = 28;

@Value("\${m18.config.seriesId.se}")
var SERIESID_SE: Long? = null;
// @Value("\${m18.config.seriesId.sf}")
var SERIESID_SF: Long = 70;

@Value("\${m18.config.seriesId.sf}")
var SERIESID_SF: Long? = null;
// @Value("\${m18.config.seriesId.sr}")
var SERIESID_SR: Long = 29;

@Value("\${m18.config.seriesId.sr}")
var SERIESID_SR: Long? = null;
// BE
@Value("\${m18.config.beId.pp}")
var BEID_PP: Long? = null;
// @Value("\${m18.config.beId.pp}")
var BEID_PP: Long = 27;

@Value("\${m18.config.beId.pf}")
var BEID_PF: Long? = null;
// @Value("\${m18.config.beId.pf}")
var BEID_PF: Long = 1;

@Value("\${m18.config.beId.toa}")
var BEID_TOA: Long? = null;
// @Value("\${m18.config.beId.toa}")
var BEID_TOA: Long = 29;

// Fetch
var ACCESS_TOKEN: String? = null;


+ 13
- 31
src/main/java/com/ffii/fpsms/m18/model/M18MasterDataRequest.kt Vedi File

@@ -1,40 +1,22 @@
package com.ffii.fpsms.m18.model

/** Product / Material Request */
data class M18ItemRequest (
val id: Long,
val params: String? = null,
)

/** Product / Material List Request */
data class M18ItemListRequest (
val stSearch: String = "pro",
val params: String? = null,
val conds: String? = null,
)

/** Vendor Request */
data class M18VendorRequest (
val id: Long,
val params: String? = null,
)
/** StSearch Type */
enum class StSearchType(val value: String) {
PRODUCT("pro"),
VENDOR("ven"),
CUSTOMER("cus"),
UNIT("unit"),
CURRENCY("cur"),
}

/** Vendor List Request */
data class M18VendorListRequest (
val stSearch: String = "ven",
/** M18 Common Master Data Request */
data class M18CommonListRequest (
val stSearch: String? = null,
val params: String? = null,
val conds: String? = null,
)

/** Customer Request */
data class M18CustomerRequest (
data class M18CommonLineRequest (
val id: Long,
val params: String? = null,
)

/** Customer List Request */
data class M18CustomerListRequest (
val stSearch: String = "cus",
val params: String? = null,
val conds: String? = null,
)
)

+ 83
- 8
src/main/java/com/ffii/fpsms/m18/model/M18MasterDataResponse.kt Vedi File

@@ -10,16 +10,17 @@ data class M18ErrorMessages (
)

/** Product / Material Response */
data class M18ItemResponse (
val data: M18ItemData?,
data class M18ProductResponse (
val data: M18ProductData?,
val messages: List<M18ErrorMessages>?
)

data class M18ItemData (
val pro: List<M18ItemPro>?
data class M18ProductData (
val pro: List<M18ProductPro>?,
val price: List<M18ProductPrice>?
)

data class M18ItemPro (
data class M18ProductPro (
val id: Long,
val code: String,
val desc: String,
@@ -28,12 +29,23 @@ data class M18ItemPro (
val lastModifyDate: Long,
)

data class M18ProductPrice (
val id: Long,
val hId: Long,
val unitId: Long,
val basicUnit: Boolean,
val saleUnit: Boolean,
val stkUnit: Boolean,
val purUnit: Boolean,
val pickUnit: Boolean,
)

/** Product / Material List Response */
data class M18ItemListResponse (
val values: List<M18ItemListValue>?,
data class M18ProductListResponse (
val values: List<M18ProductListValue>?,
)

data class M18ItemListValue (
data class M18ProductListValue (
val id: Long,
val lastModifyDate: String?,
)
@@ -72,4 +84,67 @@ data class M18VendorListResponse (
data class M18VendorListValue (
val id: Long,
val lastModifyDate: String?,
)

/** Unit List Response */
data class M18UnitListResponse (
val values: List<M18UnitListValue>?,
)

data class M18UnitListValue (
val id: Long,
val lastModifyDate: String?,
val code: String,
val udfudesc: String,
)

/** Unit Response */
data class M18UnitResponse (
val data: List<M18UnitData>
)

data class M18UnitData (
val unit: List<M18UnitUnit>
)

data class M18UnitUnit (
val id: Long,
val expiredDate: Long,
val lastModifyDate: Long,
val code: String,
val udfShortDesc: String,
val udfudesc: String,
val status: String,
)

/** Currency List Response */
data class M18CurrencyListResponse (
val values: List<M18CurrencyListValue>?,
)

data class M18CurrencyListValue (
val id: Long,
val lastModifyDate: String?,
val code: String,
val sym: String,
val curDesc: String,
)

/** Currency Response */
data class M18CurrencyResponse (
val data: List<M18CurrencyData>
)

data class M18CurrencyData (
val cur: List<M18CurrencyCur>
)

data class M18CurrencyCur (
val id: Long,
val expiredDate: Long,
val lastModifyDate: Long,
val code: String,
val sym: String,
val desc: String,
val status: String,
)

+ 17
- 4
src/main/java/com/ffii/fpsms/m18/model/M18PurchaseOrderResponse.kt Vedi File

@@ -1,5 +1,6 @@
package com.ffii.fpsms.m18.model

import com.ffii.fpsms.modules.purchaseOrder.enums.PurchaseOrderType
import java.math.BigDecimal
import java.time.Instant
import java.time.LocalDateTime
@@ -10,7 +11,7 @@ data class M18PurchaseOrderResponse (
)

data class M18PurchaseOrderData (
val mainPo: List<M18PurchaseOrderMainPo>,
val mainpo: List<M18PurchaseOrderMainPo>,
val pot: List<M18PurchaseOrderPot>
)

@@ -27,23 +28,35 @@ data class M18PurchaseOrderMainPo (
)

data class M18PurchaseOrderPot (
// Purchase Order Line ID
val id: Long,
// Purchase Order ID
val hId: Long,
// product ID
val proId: Long,
val code: String,
val desc: String,
val bDesc: String,
val unitId: Long,
val seriesId: Long,
// val seriesId: Long?,
val qty: BigDecimal,
val amt: BigDecimal,
val curId: Long,
)

/** Purchase Order List Response */
data class M18PurchaseOrderListResponseWithType (
var valuesWithType: MutableList<Pair<PurchaseOrderType, List<M18PurchaseOrderListValue>?>>
)

data class M18PurchaseOrderListResponse (
val values: List<M18PurchaseOrderListValue>
var values: List<M18PurchaseOrderListValue>
)

data class M18PurchaseOrderListValue (
val id: Long,
val code: String,
val lastModifyDate: String,

// Defined by FP-MTMS
val type: PurchaseOrderType?,
)

+ 289
- 114
src/main/java/com/ffii/fpsms/m18/service/M18MasterDataService.kt Vedi File

@@ -5,16 +5,15 @@ 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.modules.master.entity.UomConversion
import com.ffii.fpsms.modules.master.enums.ShopType
import com.ffii.fpsms.modules.master.service.ItemsService
import com.ffii.fpsms.modules.master.service.ShopService
import com.ffii.fpsms.modules.master.web.models.ItemType
import com.ffii.fpsms.modules.master.web.models.NewItemRequest
import com.ffii.fpsms.modules.master.web.models.SaveShopRequest
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.time.LocalDate
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter

@Service
@@ -23,87 +22,129 @@ open class M18MasterDataService(
val apiCallerService: ApiCallerService,
val itemsService: ItemsService,
val shopService: ShopService,
val uomConversionService: UomConversionService,
val currencyService: CurrencyService,
val itemUomService: ItemUomService,
) {

val commonUtils = CommonUtils()
val logger: Logger = LoggerFactory.getLogger(JwtTokenUtil::class.java)
val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")

// Everyday update the master data
// 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 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 commonConds =seriesIdConds + beIdConds
// "(beId=equal=${m18Config.BEID_PF}=or=beId=equal=${m18Config.BEID_PP}=or=beId=equal=${m18Config.BEID_TOA})"
val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")

// M18 API
val M18_LOAD_ITEM_API = "/root/api/read/pro"
val M18_FETCH_ITEM_LIST_API = "/search/search"
val M18_LOAD_VENDOR_API = "/root/api/read/ven"
val M18_FETCH_VENDOR_LIST_API = "/search/search"

// --------------------------------------------- Item --------------------------------------------- ///
open fun getItems(): M18ItemListResponse? {
// seems no beId
val itemsParams = M18ItemListRequest(
params = null,
conds=seriesIdConds
// conds=commonConds
// conds = "lastModifyDate=largerThan=$lastModifyDate"
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}"

// --------------------------------------------- Common Function --------------------------------------------- ///
private inline fun <reified T : Any> getList(
stSearch: String?,
params: String? = null,
conds: String? = null,
): T? {
val request = M18CommonListRequest(
stSearch = stSearch,
params = params,
conds = conds
)

val items = apiCallerService.get<M18ItemListResponse, M18ItemListRequest>(
M18_FETCH_ITEM_LIST_API,
itemsParams
val response = apiCallerService.get<T, M18CommonListRequest>(
M18_COMMON_FETCH_LIST_API,
request
).block()

return items
return response
}

open fun getItem(id: Long): M18ItemResponse? {
logger.info("M18 Item ID: $id")

val itemParams = M18ItemRequest(
private inline fun <reified T : Any> getLine(
id: Long,
params: String?,
api: String,
): T? {
val request = M18CommonLineRequest(
id = id,
params = null,
params = params,
)

val item = apiCallerService.get<M18ItemResponse, M18ItemRequest>(
M18_LOAD_ITEM_API,
itemParams
val response = apiCallerService.get<T, M18CommonLineRequest>(
api,
request
).block()

return item
return response
}

// --------------------------------------------- Product --------------------------------------------- ///
open fun getProducts(): M18ProductListResponse? {
// seems no beId
return getList<M18ProductListResponse>(
stSearch = StSearchType.PRODUCT.value,
params = null,
conds = seriesIdConds
)
// 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 saveItems() {
open fun getProduct(id: Long): M18ProductResponse? {
logger.info("M18 Product ID: $id")
return getLine<M18ProductResponse>(
id = id,
params = null,
api = M18_LOAD_PRODUCT_API
)
}

val items = getItems()
val exampleItems = listOf<Long>(10946L, 3825L)
open fun saveProducts() {
logger.info("--------------------------------------------Start - Saving M18 Products / Materials--------------------------------------------")
val items = getProducts()
val exampleProducts = listOf<Long>(10946L, 3825L)

val successList = mutableListOf<Long>()
val failList = mutableListOf<Long>()

if (items?.values != null) {
items.values.forEach { item ->
// if (item.id in exampleItems) {
val values = items?.values?.sortedBy { it.id }
if (values != null) {
values.forEach { item ->
// if (item.id in exampleProducts) {
try {
val itemDetail = getItem(item.id)
if (itemDetail != null && itemDetail.data?.pro != null) {
val pro = itemDetail.data.pro[0]
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)
val saveItemRequest = NewItemRequest(
code = pro.code,
name = pro.desc,
// type = if (pro.seriesId == m18Config.SERIESID_PF) ItemType.MATERIAL
// type = if (pro.seriesId == m18Config.SERIESID_PF) ProductType.MATERIAL
// else ItemType.PRODUCT,
type = ItemType.MATERIAL,
id = null,
id = existingItem?.id,
description = pro.desc,
remarks = null,
shelfLife = null,
@@ -113,108 +154,95 @@ open class M18MasterDataService(
m18LastModifyDate = commonUtils.InstantToLocalDateTime(pro.lastModifyDate)
)

itemsService.saveItem(saveItemRequest)
val savedItem = itemsService.saveItem(saveItemRequest)
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 m18ItemUomIds = price?.map { it.id } ?: listOf()

// Delete the item uom
logger.info("Deleting item uom...")
existingItemUoms?.filter { deleteItemUom ->
m18ItemUomIds.any { it != deleteItemUom.m18Id }
}?.mapNotNull { it.id }?.let { itemUomService.deleteItemUoms(it) }

// Update the item uom
logger.info("Updating item uom...")
price?.forEach {
val itemUomRequest = ItemUomRequest(
m18UomId = it.unitId,
itemId = savedItem.id,
baseUnit = it.basicUnit,
stockUnit = it.stkUnit,
pickingUnit = it.pickUnit,
salesUnit = it.saleUnit,
purchaseUnit = it.purUnit,
price = null,
currencyId = null,
m18Id = it.id,
m18LastModifyDate = commonUtils.InstantToLocalDateTime(pro.lastModifyDate)
)

logger.info("saved item id: ${savedItem.id}")
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")
logger.error("Fail Count ${failList.size}: Item ID - ${item.id} Not Found")
}
} catch (e: Exception) {
failList.add(item.id)
logger.error("M18 Item Data: ${e.message}")
logger.error("Fail Count ${failList.size}: Item ID ${item.id} Not Found")
logger.error("Exception")
logger.error("Fail Message: ${e.message}")
logger.error("Fail Count ${failList.size}: Item ID - ${item.id}")
}
// val itemParams = M18ItemRequest(
// id = item.id,
// params = null
// )

// apiCallerService.get<M18ItemResponse, M18ItemRequest>(
// M18_LOAD_ITEM_API,
// itemParams
// ).subscribe(
// { response ->
// val pro = response.data.pro[0]
//// when (pro.seriesId) {
//// m18Config.SERIESID_PF, m18Config.SERIESID_PP -> {
// val saveItemRequest = NewItemRequest(
// code = pro.code,
// name = pro.desc,
// type = if (pro.seriesId == m18Config.SERIESID_PF) ItemType.MATERIAL
// else ItemType.PRODUCT,
// id = null,
// description = null,
// remarks = null,
// shelfLife = null,
// countryOfOrigin = null,
// maxQty = null,
// m18Id = pro.id
// )
//
// itemsService.saveItem(saveItemRequest)
//// }
//// }
//
// logger.info("Count ${++count}: ${pro.id} | ${pro.code} | ${pro.desc}")
// },
// { error -> logger.error("WebClient Error: ${error.message}") }
// )
// }
}
} else {
logger.error("Items List is null. May occur errors.")
}

logger.info("Total Success (${successList.size}): $successList")
logger.info("Total Success (${successList.size})")

if (failList.size > 0) {
logger.error("Total Fail (${failList.size}): $failList")
}

logger.info("--------------------------------------------End - Saving M18 Products / Materials--------------------------------------------")
}

// --------------------------------------------- Vendor --------------------------------------------- ///
open fun getVendors(): M18VendorListResponse? {
val vendorsParams = M18VendorListRequest(
return getList<M18VendorListResponse>(
stSearch = StSearchType.VENDOR.value,
params = null,
conds = beIdConds
// conds = "lastModifyDate=largerThan=$lastModifyDate"
)

val vendors = apiCallerService.get<M18VendorListResponse, M18VendorListRequest>(
M18_FETCH_VENDOR_LIST_API,
vendorsParams
).block()

return vendors
}

open fun getVendor(id: Long): M18VendorResponse? {
logger.info("M18 Vendor ID: $id")

val vendorParams = M18VendorRequest(
return getLine<M18VendorResponse>(
id = id,
params = null,
api = M18_LOAD_VENDOR_API
)

val vendor = apiCallerService.get<M18VendorResponse, M18VendorRequest>(
M18_LOAD_VENDOR_API,
vendorParams
).block()

return vendor
}

open fun saveVendors() {
logger.info("--------------------------------------------Start - Saving M18 Vendors--------------------------------------------")
val vendors = getVendors()
val exampleVendors = listOf<Long>(191L)

val successList = mutableListOf<Long>()
val failList = mutableListOf<Long>()
val values = vendors?.values?.sortedBy { it.id }

if (vendors?.values != null) {
vendors.values.forEach { vendor ->
if (values != null) {
values.forEach { vendor ->
// if (vendor.id in exampleVendors) {
try {
val vendorDetail = getVendor(vendor.id)
@@ -246,23 +274,170 @@ open class M18MasterDataService(
} 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")
logger.error("Fail Count ${failList.size}: Vendor ID - ${vendor.id} Not Found")
}
} catch (e: Exception) {
failList.add(vendor.id)
logger.error("M18 Vendor Data: ${e.message}")
logger.error("Fail Count ${failList.size}: Vendor ID ${vendor.id} Not Found")
logger.error("Exception")
logger.error("Fail Message: ${e.message}")
logger.error("Fail Count ${failList.size}: Vendor ID - ${vendor.id}")
}
// }
}
} else {
logger.error("Items List is null. May occur errors.")
logger.error("Vendor List is null. May occur errors.")
}

logger.info("Total Success (${successList.size}): $successList")
logger.info("Total Success (${successList.size})")

if (failList.size > 0) {
logger.error("Total Fail (${failList.size}): $failList")
}
logger.info("--------------------------------------------End - Saving M18 Vendors--------------------------------------------")
}

// --------------------------------------------- Unit --------------------------------------------- ///
open fun getUnits(): M18UnitListResponse? {
// seems no beId
return getList<M18UnitListResponse>(
stSearch = StSearchType.UNIT.value,
params = null,
conds = null
)
}

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() {
logger.info("--------------------------------------------Start - Saving M18 Units--------------------------------------------")
val units = getUnits()

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 { unit ->
try {
val tempObject = UomConversionService.BomObject().apply {
code = unit.code
udfudesc = unit.udfudesc
lastModifyDate = unit.lastModifyDate
id = unit.id
}
finalUnitList += uomConversionService.transformItem(tempObject)
successTransformList += unit.id
logger.info("Transform Success (M18): ${unit.id}")
} catch (e: Exception) {
failTransformList.add(unit.id)
logger.error("Transform Exception")
logger.error("Transform Fail Message: ${e.message}")
logger.error("Transform Fail Count ${failTransformList.size}: Unit ID - ${unit.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--------------------------------------------")
}

// --------------------------------------------- Currency --------------------------------------------- ///
open fun getCurrencies(): M18CurrencyListResponse? {
return getList<M18CurrencyListResponse>(
stSearch = StSearchType.CURRENCY.value,
params = null,
conds = null
)
}

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() {
logger.info("--------------------------------------------Start - Saving M18 Currencies--------------------------------------------")
val currencies = getCurrencies()

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 = CurrencyRequest(
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--------------------------------------------")
}
}

+ 261
- 152
src/main/java/com/ffii/fpsms/m18/service/M18PurchaseOrderService.kt Vedi File

@@ -9,6 +9,7 @@ import com.ffii.fpsms.modules.master.service.ItemsService
import com.ffii.fpsms.modules.master.service.ShopService
import com.ffii.fpsms.modules.purchaseOrder.enums.PurchaseOrderLineStatus
import com.ffii.fpsms.modules.purchaseOrder.enums.PurchaseOrderStatus
import com.ffii.fpsms.modules.purchaseOrder.enums.PurchaseOrderType
import com.ffii.fpsms.modules.purchaseOrder.service.PurchaseOrderLineService
import com.ffii.fpsms.modules.purchaseOrder.service.PurchaseOrderService
import com.ffii.fpsms.modules.purchaseOrder.web.model.SavePurchaseOrderLineRequest
@@ -16,7 +17,6 @@ import com.ffii.fpsms.modules.purchaseOrder.web.model.SavePurchaseOrderRequest
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.stereotype.Service
import java.time.LocalDate
import java.time.LocalDateTime
import kotlin.reflect.full.memberProperties

@@ -30,28 +30,81 @@ open class M18PurchaseOrderService(
val itemsService: ItemsService,
val shopService: ShopService,
) {
val dateTimeConverter = CommonUtils()
val commonUtils = CommonUtils()
val logger: Logger = LoggerFactory.getLogger(JwtTokenUtil::class.java)

val lastModifyDate = LocalDate.now().minusDays(1)
val commonConds="(beId=equal=${m18Config.BEID_PF}=or=beId=equal=${m18Config.BEID_PP}=or=beId=equal=${m18Config.BEID_TOA})=and=lastModifyDate=largerOrEqual=${lastModifyDate}"
val lastModifyDate = LocalDateTime.now().minusMinutes(30)
// val commonConds =
// "(beId=equal=${m18Config.BEID_PF}=or=beId=equal=${m18Config.BEID_PP}=or=beId=equal=${m18Config.BEID_TOA})=and=lastModifyDate=largerOrEqual=${lastModifyDate}"

// M18 API
val M18_LOAD_PURCHASE_ORDER_API = "/root/api/read/po"
val M18_FETCH_PURCHASE_ORDER_LIST_API = "/search/search"

open fun getPurchaseOrders(): M18PurchaseOrderListResponse? {
val purchaseOrdersParams = M18PurchaseOrderListRequest(
// Include material po, shop po, oem po
open fun getPurchaseOrdersWithType(): M18PurchaseOrderListResponseWithType? {
val purchaseOrders = M18PurchaseOrderListResponseWithType(mutableListOf())

// Material PO
val materialPoBuyers =
commonUtils.ListToString(listOf(m18Config.BEID_PP, m18Config.BEID_PF), "beId=equal=", "=or=")
val materialPoSupplierNot = "venId=unequal=${m18Config.MATERIAL_PO_SUPPLIER_NOT}"
val materialPoConds = "${materialPoBuyers}=and=${materialPoSupplierNot}"
val materialPoParams = M18PurchaseOrderListRequest(
params = null,
conds = commonConds
conds = materialPoConds
)
var purchaseOrders: M18PurchaseOrderListResponse? = null

try {
purchaseOrders = apiCallerService.get<M18PurchaseOrderListResponse, M18PurchaseOrderListRequest>(
M18_FETCH_PURCHASE_ORDER_LIST_API,
purchaseOrdersParams
).block()
purchaseOrders.valuesWithType += Pair(
PurchaseOrderType.MATERIAL,
apiCallerService.get<M18PurchaseOrderListResponse, M18PurchaseOrderListRequest>(
M18_FETCH_PURCHASE_ORDER_LIST_API,
materialPoParams
).block()?.values ?: mutableListOf()
)
} catch (e: Exception) {
logger.error("Error on Function - ${e.stackTrace}")
logger.error(e.message)
}

// Shop PO
val shopPoBuyers = commonUtils.ListToString(listOf(m18Config.BEID_TOA), "beId=equal=", "=or=")
val shopPoSupplier = "venId=equal=${m18Config.SHOP_PO_SUPPLIER}"
val shopPoConds = "${shopPoBuyers}=and=${shopPoSupplier}"
val shopPoParams = M18PurchaseOrderListRequest(
params = null,
conds = shopPoConds
)

try {
purchaseOrders.valuesWithType += Pair(
PurchaseOrderType.SHOP, apiCallerService.get<M18PurchaseOrderListResponse, M18PurchaseOrderListRequest>(
M18_FETCH_PURCHASE_ORDER_LIST_API,
shopPoParams
).block()?.values ?: mutableListOf()
)
} catch (e: Exception) {
logger.error("Error on Function - ${e.stackTrace}")
logger.error(e.message)
}

// OEM PO
val oemPoBuyers = commonUtils.ListToString(listOf(m18Config.BEID_PF, m18Config.BEID_PP), "beId=equal=", "=or=")
val oemPoSupplier = "venId=equal=${m18Config.OEM_PO_SUPPLIER}"
val oemPoConds = "${oemPoBuyers}=and=${oemPoSupplier}"
val oemPoParams = M18PurchaseOrderListRequest(
params = null,
conds = oemPoConds
)

try {
purchaseOrders.valuesWithType += Pair(
PurchaseOrderType.OEM, apiCallerService.get<M18PurchaseOrderListResponse, M18PurchaseOrderListRequest>(
M18_FETCH_PURCHASE_ORDER_LIST_API,
oemPoParams
).block()?.values
)
} catch (e: Exception) {
logger.error("Error on Function - ${e.stackTrace}")
logger.error(e.message)
@@ -81,7 +134,8 @@ open class M18PurchaseOrderService(
}

open fun savePurchaseOrders() {
val purchaseOrders = getPurchaseOrders()
logger.info("--------------------------------------------Start - Saving M18 Purchase Order--------------------------------------------")
val purchaseOrdersWithType = getPurchaseOrdersWithType()
val examplePurchaseOrders = listOf<Long>(4764034L)

val successList = mutableListOf<Long>()
@@ -89,180 +143,235 @@ open class M18PurchaseOrderService(
val failList = mutableListOf<Long>()
val failDetailList = mutableListOf<Long>()

if (purchaseOrders != null) {
val poRefType = "Purchase Order"
val poLineRefType = "Purchase Order Line"

if (purchaseOrdersWithType != null) {
// Loop for Purchase Orders (values)
purchaseOrders.values.forEach { purchaseOrder ->
val purchaseOrderDetail = getPurchaseOrder(purchaseOrder.id)

var purchaseOrderId: Long? = null //FP-MTMS

// Process for Purchase Order (mainPo)
// Assume only one PO in the PO (search by PO ID)
val mainPo = purchaseOrderDetail?.data?.mainPo?.get(0)
val pot = purchaseOrderDetail?.data?.pot

// purchase_order + m18_data_log table
if (mainPo != null) {
val poRefType = "Purchase Order"

// Find the latest m18 data log by m18 id & type
val latestM18DataLog = m18DataLogService.findLatestM18DataLog(purchaseOrder.id, poRefType)

// Save to m18_data_log table
val mainPoJson =
mainPo::class.memberProperties.associate { prop -> prop.name to prop.getter.call(mainPo) }
.toMutableMap()

val saveM18PurchaseOrderLogRequest = SaveM18DataLogRequest(
id = null,
refType = poRefType,
m18Id = purchaseOrder.id,
m18LastModifyDate = dateTimeConverter.InstantToLocalDateTime(mainPo.lastModifyDate),
dataLog = mainPoJson,
status = true
)

val saveM18PurchaseOrderLog = m18DataLogService.saveM18DataLog(saveM18PurchaseOrderLogRequest)

try {
// Find the purchase_order if exist
val existingPurchaseOrder = latestM18DataLog?.id?.let { purchaseOrderService.findPurchaseOrderByM18Id(it) }

// Save to purchase_order table
val supplierId = shopService.findByM18Id(mainPo.venId)?.id
val savePurchaseOrderRequest = SavePurchaseOrderRequest(
id = existingPurchaseOrder?.id,
code = mainPo.code,
supplierId = supplierId,
orderDate = dateTimeConverter.InstantToLocalDateTime(mainPo.tDate),
estimatedArrivalDate = dateTimeConverter.InstantToLocalDateTime(mainPo.dDate),
completeDate = null,
status = PurchaseOrderStatus.PENDING.value,
m18DataLogId = saveM18PurchaseOrderLog.id,
)

val savePurchaseOrderResponse = purchaseOrderService.savePurchaseOrder(savePurchaseOrderRequest)
purchaseOrderId = savePurchaseOrderResponse.id

successList.add(purchaseOrder.id)
} catch (e: Exception) {
failList.add(purchaseOrder.id)
logger.error("Error on Function - ${e.stackTrace} | Type: Purchase Order | M18 ID: ${purchaseOrder.id} | Different? ${mainPo.id}")
logger.error(e.message)

val errorSaveM18PurchaseOrderLogRequest = SaveM18DataLogRequest(
id = saveM18PurchaseOrderLogRequest.id,
refType = "Purchase Order",
m18Id = purchaseOrder.id,
m18LastModifyDate = dateTimeConverter.InstantToLocalDateTime(mainPo.lastModifyDate),
dataLog = mainPoJson,
status = false
)

m18DataLogService.saveM18DataLog(errorSaveM18PurchaseOrderLogRequest)
}
purchaseOrdersWithType.valuesWithType.forEach { purchaseOrderWithType ->
val type = purchaseOrderWithType.first
val purchaseOrders = purchaseOrderWithType.second

if (purchaseOrders != null) {
purchaseOrders.forEach { purchaseOrder ->
val purchaseOrderDetail = getPurchaseOrder(purchaseOrder.id)

// purchase_order_line + m18_data_log
if (pot != null) {
// Loop for Purchase Order Lines (pot)
pot.forEach { line ->
val poLineRefType = "Purchase Order Line"
var purchaseOrderId: Long? = null //FP-MTMS

// Process for Purchase Order (mainpo)
// Assume only one PO in the PO (search by PO ID)
val mainpo = purchaseOrderDetail?.data?.mainpo?.get(0)
val pot = purchaseOrderDetail?.data?.pot

// purchase_order + m18_data_log table
if (mainpo != null) {
// Find the latest m18 data log by m18 id & type
val latestM18DataLog = m18DataLogService.findLatestM18DataLog(line.id, poLineRefType)
logger.info("${poRefType}: Finding For Latest M18 Data Log...")
val latestPurchaseOrderLog =
m18DataLogService.findLatestM18DataLog(purchaseOrder.id, poRefType)
// logger.info("${poRefType}: Latest M18 Data Log ID: ${latestPurchaseOrderLog?.id}")

// Save to m18_data_log table
val lineJson =
line::class.memberProperties.associate { prop -> prop.name to prop.getter.call(line) }
logger.info("${poRefType}: Saving for M18 Data Log...")
val mainpoJson =
mainpo::class.memberProperties.associate { prop -> prop.name to prop.getter.call(mainpo) }
.toMutableMap()
val saveM18PurchaseOrderLineLogRequest = SaveM18DataLogRequest(

val saveM18PurchaseOrderLogRequest = SaveM18DataLogRequest(
id = null,
refType = poLineRefType,
m18Id = line.id,
m18LastModifyDate = dateTimeConverter.InstantToLocalDateTime(mainPo.lastModifyDate),
dataLog = lineJson,
refType = poRefType,
m18Id = purchaseOrder.id,
m18LastModifyDate = commonUtils.InstantToLocalDateTime(mainpo.lastModifyDate),
dataLog = mainpoJson,
status = true
)

val saveM18PurchaseOrderLineLog = m18DataLogService.saveM18DataLog(saveM18PurchaseOrderLineLogRequest)

val item = itemsService.findByM18Id(line.id)
logger.info("Item ID: ${item?.id}")
val saveM18PurchaseOrderLog =
m18DataLogService.saveM18DataLog(saveM18PurchaseOrderLogRequest)
// logger.info("${poRefType}: Saved M18 Data Log. ID: ${saveM18PurchaseOrderLog.id}")

try {
// Find the purchase_order_line if exist
val existingPurchaseOrderLine = latestM18DataLog?.id?.let { purchaseOrderLineService.findPurchaseOrderLineByM18Id(it) }

// Save to purchase_order_line table
val savePurchaseOrderLineRequest = SavePurchaseOrderLineRequest(
id = existingPurchaseOrderLine?.id,
itemId = item?.id,
uomId = null,
purchaseOrderId = purchaseOrderId,
qty = line.qty,
price = line.amt,
priceUnit = null,
status = existingPurchaseOrderLine?.status?.value ?: PurchaseOrderLineStatus.PENDING.value,
m18DataLogId = saveM18PurchaseOrderLineLog.id,
// Find the purchase_order if exist
logger.info("${poRefType}: Finding exising purchase order...")
val existingPurchaseOrder =
latestPurchaseOrderLog?.id?.let { purchaseOrderService.findPurchaseOrderByM18Id(it) }
logger.info("${poRefType}: Exising purchase order ID: ${existingPurchaseOrder?.id}")

// Save to purchase_order table
logger.info("${poRefType}: Saving purchase order...")
val supplierId = shopService.findByM18Id(mainpo.venId)?.id
val savePurchaseOrderRequest = SavePurchaseOrderRequest(
id = existingPurchaseOrder?.id,
code = mainpo.code,
supplierId = supplierId,
orderDate = commonUtils.InstantToLocalDateTime(mainpo.tDate),
estimatedArrivalDate = commonUtils.InstantToLocalDateTime(mainpo.dDate),
completeDate = null,
status = PurchaseOrderStatus.PENDING.value,
type = type.value,
m18DataLogId = saveM18PurchaseOrderLog.id,
)

purchaseOrderLineService.savePurchaseOrderLine(savePurchaseOrderLineRequest)
val savePurchaseOrderResponse =
purchaseOrderService.savePurchaseOrder(savePurchaseOrderRequest)
purchaseOrderId = savePurchaseOrderResponse.id

successList.add(purchaseOrder.id)
logger.info("${poRefType}: Saved purchase order. ID: ${savePurchaseOrderResponse.id} | M18 ${poRefType} ID: ${purchaseOrder.id}")

} catch (e: Exception) {
failDetailList.add(line.id)
logger.error("Error on Function - ${e.stackTrace} | Type: Purchase Order Line | M18 ID: ${line.id}")
failList.add(purchaseOrder.id)
logger.error("${poRefType}: Saving Failure!")
logger.error("Error on Function - ${e.stackTrace} | Type: ${poRefType} | M18 ID: ${purchaseOrder.id} | Different? ${mainpo.id}")
logger.error(e.message)

val errorSaveM18PurchaseOrderLineLogRequest = SaveM18DataLogRequest(
id = saveM18PurchaseOrderLineLog.id,
refType = "Purchase Order",
m18Id = line.id,
m18LastModifyDate = dateTimeConverter.InstantToLocalDateTime(mainPo.lastModifyDate),
dataLog = lineJson,
val errorSaveM18PurchaseOrderLogRequest = SaveM18DataLogRequest(
id = saveM18PurchaseOrderLogRequest.id,
refType = poRefType,
m18Id = purchaseOrder.id,
m18LastModifyDate = commonUtils.InstantToLocalDateTime(mainpo.lastModifyDate),
dataLog = mainpoJson,
status = false
)

m18DataLogService.saveM18DataLog(errorSaveM18PurchaseOrderLineLogRequest)
m18DataLogService.saveM18DataLog(errorSaveM18PurchaseOrderLogRequest)
logger.error("${poRefType}: M18 Data Log Updated! Please see the error. ID: ${saveM18PurchaseOrderLogRequest.id}")
}

// purchase_order_line + m18_data_log
if (pot != null) {
// Loop for Purchase Order Lines (pot)
pot.forEach { line ->

// // Find the latest m18 data log by m18 id & type
logger.info("${poLineRefType}: Finding For Latest M18 Data Log...")
val latestPurchaseOrderLineLog =
m18DataLogService.findLatestM18DataLog(line.id, poLineRefType)
// logger.info("${poLineRefType}: Latest M18 Data Log ID: ${latestPurchaseOrderLineLog?.id}")

// Save to m18_data_log table
logger.info("${poLineRefType}: Saving for M18 Data Log...")
val lineJson =
line::class.memberProperties.associate { prop ->
prop.name to prop.getter.call(
line
)
}
.toMutableMap()
val saveM18PurchaseOrderLineLogRequest = SaveM18DataLogRequest(
id = null,
refType = poLineRefType,
m18Id = line.id,
m18LastModifyDate = commonUtils.InstantToLocalDateTime(mainpo.lastModifyDate),
dataLog = lineJson,
status = true
)

val saveM18PurchaseOrderLineLog =
m18DataLogService.saveM18DataLog(saveM18PurchaseOrderLineLogRequest)

// logger.info("${poLineRefType}: Saved M18 Data Log. ID: ${saveM18PurchaseOrderLineLog.id}")
logger.info("${poLineRefType}: Finding item...")
val item = itemsService.findByM18Id(line.proId)
logger.info("${poLineRefType}: Item ID: ${item?.id} | M18 Item ID: ${line.proId}")

try {
// Find the purchase_order_line if exist
logger.info("${poLineRefType}: Finding exising purchase order line...")
val existingPurchaseOrderLine = latestPurchaseOrderLineLog?.id?.let {
purchaseOrderLineService.findPurchaseOrderLineByM18Id(it)
}
logger.info("${poLineRefType}: Exising purchase order line ID: ${existingPurchaseOrderLine?.id}")

// Save to purchase_order_line table
logger.info("${poLineRefType}: Saving purchase order line...")
val lineUom =
val savePurchaseOrderLineRequest = SavePurchaseOrderLineRequest(
id = existingPurchaseOrderLine?.id,
itemId = item?.id,
uomId = null,
purchaseOrderId = purchaseOrderId,
qty = line.qty,
price = line.amt,
m18CurrencyId = line.curId,
status = existingPurchaseOrderLine?.status?.value
?: PurchaseOrderLineStatus.PENDING.value,
m18DataLogId = saveM18PurchaseOrderLineLog.id,
)

val savePurchaseOrderLineResponse =
purchaseOrderLineService.savePurchaseOrderLine(savePurchaseOrderLineRequest)
logger.info("${poLineRefType}: Purchase order ID: ${purchaseOrderId} | M18 ID: ${purchaseOrder.id}")
logger.info("${poLineRefType}: Saved purchase order line. ID: ${savePurchaseOrderLineResponse.id} | M18 Line ID: ${line.id}")
} catch (e: Exception) {
failDetailList.add(line.id)
logger.error("${poLineRefType}: Saving Failure!")
logger.error("Error on Function - ${e.stackTrace} | Type: ${poLineRefType} | M18 ID: ${line.id}")
logger.error(e.message)

val errorSaveM18PurchaseOrderLineLogRequest = SaveM18DataLogRequest(
id = saveM18PurchaseOrderLineLog.id,
refType = "${poRefType}",
m18Id = line.id,
m18LastModifyDate = commonUtils.InstantToLocalDateTime(mainpo.lastModifyDate),
dataLog = lineJson,
status = false
)

m18DataLogService.saveM18DataLog(errorSaveM18PurchaseOrderLineLogRequest)
logger.error("${poLineRefType}: M18 Data Log Updated! Please see the error. ID: ${saveM18PurchaseOrderLineLog.id}")
}
}
} else {
// pot
logger.error("${poLineRefType}: Saving Failure!")
val saveM18PurchaseOrderLineLogRequest = SaveM18DataLogRequest(
id = null,
refType = "${poLineRefType}",
m18Id = purchaseOrder.id,
m18LastModifyDate = commonUtils.InstantToLocalDateTime(mainpo.lastModifyDate),
dataLog = mutableMapOf(Pair("Error Message", "${poLineRefType} is null")),
status = false
)

val errorLog = m18DataLogService.saveM18DataLog(saveM18PurchaseOrderLineLogRequest)
logger.error("${poLineRefType}: M18 Data Log Updated! Please see the error. ID: ${errorLog.id}")
}
} else {
// mainpo
logger.error("${poRefType}: Saving Failure!")
val saveM18DataLogRequest = SaveM18DataLogRequest(
id = null,
refType = "${poRefType}",
m18Id = purchaseOrder.id,
// m18LastModifyDate = if(mainpo?.lastModifyDate != null) commonUtils.InstantToLocalDateTime(mainpo.lastModifyDate) else LocalDateTime.now(),
m18LastModifyDate = LocalDateTime.now(),
dataLog = mutableMapOf(Pair("Error Message", "${poRefType} is null")),
status = false
)

val errorLog = m18DataLogService.saveM18DataLog(saveM18DataLogRequest)
logger.error("${poLineRefType}: M18 Data Log Updated! Please see the error. ID: ${errorLog.id}")
}
} else {
val saveM18PurchaseOrderLineLogRequest = SaveM18DataLogRequest(
id = null,
refType = "Purchase Order Line",
m18Id = purchaseOrder.id,
m18LastModifyDate = dateTimeConverter.InstantToLocalDateTime(mainPo.lastModifyDate),
dataLog = mutableMapOf(Pair("Error Message", "Purchase Order Line is null")),
status = false
)

m18DataLogService.saveM18DataLog(saveM18PurchaseOrderLineLogRequest)
}
} else {
val saveM18DataLogRequest = SaveM18DataLogRequest(
id = null,
refType = "Purchase Order",
m18Id = purchaseOrder.id,
// m18LastModifyDate = if(mainPo?.lastModifyDate != null) dateTimeConverter.InstantToLocalDateTime(mainPo.lastModifyDate) else LocalDateTime.now(),
m18LastModifyDate = LocalDateTime.now(),
dataLog = mutableMapOf(Pair("Error Message", "Purchase Order is null")),
status = false
)

m18DataLogService.saveM18DataLog(saveM18DataLogRequest)
logger.error("${poRefType} List is null. May occur errors.")
}
}
} else {
logger.error("Purchase Order List is null. May occur errors.")
logger.error("${poRefType} List is null. May occur errors.")
}

// End of save. Check result
logger.info("Total Success (Purchase Order) (${successList.size}): $successList")
logger.info("Total Success (${poRefType}) (${successList.size}): $successList")
if (failList.size > 0) {
logger.error("Total Fail (Purchase Order) (${failList.size}): $failList")
logger.error("Total Fail (${poRefType}) (${failList.size}): $failList")
}

logger.info("Total Success (Purchase Order Line) (${successDetailList.size}): $successDetailList")
logger.info("Total Success (${poLineRefType}) (${successDetailList.size}): $successDetailList")
if (failDetailList.size > 0) {
logger.error("Total Fail (Purchase Order Line) (${failDetailList.size}): $failDetailList")
logger.error("Total Fail (${poLineRefType}) (${failDetailList.size}): $failDetailList")
}
logger.info("--------------------------------------------End - Saving M18 Purchase Order--------------------------------------------")
}
}

+ 1
- 1
src/main/java/com/ffii/fpsms/m18/service/M18TokenService.kt Vedi File

@@ -15,7 +15,7 @@ open class M18TokenService(
private val m18Config: M18Config
) {

@Bean
// @Bean
fun run() {
// val params: MutableMap<String, String> = mutableMapOf(
// "grant_type" to m18Config.GRANT_TYPE,


+ 2
- 2
src/main/java/com/ffii/fpsms/m18/utils/CommonUtils.kt Vedi File

@@ -7,8 +7,8 @@ import java.time.ZoneId
open class CommonUtils() {
open fun InstantToLocalDateTime(timestamp: Long):LocalDateTime {
val localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), ZoneId.of("Asia/Hong_Kong"))
println("ZoneId: ${ZoneId.systemDefault()}")
println("ZoneId: ${ZoneId.of("Asia/Hong_Kong")}")
// println("ZoneId: ${ZoneId.systemDefault()}")
// println("ZoneId: ${ZoneId.of("Asia/Hong_Kong")}")
println("Timestamp: $timestamp")
println("Local Date Time: $localDateTime")
return localDateTime


+ 33
- 3
src/main/java/com/ffii/fpsms/m18/web/M18TestController.kt Vedi File

@@ -4,27 +4,45 @@ import com.ffii.core.utils.JwtTokenUtil
import com.ffii.fpsms.m18.M18Config
import com.ffii.fpsms.m18.service.M18MasterDataService
import com.ffii.fpsms.m18.service.M18PurchaseOrderService
import com.ffii.fpsms.modules.master.entity.Shop
import com.ffii.fpsms.modules.master.entity.ShopRepository
import com.ffii.fpsms.modules.master.entity.projections.SearchId
import com.ffii.fpsms.modules.master.enums.ShopType
import com.ffii.fpsms.modules.master.service.ShopService
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RestController


@RestController
@RequestMapping("/m18")
class M18TestController (
private val shopRepository: ShopRepository,
private val shopService: ShopService,
private val m18MasterDataService: M18MasterDataService,
private val m18PurchaseOrderService: M18PurchaseOrderService,
private val m18Config: M18Config,
) {
var logger: Logger = LoggerFactory.getLogger(JwtTokenUtil::class.java)

@GetMapping("/test1")
fun test1(): List<Long>? {
return shopService.findVendorIdsByCodeRegexp(listOf("P06", "P07"))
}

@GetMapping("/test2")
fun test2(): List<Long>? {
return shopRepository.findIdsByCodeRegexpAndTypeAndDeletedIsFalse("P06|P07", ShopType.SUPPLIER.value)
}

// --------------------------------------------- Master Data --------------------------------------------- ///
@GetMapping("/item")
fun m18Items() {
@GetMapping("/product")
fun m18Products() {
logger.info("Access token: ${m18Config.ACCESS_TOKEN}")
m18MasterDataService.saveItems()
m18MasterDataService.saveProducts()
}

@GetMapping("/vendor")
@@ -33,6 +51,18 @@ class M18TestController (
m18MasterDataService.saveVendors()
}

@GetMapping("/unit")
fun m18Unit() {
logger.info("Access token: ${m18Config.ACCESS_TOKEN}")
m18MasterDataService.saveUnits()
}

@GetMapping("/currency")
fun m18Currency() {
logger.info("Access token: ${m18Config.ACCESS_TOKEN}")
m18MasterDataService.saveCurrencies()
}

// --------------------------------------------- Purchase Order --------------------------------------------- ///
@GetMapping("/po")
fun m18PO() {


+ 34
- 0
src/main/java/com/ffii/fpsms/modules/master/entity/Currency.kt Vedi File

@@ -0,0 +1,34 @@
package com.ffii.fpsms.modules.master.entity

import com.ffii.core.entity.BaseEntity
import jakarta.persistence.Column
import jakarta.persistence.Entity
import jakarta.persistence.Table
import jakarta.validation.constraints.NotNull
import jakarta.validation.constraints.Size
import java.time.LocalDateTime

@Entity
@Table(name = "currency")
open class Currency : BaseEntity<Long>() {
@Size(max = 30)
@NotNull
@Column(name = "code", nullable = false, length = 30)
open var code: String? = null

@Size(max = 30)
@NotNull
@Column(name = "name", nullable = false, length = 30)
open var name: String? = null

@Size(max = 100)
@NotNull
@Column(name = "description", nullable = false, length = 100)
open var description: String? = null

@Column(name = "m18Id")
open var m18Id: Long? = null

@Column(name = "m18LastModifyDate")
open var m18LastModifyDate: LocalDateTime? = null
}

+ 11
- 0
src/main/java/com/ffii/fpsms/modules/master/entity/CurrencyRepository.kt Vedi File

@@ -0,0 +1,11 @@
package com.ffii.fpsms.modules.master.entity

import com.ffii.core.support.AbstractRepository
import org.springframework.stereotype.Repository

@Repository
interface CurrencyRepository: AbstractRepository<Currency, Long> {
fun findByIdAndDeletedIsFalse (id: Long): Currency?

fun findByM18IdAndDeletedIsFalse (m18Id: Long): Currency?
}

+ 55
- 0
src/main/java/com/ffii/fpsms/modules/master/entity/ItemUom.kt Vedi File

@@ -0,0 +1,55 @@
package com.ffii.fpsms.modules.master.entity

import com.ffii.core.entity.BaseEntity
import jakarta.persistence.*
import jakarta.validation.constraints.NotNull
import java.math.BigDecimal
import java.time.LocalDateTime

@Entity
@Table(name = "item_uom")
open class ItemUom : BaseEntity<Long>() {
@NotNull
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "uomId", nullable = false)
open var uom: UomConversion? = null

@NotNull
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "itemId", nullable = false)
open var item: Items? = null

@NotNull
@Column(name = "baseUnit", nullable = false)
open var baseUnit: Boolean? = null

@NotNull
@Column(name = "stockUnit", nullable = false)
open var stockUnit: Boolean? = null

@NotNull
@Column(name = "pickingUnit", nullable = false)
open var pickingUnit: Boolean? = null

@NotNull
@Column(name = "salesUnit", nullable = false)
open var salesUnit: Boolean? = null

@NotNull
@Column(name = "purchaseUnit", nullable = false)
open var purchaseUnit: Boolean? = null

@Column(name = "price", precision = 14, scale = 2)
open var price: BigDecimal? = null

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "currencyId")
open var currency: Currency? = null

@NotNull
@Column(name = "m18Id", nullable = false)
open var m18Id: Long? = null

@Column(name = "m18LastModifyDate")
open var m18LastModifyDate: LocalDateTime? = null
}

+ 18
- 0
src/main/java/com/ffii/fpsms/modules/master/entity/ItemUomRespository.kt Vedi File

@@ -0,0 +1,18 @@
package com.ffii.fpsms.modules.master.entity

import com.ffii.core.support.AbstractRepository
import org.springframework.stereotype.Repository
import java.io.Serializable

@Repository
interface ItemUomRespository : AbstractRepository<ItemUom, Long> {
fun findAllByItemIdAndDeletedIsFalse(itemId: Serializable): List<ItemUom>

fun findByIdAndDeletedIsFalse(id: Serializable): ItemUom?

fun findByM18IdAndDeletedIsFalse(m18Id: Serializable): ItemUom?

fun deleteAllByIdIn(id: List<Serializable>)

fun findByItemIdAndPurchaseUnitIsTrueAndDeletedIsFalse(itemId: Serializable): ItemUom?
}

+ 3
- 1
src/main/java/com/ffii/fpsms/modules/master/entity/Items.kt Vedi File

@@ -1,7 +1,6 @@
package com.ffii.fpsms.modules.master.entity

import com.ffii.core.entity.BaseEntity
import com.ffii.fpsms.modules.master.web.models.ItemType
import jakarta.persistence.*
import jakarta.validation.constraints.NotNull
import java.time.LocalDateTime
@@ -41,4 +40,7 @@ open class Items : BaseEntity<Long>() {

@Column(name = "m18LastModifyDate")
open var m18LastModifyDate: LocalDateTime? = null

@OneToMany(mappedBy = "item", cascade = [CascadeType.ALL], orphanRemoval = true)
open var itemUoms: MutableSet<ItemUom> = mutableSetOf()
}

+ 13
- 0
src/main/java/com/ffii/fpsms/modules/master/entity/ShopRepository.kt Vedi File

@@ -1,6 +1,9 @@
package com.ffii.fpsms.modules.master.entity

import com.ffii.core.support.AbstractRepository
import com.ffii.fpsms.modules.master.entity.projections.SearchId
import com.ffii.fpsms.modules.master.enums.ShopType
import org.springframework.data.jpa.repository.Query
import org.springframework.stereotype.Repository

@Repository
@@ -10,4 +13,14 @@ interface ShopRepository : AbstractRepository<Shop, Long> {
fun findByIdAndDeletedIsFalse(id: Long): Shop?

fun findByM18IdAndDeletedIsFalse(m18Id: Long): Shop?

@Query(
nativeQuery = true,
value = "select s.id from Shop s where s.code regexp ?1 and s.type = ?2 and s.deleted = false")
fun findIdsByCodeRegexpAndTypeAndDeletedIsFalse(codeRegexp: String, type: String): List<Long>?

@Query(
nativeQuery = true,
value = "select s.id from Shop s where s.code not regexp ?1 and s.type = ?2 and s.deleted = false")
fun findIdsByCodeNotRegexpAndTypeAndDeletedIsFalse(codeNotRegexp: String, type: String): List<Long>?
}

+ 3
- 0
src/main/java/com/ffii/fpsms/modules/master/entity/UomConversionRepository.kt Vedi File

@@ -8,5 +8,8 @@ import java.time.LocalDateTime
interface UomConversionRepository : AbstractRepository<UomConversion, Long> {
//fun importFromM18(): ArrayList<UomConversion>;
fun findByIdAndDeletedFalse(id: Long): UomConversion;

fun findByM18IdAndDeletedFalse(m18Id: Long): UomConversion?;

fun findByLastModifyDateAndM18IdAndDeletedFalse(lastModifyDate: LocalDateTime, m18Id: Long): UomConversion?;
}

+ 5
- 0
src/main/java/com/ffii/fpsms/modules/master/entity/projections/SearchId.kt Vedi File

@@ -0,0 +1,5 @@
package com.ffii.fpsms.modules.master.entity.projections

interface SearchId {
val id: Long
}

+ 33
- 0
src/main/java/com/ffii/fpsms/modules/master/service/CurrencyService.kt Vedi File

@@ -0,0 +1,33 @@
package com.ffii.fpsms.modules.master.service

import com.ffii.fpsms.modules.master.entity.Currency
import com.ffii.fpsms.modules.master.entity.CurrencyRepository
import com.ffii.fpsms.modules.master.web.models.CurrencyRequest
import org.springframework.stereotype.Service

@Service
open class CurrencyService(
val currencyRepository: CurrencyRepository,
) {
open fun findById(id: Long): Currency? {
return currencyRepository.findByIdAndDeletedIsFalse(id);
}

open fun findByM18Id(m18Id: Long): Currency? {
return currencyRepository.findByM18IdAndDeletedIsFalse(m18Id);
}

open fun saveCurrency(request: CurrencyRequest): Currency {
val currency = request.m18Id?.let { findByM18Id(it) } ?: request.id?.let { findById(it) } ?: Currency()

currency.apply {
code = request.code
name = request.name
description = request.description
m18Id = request.m18Id
m18LastModifyDate = request.m18LastModifyDate
}

return currencyRepository.saveAndFlush(currency)
}
}

+ 57
- 0
src/main/java/com/ffii/fpsms/modules/master/service/ItemUomService.kt Vedi File

@@ -0,0 +1,57 @@
package com.ffii.fpsms.modules.master.service

import com.ffii.fpsms.modules.master.entity.ItemUom
import com.ffii.fpsms.modules.master.entity.ItemUomRespository
import com.ffii.fpsms.modules.master.web.models.ItemUomRequest
import org.springframework.stereotype.Service
import kotlin.jvm.optionals.getOrNull

@Service
open class ItemUomService(
val itemsService: ItemsService,
val uomConversionService: UomConversionService,
val itemUomRespository: ItemUomRespository,
val currencyService: CurrencyService
) {
open fun findAllByItemsId(itemId: Long): List<ItemUom> {
return itemUomRespository.findAllByItemIdAndDeletedIsFalse(itemId);
}

open fun findById(id: Long): ItemUom? {
return itemUomRespository.findByIdAndDeletedIsFalse(id);
}

open fun findByM18Id(m18Id: Long): ItemUom? {
return itemUomRespository.findByM18IdAndDeletedIsFalse(m18Id);
}

// See if need to update the response
open fun saveItemUom(request: ItemUomRequest): ItemUom {
val itemUom = request.m18Id?.let { findByM18Id(it) } ?: request.id?.let { findById(it) } ?: ItemUom()
val item = request.itemId?.let { itemsService.find(it).getOrNull() }
val uom = request.m18UomId?.let { uomConversionService.findByM18Id(it) } ?: request.uomId?.let { uomConversionService.find(it).getOrNull() }
val currency = request.currencyId?.let { currencyService.findById(it) }

itemUom.apply {
this.uom = uom
this.item = item
this.currency = currency
baseUnit = request.baseUnit
stockUnit = request.stockUnit
pickingUnit = request.pickingUnit
salesUnit = request.salesUnit
purchaseUnit = request.purchaseUnit
price = request.price
m18Id = request.m18Id
m18LastModifyDate = request.m18LastModifyDate
}

val savedItemUom = itemUomRespository.saveAndFlush(itemUom)

return savedItemUom
}

open fun deleteItemUoms(deleteIds: List<Long>) {
itemUomRespository.deleteAllByIdIn(deleteIds)
}
}

+ 11
- 0
src/main/java/com/ffii/fpsms/modules/master/service/ShopService.kt Vedi File

@@ -2,6 +2,7 @@ package com.ffii.fpsms.modules.master.service

import com.ffii.fpsms.modules.master.entity.Shop
import com.ffii.fpsms.modules.master.entity.ShopRepository
import com.ffii.fpsms.modules.master.entity.projections.SearchId
import com.ffii.fpsms.modules.master.enums.ShopType
import com.ffii.fpsms.modules.master.web.models.SaveShopRequest
import com.ffii.fpsms.modules.master.web.models.SaveShopResponse
@@ -24,6 +25,16 @@ open class ShopService(
return shopRepository.findByM18IdAndDeletedIsFalse(m18Id)
}

open fun findVendorIdsByCodeRegexp(code: List<String>): List<Long>? {
val codeRegexp = code.joinToString("|")
return shopRepository.findIdsByCodeRegexpAndTypeAndDeletedIsFalse(codeRegexp, type = ShopType.SUPPLIER.value)
}

open fun findVendorIdsByCodeNotRegexp(code: List<String>): List<Long>? {
val codeRegexp = code.joinToString("|")
return shopRepository.findIdsByCodeNotRegexpAndTypeAndDeletedIsFalse(codeRegexp, type = ShopType.SUPPLIER.value)
}

open fun saveShop(request: SaveShopRequest): SaveShopResponse {
val shop = if (request.m18Id != null) {
findByM18Id(request.m18Id) ?: Shop()


+ 5
- 1
src/main/java/com/ffii/fpsms/modules/master/service/UomConversionService.kt Vedi File

@@ -196,6 +196,9 @@ open class UomConversionService(
}
}

open fun findByM18Id(m18Id: Long) : UomConversion? {
return uomConversionRepository.findByM18IdAndDeletedFalse(m18Id)
}
@Throws(IOException::class)
@Transactional
open fun saveUomConversion(newUomConversion: UomConversion): MessageResponse {
@@ -211,7 +214,8 @@ open class UomConversionService(
)
}

val uomConversion = if (newUomConversion.id != null && newUomConversion.id > 0)
val uomConversion = findByM18Id(newUomConversion.m18Id) ?:
if (newUomConversion.id != null && newUomConversion.id > 0)
uomConversionRepository.findByIdAndDeletedFalse(newUomConversion.id)
else UomConversion()



+ 1
- 42
src/main/java/com/ffii/fpsms/modules/master/web/QcItemController.kt Vedi File

@@ -39,47 +39,6 @@ class QcItemController(

@PostMapping("/save")
fun saveQcItem(@Valid @RequestBody request: SaveQcItemRequest): SaveQcItemResponse {

// val qcItemProperties = QcItem::class.members.filterIsInstance<KProperty<QcItem>>()
val errors = mutableMapOf<String, String>()
val id = request.id
val qcItem = if (id != null) qcItemRepository.findById(id).orElseThrow() else QcItem()

// check duplicated code
val duplicateQcItem = qcItemService.findQcItemByCode(request.code)
if (duplicateQcItem != null && duplicateQcItem.id != qcItem.id) {
errors["code"] = "Code is duplicated"
}

if (errors.isNotEmpty()) {
request.let {
SaveQcItemResponse(
id = it.id,
code = it.code,
name = it.name,
description = it.description,
errors = errors
)
}
}

// Save Qc Item
qcItem.apply {
code = request.code
name = request.name
description = request.description
}

val savedQcItem = qcItemRepository.save(qcItem)

return savedQcItem.let {
SaveQcItemResponse(
id = it.id,
code = it.code,
name = it.name,
description = it.description,
errors = null
)
}
return qcItemService.saveQcItem(request)
}
}

+ 12
- 0
src/main/java/com/ffii/fpsms/modules/master/web/models/CurrencyRequest.kt Vedi File

@@ -0,0 +1,12 @@
package com.ffii.fpsms.modules.master.web.models

import java.time.LocalDateTime

data class CurrencyRequest (
val id: Long?,
val code: String?,
val name: String?,
val description: String?,
val m18Id: Long?,
val m18LastModifyDate: LocalDateTime?,
)

+ 27
- 0
src/main/java/com/ffii/fpsms/modules/master/web/models/ItemUomRequest.kt Vedi File

@@ -0,0 +1,27 @@
package com.ffii.fpsms.modules.master.web.models

import java.math.BigDecimal
import java.time.LocalDateTime

data class DeleteItemUomRequest(
val id: Long? = null,
val m18Id: Long? = null
)

data class ItemUomRequest(
val id: Long? = null,
val uomId: Long? = null,
val m18UomId: Long? = null,
val itemId: Long? = null,
val m18ItemId: Long? = null,
val baseUnit: Boolean?,
val stockUnit: Boolean?,
val pickingUnit: Boolean?,
val salesUnit: Boolean?,
val purchaseUnit: Boolean?,
val price: BigDecimal?,
val currencyId: Long? = null,
val m18CurrencyId: Long? = null,
val m18Id: Long?,
val m18LastModifyDate: LocalDateTime?,
)

+ 7
- 1
src/main/java/com/ffii/fpsms/modules/purchaseOrder/entity/PurchaseOrder.kt Vedi File

@@ -5,10 +5,11 @@ import com.ffii.fpsms.modules.master.entity.Shop
import com.ffii.fpsms.m18.entity.M18DataLog
import com.ffii.fpsms.modules.purchaseOrder.enums.PurchaseOrderStatus
import com.ffii.fpsms.modules.purchaseOrder.enums.PurchaseOrderStatusConverter
import com.ffii.fpsms.modules.purchaseOrder.enums.PurchaseOrderType
import com.ffii.fpsms.modules.purchaseOrder.enums.PurchaseOrderTypeConverter
import jakarta.persistence.*
import jakarta.validation.constraints.NotNull
import jakarta.validation.constraints.Size
import java.time.LocalDate
import java.time.LocalDateTime

@Entity
@@ -37,6 +38,11 @@ open class PurchaseOrder : BaseEntity<Long>() {
@Convert(converter = PurchaseOrderStatusConverter::class)
open var status: PurchaseOrderStatus? = null

@NotNull
@Column(name = "type", nullable = true, length = 20)
@Convert(converter = PurchaseOrderTypeConverter::class)
open var type: PurchaseOrderType? = null

@NotNull
@ManyToOne
@JoinColumn(name = "m18DataLogId", nullable = false)


+ 8
- 5
src/main/java/com/ffii/fpsms/modules/purchaseOrder/entity/PurchaseOrderLine.kt Vedi File

@@ -3,7 +3,7 @@ package com.ffii.fpsms.modules.purchaseOrder.entity
import com.ffii.core.entity.BaseEntity
import com.ffii.fpsms.modules.master.entity.Items
import com.ffii.fpsms.m18.entity.M18DataLog
import com.ffii.fpsms.modules.master.enums.ShopTypeConverter
import com.ffii.fpsms.modules.master.entity.Currency
import com.ffii.fpsms.modules.purchaseOrder.enums.PurchaseOrderLineStatus
import com.ffii.fpsms.modules.purchaseOrder.enums.PurchaseOrderLineStatusConverter
import jakarta.persistence.*
@@ -40,10 +40,6 @@ open class PurchaseOrderLine : BaseEntity<Long>(){
@Column(name = "price", precision = 14, scale = 2)
open var price: BigDecimal? = null

@Size(max = 5)
@Column(name = "priceUnit", length = 5)
open var priceUnit: String? = null

@Convert(converter = PurchaseOrderLineStatusConverter::class)
@NotNull
@Column(name = "status", nullable = false, length = 10)
@@ -53,4 +49,11 @@ open class PurchaseOrderLine : BaseEntity<Long>(){
@ManyToOne
@JoinColumn(name = "m18DataLogId", nullable = false)
open var m18DataLog: M18DataLog? = null

@Column(name = "uomId")
open var uomId: Long? = null

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "currencyId")
open var currency: Currency? = null
}

src/main/java/com/ffii/fpsms/modules/purchaseOrder/enums/PurchaseOrderStatus.kt → src/main/java/com/ffii/fpsms/modules/purchaseOrder/enums/PurchaseOrderEnum.kt Vedi File

@@ -13,4 +13,10 @@ enum class PurchaseOrderStatus(val value: String) {
?: throw IllegalArgumentException("No enum constant with value: $value")
}
}
}

enum class PurchaseOrderType(val value: String) {
MATERIAL ("material"),
SHOP ("shop"),
OEM ("oem")
}

src/main/java/com/ffii/fpsms/modules/purchaseOrder/enums/PurchaseOrderStatusConverter.kt → src/main/java/com/ffii/fpsms/modules/purchaseOrder/enums/PurchaseOrderEnumConverter.kt Vedi File

@@ -3,6 +3,7 @@ package com.ffii.fpsms.modules.purchaseOrder.enums
import jakarta.persistence.AttributeConverter
import jakarta.persistence.Converter

// Purchase Order Status
@Converter(autoApply = true)
class PurchaseOrderStatusConverter : AttributeConverter<PurchaseOrderStatus, String>{
override fun convertToDatabaseColumn(status: PurchaseOrderStatus?): String? {
@@ -14,4 +15,18 @@ class PurchaseOrderStatusConverter : AttributeConverter<PurchaseOrderStatus, Str
PurchaseOrderStatus.entries.find { it.value == v }
}
}
}

// Purchase Order Type
@Converter(autoApply = true)
class PurchaseOrderTypeConverter : AttributeConverter<PurchaseOrderType, String>{
override fun convertToDatabaseColumn(status: PurchaseOrderType?): String? {
return status?.value
}

override fun convertToEntityAttribute(value: String?): PurchaseOrderType? {
return value?.let { v ->
PurchaseOrderType.entries.find { it.value == v }
}
}
}

src/main/java/com/ffii/fpsms/modules/purchaseOrder/enums/PurchaseOrderLineStatus.kt → src/main/java/com/ffii/fpsms/modules/purchaseOrder/enums/PurchaseOrderLineEnum.kt Vedi File

@@ -1,7 +1,5 @@
package com.ffii.fpsms.modules.purchaseOrder.enums

import com.ffii.fpsms.modules.master.enums.ShopType

enum class PurchaseOrderLineStatus(val value: String) {
PENDING("pending"),
PICKING("picking"),

src/main/java/com/ffii/fpsms/modules/purchaseOrder/enums/PurchaseOrderLineStatusConverter.kt → src/main/java/com/ffii/fpsms/modules/purchaseOrder/enums/PurchaseOrderLineEnumConverter.kt Vedi File


+ 8
- 5
src/main/java/com/ffii/fpsms/modules/purchaseOrder/service/PurchaseOrderLineService.kt Vedi File

@@ -2,8 +2,8 @@ package com.ffii.fpsms.modules.purchaseOrder.service

import com.ffii.fpsms.m18.entity.M18DataLogRepository
import com.ffii.fpsms.modules.master.entity.ItemsRepository
import com.ffii.fpsms.modules.master.service.CurrencyService
import com.ffii.fpsms.modules.master.service.ItemsService
import com.ffii.fpsms.modules.purchaseOrder.entity.PurchaseOrder
import com.ffii.fpsms.modules.purchaseOrder.entity.PurchaseOrderLine
import com.ffii.fpsms.modules.purchaseOrder.entity.PurchaseOrderLineRepository
import com.ffii.fpsms.modules.purchaseOrder.entity.PurchaseOrderRepository
@@ -12,7 +12,6 @@ import com.ffii.fpsms.modules.purchaseOrder.enums.PurchaseOrderLineStatus
import com.ffii.fpsms.modules.purchaseOrder.web.model.SavePurchaseOrderLineRequest
import com.ffii.fpsms.modules.purchaseOrder.web.model.SavePurchaseOrderLineResponse
import org.springframework.stereotype.Service
import java.math.BigDecimal
import kotlin.jvm.optionals.getOrDefault
import kotlin.jvm.optionals.getOrNull

@@ -22,7 +21,8 @@ open class PurchaseOrderLineService(
private val itemsService: ItemsService,
private val itemsRepository: ItemsRepository,
private val purchaseOrderRepository: PurchaseOrderRepository,
private val m18DataLogRepository: M18DataLogRepository
private val m18DataLogRepository: M18DataLogRepository,
private val currencyService: CurrencyService,
) {
open fun allPurchaseOrderLine(): List<PurchaseOrderLine> {
return purchaseOrderLineRepository.findAll()
@@ -31,6 +31,7 @@ open class PurchaseOrderLineService(
open fun findPurchaseOrderLineByM18Id(m18DataLogId: Long): PurchaseOrderLine? {
return purchaseOrderLineRepository.findByM18DataLogIdAndDeletedIsFalse(m18DataLogId)
}

open fun findAllPoLineInfoByPoId(poId: Long): List<PurchaseOrderLineInfo> {
return purchaseOrderLineRepository.findAllPurchaseOrderLineInfoByPurchaseOrderIdAndDeletedIsFalse(poId)
}
@@ -43,6 +44,8 @@ open class PurchaseOrderLineService(
val purchaseOrder = request.purchaseOrderId?.let { purchaseOrderRepository.findById(it).getOrNull() }
val status = request.status?.let { status -> PurchaseOrderLineStatus.entries.find { it.value == status } }
val m18DataLog = request.m18DataLogId?.let { m18DataLogRepository.findById(it).getOrNull() }
val currency = request.m18CurrencyId?.let { currencyService.findByM18Id(it) }
?: request.currencyId?.let { currencyService.findById(it) }

purchaseOrderLine.apply {
this.item = item
@@ -50,7 +53,7 @@ open class PurchaseOrderLineService(
this.purchaseOrder = purchaseOrder
qty = request.qty
price = request.price
priceUnit = request.priceUnit
this.currency = currency
this.status = status
this.m18DataLog = m18DataLog ?: this.m18DataLog
}
@@ -61,7 +64,7 @@ open class PurchaseOrderLineService(
itemNo = it.itemNo,
qty = it.qty,
price = it.price,
priceUnit = it.priceUnit,
currency = currency?.name,
status = it.status?.value
)
}


+ 3
- 0
src/main/java/com/ffii/fpsms/modules/purchaseOrder/service/PurchaseOrderService.kt Vedi File

@@ -10,6 +10,7 @@ import com.ffii.fpsms.modules.purchaseOrder.entity.PurchaseOrderRepository
import com.ffii.fpsms.modules.purchaseOrder.entity.projections.PoLineWithStockInLine
import com.ffii.fpsms.modules.purchaseOrder.entity.projections.PurchaseOrderInfo
import com.ffii.fpsms.modules.purchaseOrder.enums.PurchaseOrderStatus
import com.ffii.fpsms.modules.purchaseOrder.enums.PurchaseOrderType
import com.ffii.fpsms.modules.purchaseOrder.web.model.SavePurchaseOrderRequest
import com.ffii.fpsms.modules.purchaseOrder.web.model.SavePurchaseOrderResponse
import com.ffii.fpsms.modules.stock.entity.StockInLine
@@ -77,6 +78,7 @@ open class PurchaseOrderService(
request.id?.let { purchaseOrderRepository.findById(it).getOrDefault(PurchaseOrder()) } ?: PurchaseOrder()
val supplier = request.supplierId?.let { shopRepository.findById(it).getOrNull() }
val status = request.status?.let { status -> PurchaseOrderStatus.entries.find { it.value == status } }
val type = request.type?.let { type -> PurchaseOrderType.entries.find { it.value == type } }
val m18DataLog = request.m18DataLogId?.let { m18DataLogRepository.findById(it).getOrNull() }

//Need check duplicate?
@@ -88,6 +90,7 @@ open class PurchaseOrderService(
estimatedArrivalDate = request.estimatedArrivalDate
completeDate = request.completeDate
this.status = status
this.type = type
this.m18DataLog = m18DataLog
}



+ 2
- 2
src/main/java/com/ffii/fpsms/modules/purchaseOrder/web/model/SavePurchaseOrderLineRequest.kt Vedi File

@@ -1,6 +1,5 @@
package com.ffii.fpsms.modules.purchaseOrder.web.model

import com.ffii.fpsms.modules.purchaseOrder.enums.PurchaseOrderLineStatus
import java.math.BigDecimal

data class SavePurchaseOrderLineRequest(
@@ -10,7 +9,8 @@ data class SavePurchaseOrderLineRequest(
val purchaseOrderId: Long?,
val qty: BigDecimal?,
val price: BigDecimal?,
val priceUnit: String?,
val currencyId: Long? = null,
val m18CurrencyId: Long? = null,
val status: String?,
val m18DataLogId: Long?,
)

+ 1
- 2
src/main/java/com/ffii/fpsms/modules/purchaseOrder/web/model/SavePurchaseOrderLineResponse.kt Vedi File

@@ -1,6 +1,5 @@
package com.ffii.fpsms.modules.purchaseOrder.web.model

import com.ffii.fpsms.modules.purchaseOrder.enums.PurchaseOrderLineStatus
import java.math.BigDecimal

data class SavePurchaseOrderLineResponse (
@@ -8,6 +7,6 @@ data class SavePurchaseOrderLineResponse (
val itemNo: String?,
val qty: BigDecimal?,
val price: BigDecimal?,
val priceUnit: String?,
val currency: String?,
val status: String?,
)

+ 1
- 0
src/main/java/com/ffii/fpsms/modules/purchaseOrder/web/model/SavePurchaseOrderRequest.kt Vedi File

@@ -12,5 +12,6 @@ data class SavePurchaseOrderRequest (
val estimatedArrivalDate: LocalDateTime?,
val completeDate: LocalDateTime?,
val status: String?,
val type: String?,
val m18DataLogId: Long?
)

+ 5
- 0
src/main/resources/application.yml Vedi File

@@ -36,6 +36,11 @@ m18:
password: db25f2fc14cd2d2b1e7af307241f548fb03c312a
base-url: http://16.162.251.126/jsf/rfws
base-password: qwer1234
supplier:
shop-po: P06, P07
oem-po: T62
supplier-not:
material-po: P06, P07
beId:
toa: 29
pp: 27


+ 19
- 0
src/main/resources/db/changelog/changes/20250512_01_cyril/01_create_currency_table.sql Vedi File

@@ -0,0 +1,19 @@
--liquibase formatted sql

--changeset cyril:create currency table
CREATE TABLE `currency`
(
`id` INT NOT NULL AUTO_INCREMENT,
`created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`createdBy` VARCHAR(30) NULL DEFAULT NULL,
`version` INT NOT NULL DEFAULT '0',
`modified` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`modifiedBy` VARCHAR(30) NULL DEFAULT NULL,
`deleted` TINYINT(1) NOT NULL DEFAULT '0',
`code` VARCHAR(30) NOT NULL,
`name` VARCHAR(30) NOT NULL,
`description` VARCHAR(100) NOT NULL DEFAULT '0',
`m18Id` INT NULL,
`m18LastModifyDate` DATETIME NULL,
CONSTRAINT pk_currency PRIMARY KEY (id)
);

+ 28
- 0
src/main/resources/db/changelog/changes/20250512_01_cyril/02_create_item_uom_table.sql Vedi File

@@ -0,0 +1,28 @@
--liquibase formatted sql

--changeset cyril:create item_uom table
CREATE TABLE `item_uom`
(
`id` INT NOT NULL AUTO_INCREMENT,
`created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`createdBy` VARCHAR(30) NULL DEFAULT NULL,
`version` INT NOT NULL DEFAULT '0',
`modified` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`modifiedBy` VARCHAR(30) NULL DEFAULT NULL,
`deleted` TINYINT(1) NOT NULL DEFAULT '0',
`uomId` INT NOT NULL,
`itemId` INT NOT NULL,
`baseUnit` TINYINT NOT NULL DEFAULT '0',
`stockUnit` TINYINT NOT NULL DEFAULT '0',
`pickingUnit` TINYINT NOT NULL DEFAULT '0',
`salesUnit` TINYINT NOT NULL DEFAULT '0',
`purchaseUnit` TINYINT NOT NULL DEFAULT '0',
`price` DECIMAL(14, 2) NULL,
`currencyId` INT NULL,
`m18Id` INT NOT NULL,
`m18LastModifyDate` DATETIME NULL,
CONSTRAINT pk_item_uom PRIMARY KEY (id),
CONSTRAINT FK_ITEM_UOM_ON_UOMID FOREIGN KEY (uomId) REFERENCES uom_conversion (id),
CONSTRAINT FK_ITEM_UOM_ON_ITEMID FOREIGN KEY (itemId) REFERENCES items (id),
CONSTRAINT FK_ITEM_UOM_ON_CURRENCYID FOREIGN KEY (currencyId) REFERENCES currency (id)
);

+ 8
- 0
src/main/resources/db/changelog/changes/20250512_01_cyril/03_update_purchase_order_line.sql Vedi File

@@ -0,0 +1,8 @@
--liquibase formatted sql

--changeset cyril:update purchase order line

ALTER TABLE `purchase_order_line`
CHANGE COLUMN `priceUnit` `currencyId` INT NULL DEFAULT NULL,
ADD CONSTRAINT `FK_PURCHASE_ORDER_LINE_ON_UOMID` FOREIGN KEY (`uomId`) REFERENCES `uom_conversion` (`id`),
ADD CONSTRAINT `FK_PURCHASE_ORDER_LINE_ON_CURRENCYID` FOREIGN KEY (`currencyId`) REFERENCES `currency` (`id`);

+ 6
- 0
src/main/resources/db/changelog/changes/20250512_01_cyril/04_update_purchase_order.sql Vedi File

@@ -0,0 +1,6 @@
--liquibase formatted sql

--changeset cyril:update purchase order

ALTER TABLE `purchase_order`
ADD COLUMN `type` VARCHAR(20) NULL AFTER `status`;

Caricamento…
Annulla
Salva