瀏覽代碼

Merge branch 'master' of https://git.2fi-solutions.com/derek/FPSMS-backend

create_edit_user
jason.lam 3 月之前
父節點
當前提交
d5945c120a
共有 51 個文件被更改,包括 1601 次插入151 次删除
  1. +2
    -0
      build.gradle
  2. +90
    -4
      src/main/java/com/ffii/fpsms/api/service/ApiCallerService.kt
  3. +53
    -0
      src/main/java/com/ffii/fpsms/m18/M18Config.kt
  4. +12
    -8
      src/main/java/com/ffii/fpsms/m18/entity/M18DataLog.kt
  5. +2
    -1
      src/main/java/com/ffii/fpsms/m18/entity/M18DataLogRepository.kt
  6. +12
    -0
      src/main/java/com/ffii/fpsms/m18/model/M18DataLogRequest.kt
  7. +8
    -0
      src/main/java/com/ffii/fpsms/m18/model/M18DataLogResponse.kt
  8. +40
    -0
      src/main/java/com/ffii/fpsms/m18/model/M18MasterDataRequest.kt
  9. +75
    -0
      src/main/java/com/ffii/fpsms/m18/model/M18MasterDataResponse.kt
  10. +14
    -0
      src/main/java/com/ffii/fpsms/m18/model/M18PurchaseOrderRequest.kt
  11. +49
    -0
      src/main/java/com/ffii/fpsms/m18/model/M18PurchaseOrderResponse.kt
  12. +1
    -1
      src/main/java/com/ffii/fpsms/m18/model/M18TokenRequest.kt
  13. +1
    -1
      src/main/java/com/ffii/fpsms/m18/model/M18TokenResponse.kt
  14. +42
    -0
      src/main/java/com/ffii/fpsms/m18/service/M18DataLogService.kt
  15. +268
    -0
      src/main/java/com/ffii/fpsms/m18/service/M18MasterDataService.kt
  16. +268
    -0
      src/main/java/com/ffii/fpsms/m18/service/M18PurchaseOrderService.kt
  17. +3
    -4
      src/main/java/com/ffii/fpsms/m18/service/M18TokenService.kt
  18. +20
    -0
      src/main/java/com/ffii/fpsms/m18/utils/CommonUtils.kt
  19. +42
    -0
      src/main/java/com/ffii/fpsms/m18/web/M18TestController.kt
  20. +4
    -0
      src/main/java/com/ffii/fpsms/modules/master/entity/Items.kt
  21. +4
    -0
      src/main/java/com/ffii/fpsms/modules/master/entity/ItemsRepository.kt
  22. +36
    -24
      src/main/java/com/ffii/fpsms/modules/master/entity/Shop.kt
  23. +5
    -0
      src/main/java/com/ffii/fpsms/modules/master/entity/ShopRepository.kt
  24. +6
    -0
      src/main/java/com/ffii/fpsms/modules/master/enums/ShopType.kt
  25. +17
    -0
      src/main/java/com/ffii/fpsms/modules/master/enums/ShopTypeConverter.kt
  26. +11
    -3
      src/main/java/com/ffii/fpsms/modules/master/service/ItemsService.kt
  27. +49
    -0
      src/main/java/com/ffii/fpsms/modules/master/service/QcItemService.kt
  28. +62
    -0
      src/main/java/com/ffii/fpsms/modules/master/service/ShopService.kt
  29. +3
    -1
      src/main/java/com/ffii/fpsms/modules/master/web/models/NewItemRequest.kt
  30. +21
    -0
      src/main/java/com/ffii/fpsms/modules/master/web/models/SaveShopRequest.kt
  31. +7
    -0
      src/main/java/com/ffii/fpsms/modules/master/web/models/SaveShopResponse.kt
  32. +7
    -5
      src/main/java/com/ffii/fpsms/modules/purchaseOrder/entity/PurchaseOrder.kt
  33. +12
    -4
      src/main/java/com/ffii/fpsms/modules/purchaseOrder/entity/PurchaseOrderLine.kt
  34. +2
    -0
      src/main/java/com/ffii/fpsms/modules/purchaseOrder/entity/PurchaseOrderLineRepository.kt
  35. +2
    -0
      src/main/java/com/ffii/fpsms/modules/purchaseOrder/entity/PurchaseOrderRepository.kt
  36. +10
    -0
      src/main/java/com/ffii/fpsms/modules/purchaseOrder/enums/PurchaseOrderLineStatus.kt
  37. +17
    -0
      src/main/java/com/ffii/fpsms/modules/purchaseOrder/enums/PurchaseOrderLineStatusConverter.kt
  38. +16
    -0
      src/main/java/com/ffii/fpsms/modules/purchaseOrder/enums/PurchaseOrderStatus.kt
  39. +17
    -0
      src/main/java/com/ffii/fpsms/modules/purchaseOrder/enums/PurchaseOrderStatusConverter.kt
  40. +67
    -0
      src/main/java/com/ffii/fpsms/modules/purchaseOrder/service/PurchaseOrderLineService.kt
  41. +47
    -1
      src/main/java/com/ffii/fpsms/modules/purchaseOrder/service/PurchaseOrderService.kt
  42. +16
    -0
      src/main/java/com/ffii/fpsms/modules/purchaseOrder/web/model/SavePurchaseOrderLineRequest.kt
  43. +13
    -0
      src/main/java/com/ffii/fpsms/modules/purchaseOrder/web/model/SavePurchaseOrderLineResponse.kt
  44. +16
    -0
      src/main/java/com/ffii/fpsms/modules/purchaseOrder/web/model/SavePurchaseOrderRequest.kt
  45. +15
    -0
      src/main/java/com/ffii/fpsms/modules/purchaseOrder/web/model/SavePurchaseOrderResponse.kt
  46. +1
    -0
      src/main/java/com/ffii/fpsms/modules/stock/entity/StockInLine.kt
  47. +47
    -30
      src/main/resources/application.yml
  48. +18
    -17
      src/main/resources/db/changelog/changes/20250312_01_derek/01_master.sql
  49. +21
    -20
      src/main/resources/db/changelog/changes/20250409_01_cyril/01_master_data.sql
  50. +12
    -11
      src/main/resources/db/changelog/changes/20250409_01_cyril/02_m18_data_log.sql
  51. +18
    -16
      src/main/resources/db/changelog/changes/20250414_01_cyril/01_purchase_order.sql

