diff --git a/build.gradle b/build.gradle index fa213f8..b205c01 100644 --- a/build.gradle +++ b/build.gradle @@ -30,6 +30,8 @@ dependencies { implementation 'org.liquibase:liquibase-core' implementation 'com.google.code.gson:gson:2.8.5' + implementation group: 'org.springframework.cloud', name: 'spring-cloud-context', version: '4.2.1' + implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.12.0' implementation group: 'org.apache.poi', name: 'poi', version: '5.2.3' implementation group: 'org.apache.poi', name: 'poi-ooxml', version: '5.2.3' diff --git a/src/main/java/com/ffii/fpsms/api/service/ApiCallerService.kt b/src/main/java/com/ffii/fpsms/api/service/ApiCallerService.kt index a8d742d..f42f7a2 100644 --- a/src/main/java/com/ffii/fpsms/api/service/ApiCallerService.kt +++ b/src/main/java/com/ffii/fpsms/api/service/ApiCallerService.kt @@ -1,6 +1,9 @@ package com.ffii.fpsms.api.service +import com.ffii.core.utils.JwtTokenUtil import com.ffii.fpsms.m18.M18Config +import org.slf4j.Logger +import org.slf4j.LoggerFactory import org.springframework.beans.factory.annotation.Value import org.springframework.http.HttpHeaders import org.springframework.http.MediaType @@ -11,28 +14,50 @@ import org.springframework.web.reactive.function.client.WebClient import org.springframework.web.reactive.function.client.bodyToMono import reactor.core.publisher.Mono import kotlin.reflect.full.memberProperties +import org.springframework.cloud.context.config.annotation.RefreshScope +import org.springframework.http.HttpStatus +import org.springframework.web.reactive.function.client.ClientRequest +import org.springframework.web.reactive.function.client.WebClientResponseException +import org.springframework.web.reactive.function.client.awaitBody @Service open class ApiCallerService( @Value("\${m18.config.base-url}") private val baseUrl: String, - private val m18Config: M18Config + open val m18Config: M18Config ) { + val logger: Logger = LoggerFactory.getLogger(JwtTokenUtil::class.java) val webClient: WebClient = WebClient.builder() .baseUrl(baseUrl) .defaultHeaders { headers -> headers.set(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) - headers.set(HttpHeaders.AUTHORIZATION, "Bearer ${m18Config.ACCESS_TOKEN}") +// headers.set(HttpHeaders.AUTHORIZATION, "Bearer ${m18Config.ACCESS_TOKEN}") headers.set("client_id", m18Config.CLIENT_ID) } + .filter { request, next -> + // Dynamically fetch the latest ACCESS_TOKEN + val updatedRequest = ClientRequest.from(request) + .header(HttpHeaders.AUTHORIZATION, "Bearer ${m18Config.ACCESS_TOKEN}") + .build() + next.exchange(updatedRequest) + } + .codecs { configurer -> configurer.defaultCodecs().maxInMemorySize(10 * 1024 * 1024) } .build() + /** + * Performs a GET HTTP request to the specified URL path. + * + * @param urlPath The path to send the GET request to (after /jsf/rfws) + * @param params Optional query parameters to include in the request + * @param customHeaders Optional custom headers to include in the request. The default header includes CONTENT_TYPE, AUTHORIZATION, client_id + * @return A Mono that emits the response body converted to type T + */ inline fun get( urlPath: String, params: MultiValueMap, customHeaders: Map? ): Mono { - + println("ACCESS TOKEN: ${m18Config.ACCESS_TOKEN}") return webClient.get() .uri { uriBuilder -> uriBuilder @@ -49,8 +74,30 @@ open class ApiCallerService( } .retrieve() .bodyToMono(T::class.java) + .doOnError { error -> + println("Error occurred: ${error.message}") + } + .onErrorResume(WebClientResponseException::class.java) { error -> + logger.error("WebClientResponseException") + logger.error("Error Status: ${error.statusCode} - ${error.statusText}") + logger.error("Error Message: ${error.message}") + Mono.error(error) + } + .onErrorResume { error -> + logger.error("Exception") + logger.error("Error Message: ${error.message}") + Mono.error(error) + } } + /** + * Performs a GET HTTP request to the specified URL path. + * + * @param urlPath The path to send the GET request to (after /jsf/rfws) + * @param params Optional query parameters to include in the request + * @param customHeaders Optional custom headers to include in the request. The default header includes CONTENT_TYPE, AUTHORIZATION, client_id + * @return A Mono that emits the response body converted to type T + */ inline fun get( urlPath: String, params: Map?, @@ -72,7 +119,29 @@ open class ApiCallerService( return get(urlPath, queryParams, customHeaders) } - // T: Response, U: Request + /** + * Performs a GET HTTP request to the specified URL path. + * + * @param urlPath The path to send the GET request to (after /jsf/rfws) + * @param params Optional query parameters to include in the request + * @return A Mono that emits the response body converted to type T + */ + inline fun get( + urlPath: String, + params: Map? + ): Mono { + return get(urlPath, params, null) + } + + /** + * Performs a GET HTTP request to the specified URL path. + * T: Response, U: Request + * + * @param urlPath The path to send the GET request to (after /jsf/rfws) + * @param params Optional query parameters to include in the request + * @param customHeaders Optional custom headers to include in the request. The default header includes CONTENT_TYPE, AUTHORIZATION, client_id + * @return A Mono that emits the response body converted to type T + */ inline fun get( urlPath: String, params: U?, @@ -95,4 +164,21 @@ open class ApiCallerService( return get(urlPath, queryParams, customHeaders) } + + /** + * Performs a GET HTTP request to the specified URL path. + * T: Response, U: Request + * + * @param urlPath The path to send the GET request to (after /jsf/rfws) + * @param params Optional query parameters to include in the request + * @return A Mono that emits the response body converted to type T + */ + inline fun get( + urlPath: String, + params: U?, + ): Mono { + + return get(urlPath, params, null) + } + } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/m18/M18Config.kt b/src/main/java/com/ffii/fpsms/m18/M18Config.kt index 161895c..dedcb89 100644 --- a/src/main/java/com/ffii/fpsms/m18/M18Config.kt +++ b/src/main/java/com/ffii/fpsms/m18/M18Config.kt @@ -1,12 +1,15 @@ package com.ffii.fpsms.m18 import org.springframework.beans.factory.annotation.Value +import org.springframework.cloud.context.config.annotation.RefreshScope import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration + @Configuration open class M18Config { + // Account @Value("\${m18.config.grant-type}") lateinit var GRANT_TYPE: String; @@ -22,5 +25,55 @@ open class M18Config { @Value("\${m18.config.password}") lateinit var PASSWORD: String; + // Series + @Value("\${m18.config.seriesId.pp}") + var SERIESID_PP: Long? = null; + + @Value("\${m18.config.seriesId.pf}") + var SERIESID_PF: Long? = null; + + @Value("\${m18.config.seriesId.sc}") + var SERIESID_SC: Long? = null; + + @Value("\${m18.config.seriesId.se}") + var SERIESID_SE: Long? = null; + + @Value("\${m18.config.seriesId.sf}") + var SERIESID_SF: Long? = null; + + @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.pf}") + var BEID_PF: Long? = null; + + @Value("\${m18.config.beId.toa}") + var BEID_TOA: Long? = null; + + // Fetch var ACCESS_TOKEN: String? = null; + + /** + * Condition Detail + * Conds Format: + * Id=lessThan=5=and=id=largerOrEqual=3=or=(name=contains =ss=or=name=contains=bb) + * Which means: + * Id<5 and id >=3 or (name like ‘%ss%’ or name like ‘%bb%’) + * **Please use these formats to write the conds: ** + * ("equal", "="), + * ("unequal", "<>"), + * ("largerThan", ">"), + * ("lessThan", "<"), + * ("largerOrEqual", ">="), + * ("lessOrEqual", "<="), + * ("contains", "like"), + * ("doseNotContain", "notlike"), + * ("in", "in"), + * ("notIn", "notin"), + * ("startWith", "like"), + * ("endWith", "like"); + */ } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/stock/entity/M18DataLog.kt b/src/main/java/com/ffii/fpsms/m18/entity/M18DataLog.kt similarity index 57% rename from src/main/java/com/ffii/fpsms/modules/stock/entity/M18DataLog.kt rename to src/main/java/com/ffii/fpsms/m18/entity/M18DataLog.kt index 95708e8..eb215ff 100644 --- a/src/main/java/com/ffii/fpsms/modules/stock/entity/M18DataLog.kt +++ b/src/main/java/com/ffii/fpsms/m18/entity/M18DataLog.kt @@ -1,4 +1,4 @@ -package com.ffii.fpsms.modules.stock.entity +package com.ffii.fpsms.m18.entity import com.ffii.core.entity.BaseEntity import jakarta.persistence.Column @@ -8,26 +8,30 @@ import jakarta.validation.constraints.NotNull import jakarta.validation.constraints.Size import org.hibernate.annotations.JdbcTypeCode import org.hibernate.type.SqlTypes +import java.time.LocalDateTime @Entity @Table(name = "m18_data_log") open class M18DataLog : BaseEntity() { - @Size(max = 10) + @Size(max = 1000) @NotNull - @Column(name = "refType", nullable = false, length = 10) + @Column(name = "refType", nullable = false, length = 1000) open var refType: String? = null @NotNull @Column(name = "m18Id", nullable = false) - open var m18Id: Int? = null + open var m18Id: Long? = null @NotNull @JdbcTypeCode(SqlTypes.JSON) @Column(name = "dataLog", nullable = false) - open var dataLog: MutableMap? = null + open var dataLog: MutableMap? = null - @Size(max = 5) @NotNull - @Column(name = "status", nullable = false, length = 5) - open var status: String? = null + @Column(name = "status", nullable = false) + open var status: Boolean? = null + + @NotNull + @Column(name = "m18LastModifyDate", nullable = false) + open var m18LastModifyDate: LocalDateTime? = null } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/stock/entity/M18DataLogRepository.kt b/src/main/java/com/ffii/fpsms/m18/entity/M18DataLogRepository.kt similarity index 53% rename from src/main/java/com/ffii/fpsms/modules/stock/entity/M18DataLogRepository.kt rename to src/main/java/com/ffii/fpsms/m18/entity/M18DataLogRepository.kt index bbad2b7..34bc288 100644 --- a/src/main/java/com/ffii/fpsms/modules/stock/entity/M18DataLogRepository.kt +++ b/src/main/java/com/ffii/fpsms/m18/entity/M18DataLogRepository.kt @@ -1,8 +1,9 @@ -package com.ffii.fpsms.modules.stock.entity +package com.ffii.fpsms.m18.entity import com.ffii.core.support.AbstractRepository import org.springframework.stereotype.Repository @Repository interface M18DataLogRepository : AbstractRepository { + fun findFirstByM18IdAndRefTypeAndDeletedIsFalseOrderByM18LastModifyDateDesc(m18Id: Long, refType: String): M18DataLog? } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/m18/model/M18DataLogRequest.kt b/src/main/java/com/ffii/fpsms/m18/model/M18DataLogRequest.kt new file mode 100644 index 0000000..dfbec82 --- /dev/null +++ b/src/main/java/com/ffii/fpsms/m18/model/M18DataLogRequest.kt @@ -0,0 +1,12 @@ +package com.ffii.fpsms.m18.model + +import java.time.LocalDateTime + +data class SaveM18DataLogRequest ( + val id: Long?, + val refType: String?, + val m18Id: Long?, + val m18LastModifyDate: LocalDateTime?, + val dataLog: MutableMap?, + val status: Boolean? = true, +) \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/m18/model/M18DataLogResponse.kt b/src/main/java/com/ffii/fpsms/m18/model/M18DataLogResponse.kt new file mode 100644 index 0000000..13ddda8 --- /dev/null +++ b/src/main/java/com/ffii/fpsms/m18/model/M18DataLogResponse.kt @@ -0,0 +1,8 @@ +package com.ffii.fpsms.m18.model + +data class M18DataLogResponse ( + val id: Long?, + val refType: String?, + val m18Id: Long?, + val status: Boolean?, +) \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/m18/model/M18MasterDataRequest.kt b/src/main/java/com/ffii/fpsms/m18/model/M18MasterDataRequest.kt new file mode 100644 index 0000000..288eeaa --- /dev/null +++ b/src/main/java/com/ffii/fpsms/m18/model/M18MasterDataRequest.kt @@ -0,0 +1,40 @@ +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, +) + +/** Vendor List Request */ +data class M18VendorListRequest ( + val stSearch: String = "ven", + val params: String? = null, + val conds: String? = null, +) + +/** Customer Request */ +data class M18CustomerRequest ( + 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, +) diff --git a/src/main/java/com/ffii/fpsms/m18/model/M18MasterDataResponse.kt b/src/main/java/com/ffii/fpsms/m18/model/M18MasterDataResponse.kt new file mode 100644 index 0000000..c8d293f --- /dev/null +++ b/src/main/java/com/ffii/fpsms/m18/model/M18MasterDataResponse.kt @@ -0,0 +1,75 @@ +package com.ffii.fpsms.m18.model + +import java.time.Instant +import java.time.LocalDateTime + +/** Error Messages */ +data class M18ErrorMessages ( + val msgDetail: String?, + val msgCode: String?, +) + +/** Product / Material Response */ +data class M18ItemResponse ( + val data: M18ItemData?, + val messages: List? +) + +data class M18ItemData ( + val pro: List? +) + +data class M18ItemPro ( + val id: Long, + val code: String, + val desc: String, + val unitId: Long, + val seriesId: Long, + val lastModifyDate: Long, +) + +/** Product / Material List Response */ +data class M18ItemListResponse ( + val values: List?, +) + +data class M18ItemListValue ( + val id: Long, + val lastModifyDate: String?, +) + +/** Vendor Response */ +data class M18VendorResponse ( + val data: M18VendorData?, + val messages: List? +) + +data class M18VendorData ( + val ven: List? +) + +data class M18VendorVen ( + val id: Long, + val code: String, + /** name */ + val desc: String, + val `desc_zh-TW`: String, + /** contactNo */ + val tel: String, + val email: String, + val ad1: String, + val ad2: String, + val ad3: String, + val ad4: String, + val lastModifyDate: Long, +) + +/** Vendor List Response */ +data class M18VendorListResponse ( + val values: List?, +) + +data class M18VendorListValue ( + val id: Long, + val lastModifyDate: String?, +) \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/m18/model/M18PurchaseOrderRequest.kt b/src/main/java/com/ffii/fpsms/m18/model/M18PurchaseOrderRequest.kt new file mode 100644 index 0000000..5cd95bb --- /dev/null +++ b/src/main/java/com/ffii/fpsms/m18/model/M18PurchaseOrderRequest.kt @@ -0,0 +1,14 @@ +package com.ffii.fpsms.m18.model + +data class M18PurchaseOrderRequest( + val menuCode: String = "po", + val id: Long?, + val params: String? = null, + val conds: String? = null, +) + +data class M18PurchaseOrderListRequest( + val stSearch: String = "po", + val params: String? = null, + val conds: String? = null, +) diff --git a/src/main/java/com/ffii/fpsms/m18/model/M18PurchaseOrderResponse.kt b/src/main/java/com/ffii/fpsms/m18/model/M18PurchaseOrderResponse.kt new file mode 100644 index 0000000..87a9f57 --- /dev/null +++ b/src/main/java/com/ffii/fpsms/m18/model/M18PurchaseOrderResponse.kt @@ -0,0 +1,49 @@ +package com.ffii.fpsms.m18.model + +import java.math.BigDecimal +import java.time.Instant +import java.time.LocalDateTime + +/** Purchase Order Response */ +data class M18PurchaseOrderResponse ( + val data: M18PurchaseOrderData +) + +data class M18PurchaseOrderData ( + val mainPo: List, + val pot: List +) + +data class M18PurchaseOrderMainPo ( + val id: Long, + val code: String, + /** Supplier Id */ + val venId: Long, + /** ETA */ + val dDate: Long, + /** Order Date */ + val tDate: Long, + val lastModifyDate: Long +) + +data class M18PurchaseOrderPot ( + val id: Long, + val hId: Long, + val code: String, + val desc: String, + val unitId: Long, + val seriesId: Long, + val qty: BigDecimal, + val amt: BigDecimal, +) + +/** Purchase Order List Response */ +data class M18PurchaseOrderListResponse ( + val values: List +) + +data class M18PurchaseOrderListValue ( + val id: Long, + val code: String, + val lastModifyDate: String, +) \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/m18/modals/M18TokenRequest.kt b/src/main/java/com/ffii/fpsms/m18/model/M18TokenRequest.kt similarity index 83% rename from src/main/java/com/ffii/fpsms/m18/modals/M18TokenRequest.kt rename to src/main/java/com/ffii/fpsms/m18/model/M18TokenRequest.kt index a7d92ba..985b05f 100644 --- a/src/main/java/com/ffii/fpsms/m18/modals/M18TokenRequest.kt +++ b/src/main/java/com/ffii/fpsms/m18/model/M18TokenRequest.kt @@ -1,4 +1,4 @@ -package com.ffii.fpsms.m18.modals +package com.ffii.fpsms.m18.model data class M18TokenRequest( val grant_type: String, diff --git a/src/main/java/com/ffii/fpsms/m18/modals/M18TokenResponse.kt b/src/main/java/com/ffii/fpsms/m18/model/M18TokenResponse.kt similarity index 80% rename from src/main/java/com/ffii/fpsms/m18/modals/M18TokenResponse.kt rename to src/main/java/com/ffii/fpsms/m18/model/M18TokenResponse.kt index d075db7..6f42192 100644 --- a/src/main/java/com/ffii/fpsms/m18/modals/M18TokenResponse.kt +++ b/src/main/java/com/ffii/fpsms/m18/model/M18TokenResponse.kt @@ -1,4 +1,4 @@ -package com.ffii.fpsms.m18.modals +package com.ffii.fpsms.m18.model data class M18TokenResponse( val access_token: String, diff --git a/src/main/java/com/ffii/fpsms/m18/service/M18DataLogService.kt b/src/main/java/com/ffii/fpsms/m18/service/M18DataLogService.kt new file mode 100644 index 0000000..a019e2c --- /dev/null +++ b/src/main/java/com/ffii/fpsms/m18/service/M18DataLogService.kt @@ -0,0 +1,42 @@ +package com.ffii.fpsms.m18.service + +import com.ffii.fpsms.m18.entity.M18DataLog +import com.ffii.fpsms.m18.entity.M18DataLogRepository +import com.ffii.fpsms.m18.model.M18DataLogResponse +import com.ffii.fpsms.m18.model.SaveM18DataLogRequest +import org.springframework.stereotype.Service +import kotlin.jvm.optionals.getOrDefault + +@Service +class M18DataLogService( + val m18DataLogRepository: M18DataLogRepository +) { + fun findLatestM18DataLog(m18Id: Long, refType: String): M18DataLog? { + return m18DataLogRepository.findFirstByM18IdAndRefTypeAndDeletedIsFalseOrderByM18LastModifyDateDesc(m18Id, refType) + } + + fun saveM18DataLog(request: SaveM18DataLogRequest): M18DataLogResponse { + val id = request.id + val m18DataLog = + if (id != null && id > 0) m18DataLogRepository.findById(id).getOrDefault(M18DataLog()) else M18DataLog() + + m18DataLog.apply { + refType = request.refType + m18Id = request.m18Id + m18LastModifyDate = request.m18LastModifyDate + dataLog = request.dataLog + status = request.status + } + + val response = m18DataLogRepository.saveAndFlush(m18DataLog).let { dataLog -> + M18DataLogResponse( + id = dataLog.id, + refType = dataLog.refType, + m18Id = dataLog.m18Id, + status = dataLog.status, + ) + } + + return response + } +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/m18/service/M18MasterDataService.kt b/src/main/java/com/ffii/fpsms/m18/service/M18MasterDataService.kt new file mode 100644 index 0000000..d6dd603 --- /dev/null +++ b/src/main/java/com/ffii/fpsms/m18/service/M18MasterDataService.kt @@ -0,0 +1,268 @@ +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.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 org.slf4j.Logger +import org.slf4j.LoggerFactory +import org.springframework.stereotype.Service +import java.time.LocalDate +import java.time.format.DateTimeFormatter + +@Service +open class M18MasterDataService( + val m18Config: M18Config, + val apiCallerService: ApiCallerService, + val itemsService: ItemsService, + val shopService: ShopService, +) { + + val commonUtils = CommonUtils() + val logger: Logger = LoggerFactory.getLogger(JwtTokenUtil::class.java) + + // Everyday update the master data + 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 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 items = apiCallerService.get( + M18_FETCH_ITEM_LIST_API, + itemsParams + ).block() + + return items + } + + open fun getItem(id: Long): M18ItemResponse? { + logger.info("M18 Item ID: $id") + + val itemParams = M18ItemRequest( + id = id, + params = null, + ) + + val item = apiCallerService.get( + M18_LOAD_ITEM_API, + itemParams + ).block() + + return item + } + + open fun saveItems() { + + val items = getItems() + val exampleItems = listOf(10946L, 3825L) + + val successList = mutableListOf() + val failList = mutableListOf() + + if (items?.values != null) { + items.values.forEach { item -> +// if (item.id in exampleItems) { + try { + val itemDetail = getItem(item.id) + if (itemDetail != null && itemDetail.data?.pro != null) { + val pro = itemDetail.data.pro[0] + + val saveItemRequest = NewItemRequest( + code = pro.code, + name = pro.desc, +// type = if (pro.seriesId == m18Config.SERIESID_PF) ItemType.MATERIAL +// else ItemType.PRODUCT, + type = ItemType.MATERIAL, + id = null, + description = pro.desc, + remarks = null, + shelfLife = null, + countryOfOrigin = null, + maxQty = null, + m18Id = item.id, + m18LastModifyDate = commonUtils.InstantToLocalDateTime(pro.lastModifyDate) + ) + + itemsService.saveItem(saveItemRequest) + 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("M18 Item Data: ${e.message}") + logger.error("Fail Count ${failList.size}: Item ID ${item.id} Not Found") + } +// val itemParams = M18ItemRequest( +// id = item.id, +// params = null +// ) + +// apiCallerService.get( +// 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") + + if (failList.size > 0) { + logger.error("Total Fail (${failList.size}): $failList") + } + } + + // --------------------------------------------- Vendor --------------------------------------------- /// + open fun getVendors(): M18VendorListResponse? { + val vendorsParams = M18VendorListRequest( + params = null, + conds = beIdConds +// conds = "lastModifyDate=largerThan=$lastModifyDate" + ) + + val vendors = apiCallerService.get( + M18_FETCH_VENDOR_LIST_API, + vendorsParams + ).block() + + return vendors + } + + open fun getVendor(id: Long): M18VendorResponse? { + logger.info("M18 Vendor ID: $id") + + val vendorParams = M18VendorRequest( + id = id, + params = null, + ) + + val vendor = apiCallerService.get( + M18_LOAD_VENDOR_API, + vendorParams + ).block() + + return vendor + } + + open fun saveVendors() { + val vendors = getVendors() + val exampleVendors = listOf(191L) + + val successList = mutableListOf() + val failList = mutableListOf() + + if (vendors?.values != null) { + vendors.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.desc.ifEmpty { ven.`desc_zh-TW` }, + 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.InstantToLocalDateTime(ven.lastModifyDate) + ) + + shopService.saveShop(saveShopRequest) + successList.add(vendor.id) + logger.info("Success Count ${successList.size}: ${vendor.id} | ${ven.code} | ${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("M18 Vendor Data: ${e.message}") + logger.error("Fail Count ${failList.size}: Vendor ID ${vendor.id} Not Found") + } +// } + } + } else { + logger.error("Items List is null. May occur errors.") + } + + logger.info("Total Success (${successList.size}): $successList") + + if (failList.size > 0) { + logger.error("Total Fail (${failList.size}): $failList") + } + } +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/m18/service/M18PurchaseOrderService.kt b/src/main/java/com/ffii/fpsms/m18/service/M18PurchaseOrderService.kt new file mode 100644 index 0000000..1ab8f6c --- /dev/null +++ b/src/main/java/com/ffii/fpsms/m18/service/M18PurchaseOrderService.kt @@ -0,0 +1,268 @@ +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.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.service.PurchaseOrderLineService +import com.ffii.fpsms.modules.purchaseOrder.service.PurchaseOrderService +import com.ffii.fpsms.modules.purchaseOrder.web.model.SavePurchaseOrderLineRequest +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 + +@Service +open class M18PurchaseOrderService( + val m18Config: M18Config, + val apiCallerService: ApiCallerService, + val m18DataLogService: M18DataLogService, + val purchaseOrderService: PurchaseOrderService, + val purchaseOrderLineService: PurchaseOrderLineService, + val itemsService: ItemsService, + val shopService: ShopService, +) { + val dateTimeConverter = 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}" + + // 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( + params = null, + conds = commonConds + ) + var purchaseOrders: M18PurchaseOrderListResponse? = null + + try { + purchaseOrders = apiCallerService.get( + M18_FETCH_PURCHASE_ORDER_LIST_API, + purchaseOrdersParams + ).block() + } catch (e: Exception) { + logger.error("Error on Function - ${e.stackTrace}") + logger.error(e.message) + } + + return purchaseOrders + } + + open fun getPurchaseOrder(id: Long): M18PurchaseOrderResponse? { + val purchaseOrderParams = M18PurchaseOrderRequest( + id = id + ) + + var purchaseOrder: M18PurchaseOrderResponse? = null + + try { + purchaseOrder = apiCallerService.get( + M18_LOAD_PURCHASE_ORDER_API, + purchaseOrderParams + ).block() + } catch (e: Exception) { + logger.error("Error on Function - ${e.stackTrace}") + logger.error(e.message) + } + + return purchaseOrder + } + + open fun savePurchaseOrders() { + val purchaseOrders = getPurchaseOrders() + val examplePurchaseOrders = listOf(4764034L) + + val successList = mutableListOf() + val successDetailList = mutableListOf() + val failList = mutableListOf() + val failDetailList = mutableListOf() + + if (purchaseOrders != 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) + } + + // purchase_order_line + m18_data_log + if (pot != null) { + // Loop for Purchase Order Lines (pot) + pot.forEach { line -> + val poLineRefType = "Purchase Order Line" + + // Find the latest m18 data log by m18 id & type + val latestM18DataLog = m18DataLogService.findLatestM18DataLog(line.id, poLineRefType) + + // Save to m18_data_log table + 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 = dateTimeConverter.InstantToLocalDateTime(mainPo.lastModifyDate), + dataLog = lineJson, + status = true + ) + + val saveM18PurchaseOrderLineLog = m18DataLogService.saveM18DataLog(saveM18PurchaseOrderLineLogRequest) + + val item = itemsService.findByM18Id(line.id) + logger.info("Item ID: ${item?.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, + ) + + purchaseOrderLineService.savePurchaseOrderLine(savePurchaseOrderLineRequest) + } catch (e: Exception) { + failDetailList.add(line.id) + logger.error("Error on Function - ${e.stackTrace} | Type: Purchase Order Line | M18 ID: ${line.id}") + logger.error(e.message) + + val errorSaveM18PurchaseOrderLineLogRequest = SaveM18DataLogRequest( + id = saveM18PurchaseOrderLineLog.id, + refType = "Purchase Order", + m18Id = line.id, + m18LastModifyDate = dateTimeConverter.InstantToLocalDateTime(mainPo.lastModifyDate), + dataLog = lineJson, + status = false + ) + + m18DataLogService.saveM18DataLog(errorSaveM18PurchaseOrderLineLogRequest) + } + } + } 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) + } + } + } else { + logger.error("Purchase Order List is null. May occur errors.") + } + + // End of save. Check result + logger.info("Total Success (Purchase Order) (${successList.size}): $successList") + if (failList.size > 0) { + logger.error("Total Fail (Purchase Order) (${failList.size}): $failList") + } + + logger.info("Total Success (Purchase Order Line) (${successDetailList.size}): $successDetailList") + if (failDetailList.size > 0) { + logger.error("Total Fail (Purchase Order Line) (${failDetailList.size}): $failDetailList") + } + } +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/m18/service/M18TokenService.kt b/src/main/java/com/ffii/fpsms/m18/service/M18TokenService.kt index 9b35c0d..8983339 100644 --- a/src/main/java/com/ffii/fpsms/m18/service/M18TokenService.kt +++ b/src/main/java/com/ffii/fpsms/m18/service/M18TokenService.kt @@ -2,11 +2,10 @@ package com.ffii.fpsms.m18.service import com.ffii.fpsms.api.service.ApiCallerService import com.ffii.fpsms.m18.M18Config -import com.ffii.fpsms.m18.modals.M18TokenRequest -import com.ffii.fpsms.m18.modals.M18TokenResponse +import com.ffii.fpsms.m18.model.M18TokenRequest +import com.ffii.fpsms.m18.model.M18TokenResponse import jakarta.annotation.PostConstruct import org.springframework.context.annotation.Bean -import org.springframework.stereotype.Component import org.springframework.stereotype.Service @@ -16,7 +15,7 @@ open class M18TokenService( private val m18Config: M18Config ) { -// @Bean + @Bean fun run() { // val params: MutableMap = mutableMapOf( // "grant_type" to m18Config.GRANT_TYPE, diff --git a/src/main/java/com/ffii/fpsms/m18/utils/CommonUtils.kt b/src/main/java/com/ffii/fpsms/m18/utils/CommonUtils.kt new file mode 100644 index 0000000..dd49b1d --- /dev/null +++ b/src/main/java/com/ffii/fpsms/m18/utils/CommonUtils.kt @@ -0,0 +1,20 @@ +package com.ffii.fpsms.m18.utils + +import java.time.Instant +import java.time.LocalDateTime +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("Timestamp: $timestamp") + println("Local Date Time: $localDateTime") + return localDateTime + } + + open fun ListToString(numbers: List, prefix: String, delimiter: String): String { + return numbers.joinToString(delimiter) { "$prefix$it" } + } +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/m18/web/M18TestController.kt b/src/main/java/com/ffii/fpsms/m18/web/M18TestController.kt new file mode 100644 index 0000000..69c9532 --- /dev/null +++ b/src/main/java/com/ffii/fpsms/m18/web/M18TestController.kt @@ -0,0 +1,42 @@ +package com.ffii.fpsms.m18.web + +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 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.RestController + + +@RestController +@RequestMapping("/m18") +class M18TestController ( + private val m18MasterDataService: M18MasterDataService, + private val m18PurchaseOrderService: M18PurchaseOrderService, + private val m18Config: M18Config, +) { + var logger: Logger = LoggerFactory.getLogger(JwtTokenUtil::class.java) + + // --------------------------------------------- Master Data --------------------------------------------- /// + @GetMapping("/item") + fun m18Items() { + logger.info("Access token: ${m18Config.ACCESS_TOKEN}") + m18MasterDataService.saveItems() + } + + @GetMapping("/vendor") + fun m18Vendor() { + logger.info("Access token: ${m18Config.ACCESS_TOKEN}") + m18MasterDataService.saveVendors() + } + + // --------------------------------------------- Purchase Order --------------------------------------------- /// + @GetMapping("/po") + fun m18PO() { + logger.info("Access token: ${m18Config.ACCESS_TOKEN}") + m18PurchaseOrderService.savePurchaseOrders() + } +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/master/entity/Items.kt b/src/main/java/com/ffii/fpsms/modules/master/entity/Items.kt index 1fe6afe..0db4657 100644 --- a/src/main/java/com/ffii/fpsms/modules/master/entity/Items.kt +++ b/src/main/java/com/ffii/fpsms/modules/master/entity/Items.kt @@ -4,6 +4,7 @@ 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 @Entity @Table(name = "items") @@ -37,4 +38,7 @@ open class Items : BaseEntity() { @Column(name = "m18Id") open var m18Id: Long? = null + + @Column(name = "m18LastModifyDate") + open var m18LastModifyDate: LocalDateTime? = null } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/master/entity/ItemsRepository.kt b/src/main/java/com/ffii/fpsms/modules/master/entity/ItemsRepository.kt index dd60959..633ff16 100644 --- a/src/main/java/com/ffii/fpsms/modules/master/entity/ItemsRepository.kt +++ b/src/main/java/com/ffii/fpsms/modules/master/entity/ItemsRepository.kt @@ -7,6 +7,10 @@ import org.springframework.stereotype.Repository @Repository interface ItemsRepository : AbstractRepository { fun findAllByDeletedFalse(): List; + fun findByIdAndDeletedFalse(id: Long): Items; + fun findByCodeAndTypeAndDeletedFalse(code: String, type: String): Items?; + + fun findByM18IdAndDeletedIsFalse(m18Id: Long): Items?; } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/master/entity/Shop.kt b/src/main/java/com/ffii/fpsms/modules/master/entity/Shop.kt index 3d58278..3abbbb8 100644 --- a/src/main/java/com/ffii/fpsms/modules/master/entity/Shop.kt +++ b/src/main/java/com/ffii/fpsms/modules/master/entity/Shop.kt @@ -1,63 +1,75 @@ package com.ffii.fpsms.modules.master.entity import com.ffii.core.entity.BaseEntity +import com.ffii.fpsms.modules.master.enums.ShopType +import com.ffii.fpsms.modules.master.enums.ShopTypeConverter import jakarta.persistence.Column +import jakarta.persistence.Convert 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 = "shop") open class Shop : BaseEntity() { - @Size(max = 30) + @Size(max = 50) @NotNull - @Column(name = "code", nullable = false, length = 30) + @Column(name = "code", nullable = false, length = 50) open var code: String? = null - @Size(max = 30) + @Size(max = 300) @NotNull - @Column(name = "name", nullable = false, length = 30) + @Column(name = "name", nullable = false, length = 300) open var name: String? = null - @Size(max = 30) - @Column(name = "brNo", length = 30) + @Size(max = 50) + @Column(name = "brNo", length = 50) open var brNo: String? = null - @Size(max = 30) - @Column(name = "contactNo", length = 30) + @Size(max = 50) + @Column(name = "contactNo", length = 50) open var contactNo: String? = null - @Size(max = 30) - @Column(name = "contactEmail", length = 30) + @Size(max = 50) + @Column(name = "contactEmail", length = 50) open var contactEmail: String? = null - @Size(max = 30) - @Column(name = "contactName", length = 30) + @Size(max = 50) + @Column(name = "contactName", length = 50) open var contactName: String? = null - @Size(max = 30) - @Column(name = "addr1", length = 30) + @Size(max = 300) + @Column(name = "addr1", length = 300) open var addr1: String? = null - @Size(max = 30) - @Column(name = "addr2", length = 30) + @Size(max = 300) + @Column(name = "addr2", length = 300) open var addr2: String? = null - @Size(max = 30) - @Column(name = "addr3", length = 30) + @Size(max = 300) + @Column(name = "addr3", length = 300) open var addr3: String? = null - @Size(max = 30) - @Column(name = "addr4", length = 30) + @Size(max = 300) + @Column(name = "addr4", length = 300) open var addr4: String? = null - @Size(max = 30) - @Column(name = "district", length = 30) + @Size(max = 300) + @Column(name = "district", length = 300) open var district: String? = null - @Size(max = 10) + @Convert(converter = ShopTypeConverter::class) @Column(name = "type", length = 10) - open var type: String? = null + open var type: ShopType? = null + + @NotNull + @Column(name = "m18Id", nullable = false) + open var m18Id: Long? = null + + @NotNull + @Column(name = "m18LastModifyDate", nullable = false) + open var m18LastModifyDate: LocalDateTime? = null } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/master/entity/ShopRepository.kt b/src/main/java/com/ffii/fpsms/modules/master/entity/ShopRepository.kt index edf5784..ab87775 100644 --- a/src/main/java/com/ffii/fpsms/modules/master/entity/ShopRepository.kt +++ b/src/main/java/com/ffii/fpsms/modules/master/entity/ShopRepository.kt @@ -5,4 +5,9 @@ import org.springframework.stereotype.Repository @Repository interface ShopRepository : AbstractRepository { + fun findAllByDeletedIsFalse(): List + + fun findByIdAndDeletedIsFalse(id: Long): Shop? + + fun findByM18IdAndDeletedIsFalse(m18Id: Long): Shop? } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/master/enums/ShopType.kt b/src/main/java/com/ffii/fpsms/modules/master/enums/ShopType.kt new file mode 100644 index 0000000..2d9dae4 --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/master/enums/ShopType.kt @@ -0,0 +1,6 @@ +package com.ffii.fpsms.modules.master.enums + +enum class ShopType(val value: String) { + SUPPLIER("supplier"), + SHOP("shop"); +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/master/enums/ShopTypeConverter.kt b/src/main/java/com/ffii/fpsms/modules/master/enums/ShopTypeConverter.kt new file mode 100644 index 0000000..48499d6 --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/master/enums/ShopTypeConverter.kt @@ -0,0 +1,17 @@ +package com.ffii.fpsms.modules.master.enums + +import jakarta.persistence.AttributeConverter +import jakarta.persistence.Converter + +@Converter(autoApply = true) +class ShopTypeConverter : AttributeConverter { + override fun convertToDatabaseColumn(type: ShopType?): String? { + return type?.value + } + + override fun convertToEntityAttribute(value: String?): ShopType? { + return value?.let { v -> + ShopType.entries.find { it.value == v } + } + } +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/master/service/ItemsService.kt b/src/main/java/com/ffii/fpsms/modules/master/service/ItemsService.kt index 83b93cc..f664ffe 100644 --- a/src/main/java/com/ffii/fpsms/modules/master/service/ItemsService.kt +++ b/src/main/java/com/ffii/fpsms/modules/master/service/ItemsService.kt @@ -48,6 +48,11 @@ open class ItemsService( return jdbcDao.queryForList(sql.toString(), args); } + + open fun findByM18Id(m18Id: Long): Items? { + return itemsRepository.findByM18IdAndDeletedIsFalse(m18Id) + } + // QcCheck included item open fun getItem(id: Long): ItemWithQcResponse { val list = listOf(1,2) @@ -83,7 +88,7 @@ open class ItemsService( @Throws(IOException::class) @Transactional open fun saveItem(request: NewItemRequest): MessageResponse { - val duplicatedItem = itemsRepository.findByCodeAndTypeAndDeletedFalse(request.code, request.type.name) + val duplicatedItem = itemsRepository.findByCodeAndTypeAndDeletedFalse(request.code, request.type.type) if (duplicatedItem != null && duplicatedItem.id != request.id) { return MessageResponse( id = request.id, @@ -94,7 +99,8 @@ open class ItemsService( errorPosition = "code" ) } - val item = if (request.id != null && request.id > 0) itemsRepository.findByIdAndDeletedFalse(request.id) + val item = if (request.m18Id != null) findByM18Id(request.m18Id) ?: Items() + else if (request.id != null && request.id > 0) itemsRepository.findByIdAndDeletedFalse(request.id) else Items() item.apply { code = request.code @@ -104,7 +110,9 @@ open class ItemsService( shelfLife = request.shelfLife countryOfOrigin = request.countryOfOrigin maxQty = request.maxQty - this.type = request.type.name + this.type = request.type.type + m18Id = request.m18Id ?: this.m18Id + m18LastModifyDate = request.m18LastModifyDate ?: this.m18LastModifyDate } val savedItem = itemsRepository.saveAndFlush(item) return MessageResponse( diff --git a/src/main/java/com/ffii/fpsms/modules/master/service/QcItemService.kt b/src/main/java/com/ffii/fpsms/modules/master/service/QcItemService.kt index 7f4fd00..75f9edc 100644 --- a/src/main/java/com/ffii/fpsms/modules/master/service/QcItemService.kt +++ b/src/main/java/com/ffii/fpsms/modules/master/service/QcItemService.kt @@ -4,7 +4,11 @@ import com.ffii.core.support.AbstractBaseEntityService import com.ffii.core.support.JdbcDao import com.ffii.fpsms.modules.master.entity.QcItem import com.ffii.fpsms.modules.master.entity.QcItemRepository +import com.ffii.fpsms.modules.master.web.models.SaveQcItemRequest +import com.ffii.fpsms.modules.master.web.models.SaveQcItemResponse +import jakarta.validation.Valid import org.springframework.stereotype.Service +import org.springframework.web.bind.annotation.RequestBody @Service open class QcItemService( @@ -31,4 +35,49 @@ open class QcItemService( return allQcItems() } + + open fun saveQcItem(@Valid @RequestBody request: SaveQcItemRequest): SaveQcItemResponse { + +// val qcItemProperties = QcItem::class.members.filterIsInstance>() + val errors = mutableMapOf() + val id = request.id + val qcItem = if (id != null) qcItemRepository.findById(id).orElseThrow() else QcItem() + + // check duplicated code + val duplicateQcItem = 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 + ) + } + } } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/master/service/ShopService.kt b/src/main/java/com/ffii/fpsms/modules/master/service/ShopService.kt new file mode 100644 index 0000000..9a9cf24 --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/master/service/ShopService.kt @@ -0,0 +1,62 @@ +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.enums.ShopType +import com.ffii.fpsms.modules.master.web.models.SaveShopRequest +import com.ffii.fpsms.modules.master.web.models.SaveShopResponse +import org.springframework.stereotype.Service +import kotlin.jvm.optionals.getOrDefault + +@Service +open class ShopService( + val shopRepository: ShopRepository +) { + open fun findAll(): List { + return shopRepository.findAllByDeletedIsFalse() + } + + open fun findById(id: Long): Shop? { + return shopRepository.findByIdAndDeletedIsFalse(id) + } + + open fun findByM18Id(m18Id: Long): Shop? { + return shopRepository.findByM18IdAndDeletedIsFalse(m18Id) + } + + open fun saveShop(request: SaveShopRequest): SaveShopResponse { + val shop = if (request.m18Id != null) { + findByM18Id(request.m18Id) ?: Shop() + } else { + request.id?.let { shopRepository.findById(it).getOrDefault(Shop()) } ?: Shop() + } + val type = request.type?.let { type -> ShopType.entries.find { it.value == type } } + + shop.apply { + code = request.code + name = request.name + brNo = request.brNo + contactNo = request.contactNo + contactEmail = request.contactEmail + contactName = request.contactName + addr1 = request.addr1 + addr2 = request.addr2 + addr3 = request.addr3 + addr4 = request.addr4 + district = request.district + this.type = type + m18Id = request.m18Id ?: this.m18Id + m18LastModifyDate = request.m18LastModifyDate ?: this.m18LastModifyDate + } + + val response = shopRepository.saveAndFlush(shop).let { + SaveShopResponse( + id = it.id, + code = it.code, + name = it.name, + ) + } + + return response + } +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/master/web/models/NewItemRequest.kt b/src/main/java/com/ffii/fpsms/modules/master/web/models/NewItemRequest.kt index c360614..aa8a0c2 100644 --- a/src/main/java/com/ffii/fpsms/modules/master/web/models/NewItemRequest.kt +++ b/src/main/java/com/ffii/fpsms/modules/master/web/models/NewItemRequest.kt @@ -2,8 +2,9 @@ package com.ffii.fpsms.modules.master.web.models import jakarta.validation.constraints.NotBlank import jakarta.validation.constraints.NotNull +import java.time.LocalDateTime -enum class ItemType(type: String) { +enum class ItemType(val type: String) { MATERIAL("mat"), BY_PRODUCT("byp"), PRODUCT("product"), @@ -24,6 +25,7 @@ data class NewItemRequest( val countryOfOrigin: String?, val maxQty: Double?, val m18Id: Long?, + val m18LastModifyDate: LocalDateTime?, // val type: List?, // val uom: List?, // val weightUnit: List?, diff --git a/src/main/java/com/ffii/fpsms/modules/master/web/models/SaveShopRequest.kt b/src/main/java/com/ffii/fpsms/modules/master/web/models/SaveShopRequest.kt new file mode 100644 index 0000000..ce8cb7c --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/master/web/models/SaveShopRequest.kt @@ -0,0 +1,21 @@ +package com.ffii.fpsms.modules.master.web.models + +import java.time.LocalDateTime + +data class SaveShopRequest ( + val id: Long?, + val code: String?, + val name: String?, + val brNo: String?, + val contactNo: String?, + val contactEmail: String?, + val contactName: String?, + val addr1: String?, + val addr2: String?, + val addr3: String?, + val addr4: String?, + val district: String?, + val type: String?, + val m18Id: Long?, + val m18LastModifyDate: LocalDateTime?, +) \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/master/web/models/SaveShopResponse.kt b/src/main/java/com/ffii/fpsms/modules/master/web/models/SaveShopResponse.kt new file mode 100644 index 0000000..b36f1c8 --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/master/web/models/SaveShopResponse.kt @@ -0,0 +1,7 @@ +package com.ffii.fpsms.modules.master.web.models + +data class SaveShopResponse ( + val id: Long?, + val code: String?, + val name: String?, +) \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/purchaseOrder/entity/PurchaseOrder.kt b/src/main/java/com/ffii/fpsms/modules/purchaseOrder/entity/PurchaseOrder.kt index 61ba98c..69f7226 100644 --- a/src/main/java/com/ffii/fpsms/modules/purchaseOrder/entity/PurchaseOrder.kt +++ b/src/main/java/com/ffii/fpsms/modules/purchaseOrder/entity/PurchaseOrder.kt @@ -2,7 +2,9 @@ package com.ffii.fpsms.modules.purchaseOrder.entity import com.ffii.core.entity.BaseEntity import com.ffii.fpsms.modules.master.entity.Shop -import com.ffii.fpsms.modules.stock.entity.M18DataLog +import com.ffii.fpsms.m18.entity.M18DataLog +import com.ffii.fpsms.modules.purchaseOrder.enums.PurchaseOrderStatus +import com.ffii.fpsms.modules.purchaseOrder.enums.PurchaseOrderStatusConverter import jakarta.persistence.* import jakarta.validation.constraints.NotNull import jakarta.validation.constraints.Size @@ -24,16 +26,16 @@ open class PurchaseOrder : BaseEntity() { @Column(name = "orderDate") open var orderDate: LocalDateTime? = null - @Column(name = "estimatedCompleteDate") - open var estimatedCompleteDate: LocalDate? = null + @Column(name = "estimatedArrivalDate") + open var estimatedArrivalDate: LocalDateTime? = null @Column(name = "completeDate") open var completeDate: LocalDateTime? = null - @Size(max = 10) @NotNull @Column(name = "status", nullable = false, length = 10) - open var status: String? = null + @Convert(converter = PurchaseOrderStatusConverter::class) + open var status: PurchaseOrderStatus? = null @NotNull @ManyToOne(fetch = FetchType.LAZY, optional = false) diff --git a/src/main/java/com/ffii/fpsms/modules/purchaseOrder/entity/PurchaseOrderLine.kt b/src/main/java/com/ffii/fpsms/modules/purchaseOrder/entity/PurchaseOrderLine.kt index 30dbc64..cc462c8 100644 --- a/src/main/java/com/ffii/fpsms/modules/purchaseOrder/entity/PurchaseOrderLine.kt +++ b/src/main/java/com/ffii/fpsms/modules/purchaseOrder/entity/PurchaseOrderLine.kt @@ -2,7 +2,10 @@ 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.modules.stock.entity.M18DataLog +import com.ffii.fpsms.m18.entity.M18DataLog +import com.ffii.fpsms.modules.master.enums.ShopTypeConverter +import com.ffii.fpsms.modules.purchaseOrder.enums.PurchaseOrderLineStatus +import com.ffii.fpsms.modules.purchaseOrder.enums.PurchaseOrderLineStatusConverter import jakarta.persistence.* import jakarta.validation.constraints.NotNull import jakarta.validation.constraints.Size @@ -10,7 +13,7 @@ import java.math.BigDecimal @Entity @Table(name = "purchase_order_line") -class PurchaseOrderLine : BaseEntity(){ +open class PurchaseOrderLine : BaseEntity(){ @NotNull @ManyToOne(fetch = FetchType.LAZY, optional = false) @JoinColumn(name = "itemId", nullable = false) @@ -21,6 +24,11 @@ class PurchaseOrderLine : BaseEntity(){ @Column(name = "itemNo", nullable = false, length = 20) open var itemNo: String? = null +// @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 = "purchaseOrderId", nullable = false) @@ -36,10 +44,10 @@ class PurchaseOrderLine : BaseEntity(){ @Column(name = "priceUnit", length = 5) open var priceUnit: String? = null - @Size(max = 10) + @Convert(converter = PurchaseOrderLineStatusConverter::class) @NotNull @Column(name = "status", nullable = false, length = 10) - open var status: String? = null + open var status: PurchaseOrderLineStatus? = null @NotNull @ManyToOne(fetch = FetchType.LAZY, optional = false) diff --git a/src/main/java/com/ffii/fpsms/modules/purchaseOrder/entity/PurchaseOrderLineRepository.kt b/src/main/java/com/ffii/fpsms/modules/purchaseOrder/entity/PurchaseOrderLineRepository.kt index 63f1f66..9bb07d0 100644 --- a/src/main/java/com/ffii/fpsms/modules/purchaseOrder/entity/PurchaseOrderLineRepository.kt +++ b/src/main/java/com/ffii/fpsms/modules/purchaseOrder/entity/PurchaseOrderLineRepository.kt @@ -2,7 +2,9 @@ package com.ffii.fpsms.modules.purchaseOrder.entity import com.ffii.core.support.AbstractRepository import org.springframework.stereotype.Repository +import java.io.Serializable @Repository interface PurchaseOrderLineRepository : AbstractRepository { + fun findByM18DataLogIdAndDeletedIsFalse(m18datalogId: Serializable): PurchaseOrderLine? } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/purchaseOrder/entity/PurchaseOrderRepository.kt b/src/main/java/com/ffii/fpsms/modules/purchaseOrder/entity/PurchaseOrderRepository.kt index 7e28a40..4e80f50 100644 --- a/src/main/java/com/ffii/fpsms/modules/purchaseOrder/entity/PurchaseOrderRepository.kt +++ b/src/main/java/com/ffii/fpsms/modules/purchaseOrder/entity/PurchaseOrderRepository.kt @@ -2,7 +2,9 @@ package com.ffii.fpsms.modules.purchaseOrder.entity import com.ffii.core.support.AbstractRepository import org.springframework.stereotype.Repository +import java.io.Serializable @Repository interface PurchaseOrderRepository : AbstractRepository { + fun findByM18DataLogIdAndDeletedIsFalse(m18datalogId: Serializable): PurchaseOrder? } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/purchaseOrder/enums/PurchaseOrderLineStatus.kt b/src/main/java/com/ffii/fpsms/modules/purchaseOrder/enums/PurchaseOrderLineStatus.kt new file mode 100644 index 0000000..c7371a1 --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/purchaseOrder/enums/PurchaseOrderLineStatus.kt @@ -0,0 +1,10 @@ +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"), + COMPLETED("completed"); +} + diff --git a/src/main/java/com/ffii/fpsms/modules/purchaseOrder/enums/PurchaseOrderLineStatusConverter.kt b/src/main/java/com/ffii/fpsms/modules/purchaseOrder/enums/PurchaseOrderLineStatusConverter.kt new file mode 100644 index 0000000..d6901fd --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/purchaseOrder/enums/PurchaseOrderLineStatusConverter.kt @@ -0,0 +1,17 @@ +package com.ffii.fpsms.modules.purchaseOrder.enums + +import jakarta.persistence.AttributeConverter +import jakarta.persistence.Converter + +@Converter(autoApply = true) +class PurchaseOrderLineStatusConverter : AttributeConverter { + override fun convertToDatabaseColumn(status: PurchaseOrderLineStatus?): String? { + return status?.value + } + + override fun convertToEntityAttribute(value: String?): PurchaseOrderLineStatus? { + return value?.let { v -> + PurchaseOrderLineStatus.entries.find { it.value == v } + } + } +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/purchaseOrder/enums/PurchaseOrderStatus.kt b/src/main/java/com/ffii/fpsms/modules/purchaseOrder/enums/PurchaseOrderStatus.kt new file mode 100644 index 0000000..c0d4b6d --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/purchaseOrder/enums/PurchaseOrderStatus.kt @@ -0,0 +1,16 @@ +package com.ffii.fpsms.modules.purchaseOrder.enums + +import com.ffii.fpsms.modules.master.enums.ShopType + +enum class PurchaseOrderStatus(val value: String) { + PENDING ("pending"), + RECEIVING ("receiving"), + COMPLETED ("completed"); + + companion object { + fun fromValue(value: String): ShopType { + return ShopType.entries.find { it.value == value } + ?: throw IllegalArgumentException("No enum constant with value: $value") + } + } +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/purchaseOrder/enums/PurchaseOrderStatusConverter.kt b/src/main/java/com/ffii/fpsms/modules/purchaseOrder/enums/PurchaseOrderStatusConverter.kt new file mode 100644 index 0000000..5507231 --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/purchaseOrder/enums/PurchaseOrderStatusConverter.kt @@ -0,0 +1,17 @@ +package com.ffii.fpsms.modules.purchaseOrder.enums + +import jakarta.persistence.AttributeConverter +import jakarta.persistence.Converter + +@Converter(autoApply = true) +class PurchaseOrderStatusConverter : AttributeConverter{ + override fun convertToDatabaseColumn(status: PurchaseOrderStatus?): String? { + return status?.value + } + + override fun convertToEntityAttribute(value: String?): PurchaseOrderStatus? { + return value?.let { v -> + PurchaseOrderStatus.entries.find { it.value == v } + } + } +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/purchaseOrder/service/PurchaseOrderLineService.kt b/src/main/java/com/ffii/fpsms/modules/purchaseOrder/service/PurchaseOrderLineService.kt new file mode 100644 index 0000000..53814c3 --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/purchaseOrder/service/PurchaseOrderLineService.kt @@ -0,0 +1,67 @@ +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.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 +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 + +@Service +open class PurchaseOrderLineService( + val purchaseOrderLineRepository: PurchaseOrderLineRepository, + val itemsService: ItemsService, + val itemsRepository: ItemsRepository, + val purchaseOrderRepository: PurchaseOrderRepository, + val m18DataLogRepository: M18DataLogRepository +) { + open fun allPurchaseOrderLine(): List { + return purchaseOrderLineRepository.findAll() + } + + open fun findPurchaseOrderLineByM18Id(m18DataLogId: Long): PurchaseOrderLine? { + return purchaseOrderLineRepository.findByM18DataLogIdAndDeletedIsFalse(m18DataLogId) + } + + open fun savePurchaseOrderLine(request: SavePurchaseOrderLineRequest): SavePurchaseOrderLineResponse { + val purchaseOrderLine = + request.id?.let { purchaseOrderLineRepository.findById(it).getOrDefault(PurchaseOrderLine()) } + ?: PurchaseOrderLine() + val item = request.itemId?.let { itemsRepository.findById(it).getOrNull() } + 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() } + + purchaseOrderLine.apply { + this.item = item + itemNo = item?.code + this.purchaseOrder = purchaseOrder + qty = request.qty + price = request.price + priceUnit = request.priceUnit + this.status = status + this.m18DataLog = m18DataLog ?: this.m18DataLog + } + + val savedPurchaseOrderLine = purchaseOrderLineRepository.saveAndFlush(purchaseOrderLine).let { + SavePurchaseOrderLineResponse( + id = it.id, + itemNo = it.itemNo, + qty = it.qty, + price = it.price, + priceUnit = it.priceUnit, + status = it.status?.value + ) + } + + return savedPurchaseOrderLine + } +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/purchaseOrder/service/PurchaseOrderService.kt b/src/main/java/com/ffii/fpsms/modules/purchaseOrder/service/PurchaseOrderService.kt index 3e9f960..98ffe77 100644 --- a/src/main/java/com/ffii/fpsms/modules/purchaseOrder/service/PurchaseOrderService.kt +++ b/src/main/java/com/ffii/fpsms/modules/purchaseOrder/service/PurchaseOrderService.kt @@ -1,14 +1,60 @@ package com.ffii.fpsms.modules.purchaseOrder.service +import com.ffii.fpsms.m18.entity.M18DataLogRepository +import com.ffii.fpsms.modules.master.entity.ShopRepository import com.ffii.fpsms.modules.purchaseOrder.entity.PurchaseOrder import com.ffii.fpsms.modules.purchaseOrder.entity.PurchaseOrderRepository +import com.ffii.fpsms.modules.purchaseOrder.enums.PurchaseOrderStatus +import com.ffii.fpsms.modules.purchaseOrder.web.model.SavePurchaseOrderRequest +import com.ffii.fpsms.modules.purchaseOrder.web.model.SavePurchaseOrderResponse import org.springframework.stereotype.Service +import kotlin.jvm.optionals.getOrDefault +import kotlin.jvm.optionals.getOrNull @Service open class PurchaseOrderService( - val purchaseOrderRepository: PurchaseOrderRepository + val purchaseOrderRepository: PurchaseOrderRepository, + val shopRepository: ShopRepository, + val m18DataLogRepository: M18DataLogRepository, ) { open fun allPurchaseOrder(): List { return purchaseOrderRepository.findAll() } + + open fun findPurchaseOrderByM18Id(m18DataLogId: Long): PurchaseOrder? { + return purchaseOrderRepository.findByM18DataLogIdAndDeletedIsFalse(m18DataLogId) + } + open fun savePurchaseOrder(request: SavePurchaseOrderRequest): SavePurchaseOrderResponse { + val purchaseOrder = + 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 m18DataLog = request.m18DataLogId?.let { m18DataLogRepository.findById(it).getOrNull() } + + //Need check duplicate? + + purchaseOrder.apply { + code = request.code + this.supplier = supplier + orderDate = request.orderDate + estimatedArrivalDate = request.estimatedArrivalDate + completeDate = request.completeDate + this.status = status + this.m18DataLog = m18DataLog + } + + val savedPurchaseOrder = purchaseOrderRepository.saveAndFlush(purchaseOrder).let { + SavePurchaseOrderResponse( + id = it.id, + code = it.code, + supplierCode = it.supplier?.code, + orderDate = it.orderDate, + estimatedArrivalDate = it.estimatedArrivalDate, + completeDate = it.completeDate, + status = it.status?.value + ) + } + + return savedPurchaseOrder + } } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/purchaseOrder/web/model/SavePurchaseOrderLineRequest.kt b/src/main/java/com/ffii/fpsms/modules/purchaseOrder/web/model/SavePurchaseOrderLineRequest.kt new file mode 100644 index 0000000..0a01644 --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/purchaseOrder/web/model/SavePurchaseOrderLineRequest.kt @@ -0,0 +1,16 @@ +package com.ffii.fpsms.modules.purchaseOrder.web.model + +import com.ffii.fpsms.modules.purchaseOrder.enums.PurchaseOrderLineStatus +import java.math.BigDecimal + +data class SavePurchaseOrderLineRequest( + val id: Long?, + val itemId: Long?, + val uomId: Long?, + val purchaseOrderId: Long?, + val qty: BigDecimal?, + val price: BigDecimal?, + val priceUnit: String?, + val status: String?, + val m18DataLogId: Long?, +) \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/purchaseOrder/web/model/SavePurchaseOrderLineResponse.kt b/src/main/java/com/ffii/fpsms/modules/purchaseOrder/web/model/SavePurchaseOrderLineResponse.kt new file mode 100644 index 0000000..d198d95 --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/purchaseOrder/web/model/SavePurchaseOrderLineResponse.kt @@ -0,0 +1,13 @@ +package com.ffii.fpsms.modules.purchaseOrder.web.model + +import com.ffii.fpsms.modules.purchaseOrder.enums.PurchaseOrderLineStatus +import java.math.BigDecimal + +data class SavePurchaseOrderLineResponse ( + val id: Long?, + val itemNo: String?, + val qty: BigDecimal?, + val price: BigDecimal?, + val priceUnit: String?, + val status: String?, +) \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/purchaseOrder/web/model/SavePurchaseOrderRequest.kt b/src/main/java/com/ffii/fpsms/modules/purchaseOrder/web/model/SavePurchaseOrderRequest.kt new file mode 100644 index 0000000..60043bf --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/purchaseOrder/web/model/SavePurchaseOrderRequest.kt @@ -0,0 +1,16 @@ +package com.ffii.fpsms.modules.purchaseOrder.web.model + +import com.ffii.fpsms.modules.purchaseOrder.enums.PurchaseOrderStatus +import java.time.LocalDate +import java.time.LocalDateTime + +data class SavePurchaseOrderRequest ( + val id: Long?, + val code: String?, + val supplierId: Long?, + val orderDate: LocalDateTime?, + val estimatedArrivalDate: LocalDateTime?, + val completeDate: LocalDateTime?, + val status: String?, + val m18DataLogId: Long? +) \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/purchaseOrder/web/model/SavePurchaseOrderResponse.kt b/src/main/java/com/ffii/fpsms/modules/purchaseOrder/web/model/SavePurchaseOrderResponse.kt new file mode 100644 index 0000000..3dd9004 --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/purchaseOrder/web/model/SavePurchaseOrderResponse.kt @@ -0,0 +1,15 @@ +package com.ffii.fpsms.modules.purchaseOrder.web.model + +import com.ffii.fpsms.modules.purchaseOrder.enums.PurchaseOrderStatus +import java.time.LocalDate +import java.time.LocalDateTime + +data class SavePurchaseOrderResponse ( + val id: Long?, + val code: String?, + val supplierCode: String?, + val orderDate: LocalDateTime?, + val estimatedArrivalDate: LocalDateTime?, + val completeDate: LocalDateTime?, + val status: String?, +) \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/stock/entity/StockInLine.kt b/src/main/java/com/ffii/fpsms/modules/stock/entity/StockInLine.kt index 56bcf54..6c218bd 100644 --- a/src/main/java/com/ffii/fpsms/modules/stock/entity/StockInLine.kt +++ b/src/main/java/com/ffii/fpsms/modules/stock/entity/StockInLine.kt @@ -1,6 +1,7 @@ package com.ffii.fpsms.modules.stock.entity import com.ffii.core.entity.BaseEntity +import com.ffii.fpsms.m18.entity.M18DataLog import com.ffii.fpsms.modules.master.entity.Items import com.ffii.fpsms.modules.stock.entity.enum.StockInLineStatus import com.ffii.fpsms.modules.user.entity.User diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 8633371..cd993a8 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,37 +1,54 @@ server: - servlet: - contextPath: /api - encoding: - charset: UTF-8 - enabled: true - force: true - port: 8090 - error: - include-message: always + servlet: + contextPath: /api + encoding: + charset: UTF-8 + enabled: true + force: true + port: 8090 + error: + include-message: always spring: - servlet: - multipart: - max-file-size: 500MB - max-request-size: 600MB - jpa: - hibernate: - naming: - physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl - database-platform: org.hibernate.dialect.MySQL8Dialect - properties: - hibernate: - dialect: - storage_engine: innodb + servlet: + multipart: + max-file-size: 500MB + max-request-size: 600MB + jpa: + hibernate: + naming: + physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl + database-platform: org.hibernate.dialect.MySQL8Dialect + properties: + hibernate: + dialect: + storage_engine: innodb logging: - config: 'classpath:log4j2.yml' + config: 'classpath:log4j2.yml' m18: - config: - grant-type: password - client-id: M2Y1OGYxMmQtZDRiOS00OTA4LTgyNTktZDRkNzEzNWVkMzRm - client-secret: M2Y2YjQzYzQtZTc2Mi00OTFhLTkwYmItYmJhMzFjZjEyYmY5 - username: testingMTMS - password: db25f2fc14cd2d2b1e7af307241f548fb03c312a - base-url: http://16.162.251.126/jsf/rfws + config: + grant-type: password + client-id: M2Y1OGYxMmQtZDRiOS00OTA4LTgyNTktZDRkNzEzNWVkMzRm + client-secret: M2Y2YjQzYzQtZTc2Mi00OTFhLTkwYmItYmJhMzFjZjEyYmY5 + username: testingMTMS + password: db25f2fc14cd2d2b1e7af307241f548fb03c312a + base-url: http://16.162.251.126/jsf/rfws + base-password: qwer1234 + beId: + toa: 29 + pp: 27 + pf: 1 + seriesId: + pp: 26 + pf: 33 + fa: 2 + fb: 3 + fc: 4 + fd: 5 + ff: 6 + sc: 27 + se: 28 + sf: 70 + sr: 29 \ No newline at end of file diff --git a/src/main/resources/db/changelog/changes/20250312_01_derek/01_master.sql b/src/main/resources/db/changelog/changes/20250312_01_derek/01_master.sql index af45030..a2e2eb5 100644 --- a/src/main/resources/db/changelog/changes/20250312_01_derek/01_master.sql +++ b/src/main/resources/db/changelog/changes/20250312_01_derek/01_master.sql @@ -4,23 +4,24 @@ CREATE TABLE items ( - id INT NOT NULL AUTO_INCREMENT, - version INT NOT NULL DEFAULT '0', - created datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, - createdBy VARCHAR(30) NULL, - modified datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, - modifiedBy VARCHAR(30) NULL, - deleted TINYINT(1) NOT NULL DEFAULT '0', - `code` VARCHAR(50) NOT NULL, - `name` VARCHAR(50) NOT NULL, - description VARCHAR(100) NULL, - remarks varchar(500) NULL, - type VARCHAR(50) NOT NULL, - uomId INT(11) NOT NULL DEFAULT 0, - shelfLife INT(11) NULL, - countryOfOrigin varchar(50) NULL, - maxQty DECIMAL(16, 2) NULL, - m18Id INT(11) NULL, + id INT NOT NULL AUTO_INCREMENT, + version INT NOT NULL DEFAULT '0', + created datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + createdBy VARCHAR(30) NULL, + modified datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + modifiedBy VARCHAR(30) NULL, + deleted TINYINT(1) NOT NULL DEFAULT '0', + `code` VARCHAR(50) NOT NULL, + `name` VARCHAR(500) NOT NULL, + description VARCHAR(500) NULL, + remarks varchar(500) NULL, + type VARCHAR(50) NOT NULL, + uomId INT(11) NOT NULL DEFAULT 0, + shelfLife INT(11) NULL, + countryOfOrigin varchar(50) NULL, + maxQty DECIMAL(16, 2) NULL, + m18Id INT(11) NULL, + m18LastModifyDate datetime NOT NULL, CONSTRAINT pk_material PRIMARY KEY (id) ); CREATE TABLE uom_conversion diff --git a/src/main/resources/db/changelog/changes/20250409_01_cyril/01_master_data.sql b/src/main/resources/db/changelog/changes/20250409_01_cyril/01_master_data.sql index f83e2d5..78ea740 100644 --- a/src/main/resources/db/changelog/changes/20250409_01_cyril/01_master_data.sql +++ b/src/main/resources/db/changelog/changes/20250409_01_cyril/01_master_data.sql @@ -3,26 +3,27 @@ --changeset cyril:master data for shop and warehouse CREATE TABLE `shop` ( - `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, - `brNo` VARCHAR(30) NULL, - `contactNo` VARCHAR(30) NULL, - `contactEmail` VARCHAR(30) NULL, - `contactName` VARCHAR(30) NULL, - `addr1` VARCHAR(30) NULL, - `addr2` VARCHAR(30) NULL, - `addr3` VARCHAR(30) NULL, - `addr4` VARCHAR(30) NULL, - `district` VARCHAR(30) NULL, - `type` VARCHAR(10) NULL, - `m18Id` INT NOT NULL, + `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(50) NOT NULL, + `name` VARCHAR(300) NOT NULL, + `brNo` VARCHAR(50) NULL, + `contactNo` VARCHAR(50) NULL, + `contactEmail` VARCHAR(50) NULL, + `contactName` VARCHAR(50) NULL, + `addr1` VARCHAR(300) NULL, + `addr2` VARCHAR(300) NULL, + `addr3` VARCHAR(300) NULL, + `addr4` VARCHAR(300) NULL, + `district` VARCHAR(300) NULL, + `type` VARCHAR(10) NULL, + `m18Id` INT NOT NULL, + `m18LastModifyDate` datetime NOT NULL, CONSTRAINT pk_shop PRIMARY KEY (id) ); diff --git a/src/main/resources/db/changelog/changes/20250409_01_cyril/02_m18_data_log.sql b/src/main/resources/db/changelog/changes/20250409_01_cyril/02_m18_data_log.sql index 68ffd66..ce9a854 100644 --- a/src/main/resources/db/changelog/changes/20250409_01_cyril/02_m18_data_log.sql +++ b/src/main/resources/db/changelog/changes/20250409_01_cyril/02_m18_data_log.sql @@ -3,16 +3,17 @@ --changeset cyril:m18 data log CREATE TABLE `m18_data_log` ( - `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', - `refType` VARCHAR(10) NOT NULL, - `m18Id` INT NOT NULL, - `dataLog` JSON NOT NULL, - `status` VARCHAR(5) NOT NULL, + `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', + `refType` VARCHAR(1000) NOT NULL, + `m18Id` INT NOT NULL, + `m18LastModifyDate` datetime NOT NULL, + `dataLog` JSON NOT NULL, + `status` TINYINT(1) NOT NULL, CONSTRAINT pk_m18_data_log PRIMARY KEY (id) ); \ No newline at end of file diff --git a/src/main/resources/db/changelog/changes/20250414_01_cyril/01_purchase_order.sql b/src/main/resources/db/changelog/changes/20250414_01_cyril/01_purchase_order.sql index 0ead8f8..a4c0171 100644 --- a/src/main/resources/db/changelog/changes/20250414_01_cyril/01_purchase_order.sql +++ b/src/main/resources/db/changelog/changes/20250414_01_cyril/01_purchase_order.sql @@ -13,7 +13,7 @@ CREATE TABLE `purchase_order` `code` VARCHAR(30) NOT NULL, `supplierId` INT NULL, `orderDate` DATETIME NULL, - `estimatedCompleteDate` DATETIME NULL, + `estimatedArrivalDate` DATETIME NULL, `completeDate` DATETIME NULL, `status` VARCHAR(10) NOT NULL DEFAULT 'pending', `m18DataLogId` INT NOT NULL, @@ -24,23 +24,25 @@ CREATE TABLE `purchase_order` CREATE TABLE `purchase_order_line` ( - `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', - `itemId` INT NOT NULL, - `itemNo` VARCHAR(20) NOT NULL, - `purchaseOrderId` INT NOT NULL, - `qty` DECIMAL(14, 2) NULL, - `price` DECIMAL(14, 2) NULL, - `priceUnit` VARCHAR(5) NULL, - `status` VARCHAR(10) NOT NULL DEFAULT 'pending', - `m18DataLogId` INT NOT NULL, + `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', + `itemId` INT NOT NULL, + `itemNo` VARCHAR(20) NOT NULL, + `uomId` INT NULL, + `purchaseOrderId` INT NOT NULL, + `qty` DECIMAL(14, 2) NULL, + `price` DECIMAL(14, 2) NULL, + `priceUnit` VARCHAR(5) NULL, + `status` VARCHAR(10) NOT NULL DEFAULT 'pending', + `m18DataLogId` INT NOT NULL, CONSTRAINT pk_purchase_order_line PRIMARY KEY (id), CONSTRAINT FK_PURCHASE_ORDER_LINE_ON_ITEMID FOREIGN KEY (itemId) REFERENCES items (id), +-- CONSTRAINT FK_PURCHASE_ORDER_LINE_ON_UOMID FOREIGN KEY (uomId) REFERENCES uom_conversion (id), CONSTRAINT FK_PURCHASE_ORDER_LINE_ON_PURCHASEORDERID FOREIGN KEY (purchaseOrderId) REFERENCES purchase_order (id), CONSTRAINT FK_PURCHASE_ORDER_LINE_ON_M18DATALOGID FOREIGN KEY (m18DataLogId) REFERENCES m18_data_log (id) ); \ No newline at end of file