+ 2
- 0
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'


+ 90
- 4
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 <reified T : Any> get(
urlPath: String,
params: MultiValueMap<String, String>,
customHeaders: Map<String, String>?
): Mono<T> {

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 <reified T : Any> get(
urlPath: String,
params: Map<String, Any>?,
@@ -72,7 +119,29 @@ open class ApiCallerService(
return get<T>(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 <reified T : Any> get(
urlPath: String,
params: Map<String, Any>?
): Mono<T> {
return get<T>(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 <reified T : Any, reified U : Any> get(
urlPath: String,
params: U?,
@@ -95,4 +164,21 @@ open class ApiCallerService(

return get<T>(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 <reified T : Any, reified U : Any> get(
urlPath: String,
params: U?,
): Mono<T> {

return get<T, U>(urlPath, params, null)
}

}

+ 53
- 0
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");
*/
}

src/main/java/com/ffii/fpsms/modules/stock/entity/M18DataLog.kt → 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<Long>() {
@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<String, Any>? = null
open var dataLog: MutableMap<String, Any?>? = 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
}

src/main/java/com/ffii/fpsms/modules/stock/entity/M18DataLogRepository.kt → 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<M18DataLog, Long> {
fun findFirstByM18IdAndRefTypeAndDeletedIsFalseOrderByM18LastModifyDateDesc(m18Id: Long, refType: String): M18DataLog?
}

+ 12
- 0
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<String, Any?>?,
val status: Boolean? = true,
)

+ 8
- 0
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?,
)

+ 40
- 0
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,
)

+ 75
- 0
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<M18ErrorMessages>?
)

data class M18ItemData (
val pro: List<M18ItemPro>?
)

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<M18ItemListValue>?,
)

data class M18ItemListValue (
val id: Long,
val lastModifyDate: String?,
)

/** Vendor Response */
data class M18VendorResponse (
val data: M18VendorData?,
val messages: List<M18ErrorMessages>?
)

data class M18VendorData (
val ven: List<M18VendorVen>?
)

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<M18VendorListValue>?,
)

data class M18VendorListValue (
val id: Long,
val lastModifyDate: String?,
)

+ 14
- 0
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,
)

+ 49
- 0
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<M18PurchaseOrderMainPo>,
val pot: List<M18PurchaseOrderPot>
)

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<M18PurchaseOrderListValue>
)

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

src/main/java/com/ffii/fpsms/m18/modals/M18TokenRequest.kt → 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,

src/main/java/com/ffii/fpsms/m18/modals/M18TokenResponse.kt → 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,

+ 42
- 0
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
}
}

+ 268
- 0
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<M18ItemListResponse, M18ItemListRequest>(
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<M18ItemResponse, M18ItemRequest>(
M18_LOAD_ITEM_API,
itemParams
).block()

return item
}

open fun saveItems() {

val items = getItems()
val exampleItems = 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) {
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<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")

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<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(
id = id,
params = null,
)

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

return vendor
}

open fun saveVendors() {
val vendors = getVendors()
val exampleVendors = listOf<Long>(191L)

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

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")
}
}
}

+ 268
- 0
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<M18PurchaseOrderListResponse, M18PurchaseOrderListRequest>(
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<M18PurchaseOrderResponse, M18PurchaseOrderRequest>(
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<Long>(4764034L)

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

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")
}
}
}

+ 3
- 4
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<String, String> = mutableMapOf(
// "grant_type" to m18Config.GRANT_TYPE,


+ 20
- 0
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<Long>, prefix: String, delimiter: String): String {
return numbers.joinToString(delimiter) { "$prefix$it" }
}
}

+ 42
- 0
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()
}
}

+ 4
- 0
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<Long>() {

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

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

+ 4
- 0
src/main/java/com/ffii/fpsms/modules/master/entity/ItemsRepository.kt 查看文件

@@ -7,6 +7,10 @@ import org.springframework.stereotype.Repository
@Repository
interface ItemsRepository : AbstractRepository<Items, Long> {
fun findAllByDeletedFalse(): List<Items>;

fun findByIdAndDeletedFalse(id: Long): Items;

fun findByCodeAndTypeAndDeletedFalse(code: String, type: String): Items?;

fun findByM18IdAndDeletedIsFalse(m18Id: Long): Items?;
}

+ 36
- 24
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<Long>() {

@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
}

+ 5
- 0
src/main/java/com/ffii/fpsms/modules/master/entity/ShopRepository.kt 查看文件

@@ -5,4 +5,9 @@ import org.springframework.stereotype.Repository

@Repository
interface ShopRepository : AbstractRepository<Shop, Long> {
fun findAllByDeletedIsFalse(): List<Shop>

fun findByIdAndDeletedIsFalse(id: Long): Shop?

fun findByM18IdAndDeletedIsFalse(m18Id: Long): Shop?
}

+ 6
- 0
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");
}

+ 17
- 0
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<ShopType, String> {
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 }
}
}
}

+ 11
- 3
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(


+ 49
- 0
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<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 = 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
)
}
}
}

+ 62
- 0
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<Shop> {
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
}
}

+ 3
- 1
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<NewTypeRequest>?,
// val uom: List<NewUomRequest>?,
// val weightUnit: List<NewWeightUnitRequest>?,


+ 21
- 0
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?,
)

+ 7
- 0
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?,
)

+ 7
- 5
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<Long>() {
@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)


+ 12
- 4
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<Long>(){
open class PurchaseOrderLine : BaseEntity<Long>(){
@NotNull
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "itemId", nullable = false)
@@ -21,6 +24,11 @@ class PurchaseOrderLine : BaseEntity<Long>(){
@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<Long>(){
@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)


+ 2
- 0
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<PurchaseOrderLine, Long> {
fun findByM18DataLogIdAndDeletedIsFalse(m18datalogId: Serializable): PurchaseOrderLine?
}

+ 2
- 0
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<PurchaseOrder, Long> {
fun findByM18DataLogIdAndDeletedIsFalse(m18datalogId: Serializable): PurchaseOrder?
}

+ 10
- 0
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");
}


+ 17
- 0
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<PurchaseOrderLineStatus, String> {
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 }
}
}
}

+ 16
- 0
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")
}
}
}

+ 17
- 0
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<PurchaseOrderStatus, String>{
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 }
}
}
}

+ 67
- 0
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<PurchaseOrderLine> {
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
}
}

+ 47
- 1
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<PurchaseOrder> {
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
}
}

+ 16
- 0
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?,
)

+ 13
- 0
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?,
)

+ 16
- 0
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?
)

+ 15
- 0
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?,
)

+ 1
- 0
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


+ 47
- 30
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

+ 18
- 17
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


+ 21
- 20
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)
);



+ 12
- 11
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)
);

+ 18
- 16
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)
);

Loading…
取消
儲存