From da55613d556ddd9407fd9c97b1be7df51815ecf8 Mon Sep 17 00:00:00 2001 From: "cyril.tsui" Date: Wed, 20 Aug 2025 17:09:50 +0800 Subject: [PATCH 1/8] quick update --- .../modules/stock/entity/projection/InventoryInfo.kt | 4 ++-- .../stock/entity/projection/InventoryLotLineInfo.kt | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/ffii/fpsms/modules/stock/entity/projection/InventoryInfo.kt b/src/main/java/com/ffii/fpsms/modules/stock/entity/projection/InventoryInfo.kt index 7af8bea..464bcc4 100644 --- a/src/main/java/com/ffii/fpsms/modules/stock/entity/projection/InventoryInfo.kt +++ b/src/main/java/com/ffii/fpsms/modules/stock/entity/projection/InventoryInfo.kt @@ -22,9 +22,9 @@ interface InventoryInfo{ val unavailableQty: BigDecimal? @get:Value("#{(target.onHandQty - target.onHoldQty - target.unavailableQty) / (target.item.itemUoms.^[stockUnit == true && deleted == false]?.ratioN / target.item.itemUoms.^[stockUnit == true && deleted == false]?.ratioD)}") val availableQty: BigDecimal? - @get:Value("#{target.item.itemUoms.^[purchaseUnit == true && deleted == false]?.uom.code}") + @get:Value("#{target.item.itemUoms.^[stockUnit == true && deleted == false]?.uom.code}") val uomCode: String? - @get:Value("#{target.item.itemUoms.^[purchaseUnit == true && deleted == false]?.uom.udfudesc}") + @get:Value("#{target.item.itemUoms.^[stockUnit == true && deleted == false]?.uom.udfudesc}") val uomUdfudesc: String? // @get:Value("#{target.qty * target.uom.gramPerSmallestUnit}") // val germPerSmallestUnit: BigDecimal? diff --git a/src/main/java/com/ffii/fpsms/modules/stock/entity/projection/InventoryLotLineInfo.kt b/src/main/java/com/ffii/fpsms/modules/stock/entity/projection/InventoryLotLineInfo.kt index 38d159e..6e7f58a 100644 --- a/src/main/java/com/ffii/fpsms/modules/stock/entity/projection/InventoryLotLineInfo.kt +++ b/src/main/java/com/ffii/fpsms/modules/stock/entity/projection/InventoryLotLineInfo.kt @@ -26,13 +26,13 @@ interface InventoryLotLineInfo { @get:Value("#{target.inventoryLot.item}") val item: InventoryLotLineItemInfo? val warehouse: InventoryLotLineWarehouseInfo? - @get:Value("#{((target.inQty ?: 0) - (target.outQty ?: 0) - (target.holdQty ?: 0)) / (target.inventoryLot.item.itemUoms.^[salesUnit == true && deleted == false]?.ratioN / target.inventoryLot.item.itemUoms.^[salesUnit == true && deleted == false]?.ratioD)}") + @get:Value("#{((target.inQty ?: 0) - (target.outQty ?: 0) - (target.holdQty ?: 0)) / (target.inventoryLot.item.itemUoms.^[stockUnit == true && deleted == false]?.ratioN / target.inventoryLot.item.itemUoms.^[stockUnit == true && deleted == false]?.ratioD)}") var availableQty: BigDecimal? - @get:Value("#{(target.inQty ?: 0)/ (target.inventoryLot.item.itemUoms.^[salesUnit == true && deleted == false]?.ratioN / target.inventoryLot.item.itemUoms.^[salesUnit == true && deleted == false]?.ratioD)}") + @get:Value("#{(target.inQty ?: 0)/ (target.inventoryLot.item.itemUoms.^[stockUnit == true && deleted == false]?.ratioN / target.inventoryLot.item.itemUoms.^[stockUnit == true && deleted == false]?.ratioD)}") var inQty: BigDecimal? - @get:Value("#{(target.outQty ?: 0)/ (target.inventoryLot.item.itemUoms.^[salesUnit == true && deleted == false]?.ratioN / target.inventoryLot.item.itemUoms.^[salesUnit == true && deleted == false]?.ratioD)}") + @get:Value("#{(target.outQty ?: 0)/ (target.inventoryLot.item.itemUoms.^[stockUnit == true && deleted == false]?.ratioN / target.inventoryLot.item.itemUoms.^[stockUnit == true && deleted == false]?.ratioD)}") var outQty: BigDecimal? - @get:Value("#{(target.holdQty ?: 0)/ (target.inventoryLot.item.itemUoms.^[salesUnit == true && deleted == false]?.ratioN / target.inventoryLot.item.itemUoms.^[salesUnit == true && deleted == false]?.ratioD)}") + @get:Value("#{(target.holdQty ?: 0)/ (target.inventoryLot.item.itemUoms.^[stockUnit == true && deleted == false]?.ratioN / target.inventoryLot.item.itemUoms.^[stockUnit == true && deleted == false]?.ratioD)}") var holdQty: BigDecimal? @get:Value("#{(target.inQty ?: 0) - (target.outQty ?: 0) - (target.holdQty ?: 0)}") val qtyPerSmallestUnit: BigDecimal? From 7bbd19808c3285a03ce0fa051367e48398f35976 Mon Sep 17 00:00:00 2001 From: kelvinsuen Date: Wed, 20 Aug 2025 17:48:56 +0800 Subject: [PATCH 2/8] update inventory lot --- .../modules/stock/entity/projection/InventoryInfo.kt | 12 ++++++++---- .../stock/entity/projection/InventoryLotLineInfo.kt | 12 ++++++++---- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/ffii/fpsms/modules/stock/entity/projection/InventoryInfo.kt b/src/main/java/com/ffii/fpsms/modules/stock/entity/projection/InventoryInfo.kt index 464bcc4..523c7a4 100644 --- a/src/main/java/com/ffii/fpsms/modules/stock/entity/projection/InventoryInfo.kt +++ b/src/main/java/com/ffii/fpsms/modules/stock/entity/projection/InventoryInfo.kt @@ -14,13 +14,17 @@ interface InventoryInfo{ @get:Value("#{target.item.type}") val itemType: String? // @get:Value("#{target.qty / (target.item.itemUoms.^[stockUnit == true && deleted == false]?.ratioN / target.item.itemUoms.^[stockUnit == true && deleted == false]?.ratioD)}") - @get:Value("#{target.onHandQty / (target.item.itemUoms.^[stockUnit == true && deleted == false]?.ratioN / target.item.itemUoms.^[stockUnit == true && deleted == false]?.ratioD)}") +// @get:Value("#{target.onHandQty / (target.item.itemUoms.^[stockUnit == true && deleted == false]?.ratioN / target.item.itemUoms.^[stockUnit == true && deleted == false]?.ratioD)}") + @get:Value("#{target.onHandQty}") val onHandQty: BigDecimal? - @get:Value("#{target.onHoldQty / (target.item.itemUoms.^[stockUnit == true && deleted == false]?.ratioN / target.item.itemUoms.^[stockUnit == true && deleted == false]?.ratioD)}") +// @get:Value("#{target.onHoldQty / (target.item.itemUoms.^[stockUnit == true && deleted == false]?.ratioN / target.item.itemUoms.^[stockUnit == true && deleted == false]?.ratioD)}") + @get:Value("#{target.onHoldQty}") val onHoldQty: BigDecimal? - @get:Value("#{target.unavailableQty / (target.item.itemUoms.^[stockUnit == true && deleted == false]?.ratioN / target.item.itemUoms.^[stockUnit == true && deleted == false]?.ratioD)}") +// @get:Value("#{target.unavailableQty / (target.item.itemUoms.^[stockUnit == true && deleted == false]?.ratioN / target.item.itemUoms.^[stockUnit == true && deleted == false]?.ratioD)}") + @get:Value("#{target.unavailableQty}") val unavailableQty: BigDecimal? - @get:Value("#{(target.onHandQty - target.onHoldQty - target.unavailableQty) / (target.item.itemUoms.^[stockUnit == true && deleted == false]?.ratioN / target.item.itemUoms.^[stockUnit == true && deleted == false]?.ratioD)}") +// @get:Value("#{(target.onHandQty - target.onHoldQty - target.unavailableQty) / (target.item.itemUoms.^[stockUnit == true && deleted == false]?.ratioN / target.item.itemUoms.^[stockUnit == true && deleted == false]?.ratioD)}") + @get:Value("#{(target.onHandQty - target.onHoldQty - target.unavailableQty)}") val availableQty: BigDecimal? @get:Value("#{target.item.itemUoms.^[stockUnit == true && deleted == false]?.uom.code}") val uomCode: String? diff --git a/src/main/java/com/ffii/fpsms/modules/stock/entity/projection/InventoryLotLineInfo.kt b/src/main/java/com/ffii/fpsms/modules/stock/entity/projection/InventoryLotLineInfo.kt index 6e7f58a..8fee2f0 100644 --- a/src/main/java/com/ffii/fpsms/modules/stock/entity/projection/InventoryLotLineInfo.kt +++ b/src/main/java/com/ffii/fpsms/modules/stock/entity/projection/InventoryLotLineInfo.kt @@ -26,13 +26,17 @@ interface InventoryLotLineInfo { @get:Value("#{target.inventoryLot.item}") val item: InventoryLotLineItemInfo? val warehouse: InventoryLotLineWarehouseInfo? - @get:Value("#{((target.inQty ?: 0) - (target.outQty ?: 0) - (target.holdQty ?: 0)) / (target.inventoryLot.item.itemUoms.^[stockUnit == true && deleted == false]?.ratioN / target.inventoryLot.item.itemUoms.^[stockUnit == true && deleted == false]?.ratioD)}") +// @get:Value("#{((target.inQty ?: 0) - (target.outQty ?: 0) - (target.holdQty ?: 0)) / (target.inventoryLot.item.itemUoms.^[stockUnit == true && deleted == false]?.ratioN / target.inventoryLot.item.itemUoms.^[stockUnit == true && deleted == false]?.ratioD)}") + @get:Value("#{(target.inQty ?: 0) - (target.outQty ?: 0) - (target.holdQty ?: 0)}") var availableQty: BigDecimal? - @get:Value("#{(target.inQty ?: 0)/ (target.inventoryLot.item.itemUoms.^[stockUnit == true && deleted == false]?.ratioN / target.inventoryLot.item.itemUoms.^[stockUnit == true && deleted == false]?.ratioD)}") +// @get:Value("#{(target.inQty ?: 0)/ (target.inventoryLot.item.itemUoms.^[stockUnit == true && deleted == false]?.ratioN / target.inventoryLot.item.itemUoms.^[stockUnit == true && deleted == false]?.ratioD)}") + @get:Value("#{target.inQty ?: 0}") var inQty: BigDecimal? - @get:Value("#{(target.outQty ?: 0)/ (target.inventoryLot.item.itemUoms.^[stockUnit == true && deleted == false]?.ratioN / target.inventoryLot.item.itemUoms.^[stockUnit == true && deleted == false]?.ratioD)}") +// @get:Value("#{(target.outQty ?: 0)/ (target.inventoryLot.item.itemUoms.^[stockUnit == true && deleted == false]?.ratioN / target.inventoryLot.item.itemUoms.^[stockUnit == true && deleted == false]?.ratioD)}") + @get:Value("#{(target.outQty ?: 0)}") var outQty: BigDecimal? - @get:Value("#{(target.holdQty ?: 0)/ (target.inventoryLot.item.itemUoms.^[stockUnit == true && deleted == false]?.ratioN / target.inventoryLot.item.itemUoms.^[stockUnit == true && deleted == false]?.ratioD)}") +// @get:Value("#{(target.holdQty ?: 0)/ (target.inventoryLot.item.itemUoms.^[stockUnit == true && deleted == false]?.ratioN / target.inventoryLot.item.itemUoms.^[stockUnit == true && deleted == false]?.ratioD)}") + @get:Value("#{(target.holdQty ?: 0)}") var holdQty: BigDecimal? @get:Value("#{(target.inQty ?: 0) - (target.outQty ?: 0) - (target.holdQty ?: 0)}") val qtyPerSmallestUnit: BigDecimal? From d46343c2660fc054524658da8e01dbd222cf6236 Mon Sep 17 00:00:00 2001 From: "cyril.tsui" Date: Thu, 21 Aug 2025 11:53:06 +0800 Subject: [PATCH 3/8] update escalation log table --- ...rvisionApprovalLog.kt => EscalationLog.kt} | 17 ++-- .../modules/stock/entity/EscalationLogInfo.kt | 57 ++++++++++++ .../stock/entity/EscalationLogRepository.kt | 15 ++++ .../entity/SupervisionApprovalLogInfo.kt | 20 ----- .../SupervisionApprovalLogRepository.kt | 11 --- .../modules/stock/enums/EscalationLogEnum.kt | 7 ++ .../stock/enums/EscalationLogEnumConverter.kt | 18 ++++ .../stock/service/EscalationLogService.kt | 88 +++++++++++++++++++ .../service/SupervisionApprovalLogService.kt | 38 -------- .../stock/web/EscalationLogController.kt | 40 +++++++++ .../web/SupervisionApprovalLogController.kt | 19 ---- .../web/model/SaveEscalationLogRequest.kt | 20 +++++ .../web/model/SaveEscalationLogResponse.kt | 16 ++++ .../web/model/SearchEscalationLogRequest.kt | 7 ++ .../01_update_supervision_approval_log.sql | 6 ++ .../02_update_supervision_approval_log.sql | 5 ++ .../03_rename_supervision_approval_log.sql | 5 ++ 17 files changed, 296 insertions(+), 93 deletions(-) rename src/main/java/com/ffii/fpsms/modules/stock/entity/{SupervisionApprovalLog.kt => EscalationLog.kt} (69%) create mode 100644 src/main/java/com/ffii/fpsms/modules/stock/entity/EscalationLogInfo.kt create mode 100644 src/main/java/com/ffii/fpsms/modules/stock/entity/EscalationLogRepository.kt delete mode 100644 src/main/java/com/ffii/fpsms/modules/stock/entity/SupervisionApprovalLogInfo.kt delete mode 100644 src/main/java/com/ffii/fpsms/modules/stock/entity/SupervisionApprovalLogRepository.kt create mode 100644 src/main/java/com/ffii/fpsms/modules/stock/enums/EscalationLogEnum.kt create mode 100644 src/main/java/com/ffii/fpsms/modules/stock/enums/EscalationLogEnumConverter.kt create mode 100644 src/main/java/com/ffii/fpsms/modules/stock/service/EscalationLogService.kt delete mode 100644 src/main/java/com/ffii/fpsms/modules/stock/service/SupervisionApprovalLogService.kt create mode 100644 src/main/java/com/ffii/fpsms/modules/stock/web/EscalationLogController.kt delete mode 100644 src/main/java/com/ffii/fpsms/modules/stock/web/SupervisionApprovalLogController.kt create mode 100644 src/main/java/com/ffii/fpsms/modules/stock/web/model/SaveEscalationLogRequest.kt create mode 100644 src/main/java/com/ffii/fpsms/modules/stock/web/model/SaveEscalationLogResponse.kt create mode 100644 src/main/java/com/ffii/fpsms/modules/stock/web/model/SearchEscalationLogRequest.kt create mode 100644 src/main/resources/db/changelog/changes/20250818_01_cyril/01_update_supervision_approval_log.sql create mode 100644 src/main/resources/db/changelog/changes/20250818_01_cyril/02_update_supervision_approval_log.sql create mode 100644 src/main/resources/db/changelog/changes/20250818_01_cyril/03_rename_supervision_approval_log.sql diff --git a/src/main/java/com/ffii/fpsms/modules/stock/entity/SupervisionApprovalLog.kt b/src/main/java/com/ffii/fpsms/modules/stock/entity/EscalationLog.kt similarity index 69% rename from src/main/java/com/ffii/fpsms/modules/stock/entity/SupervisionApprovalLog.kt rename to src/main/java/com/ffii/fpsms/modules/stock/entity/EscalationLog.kt index 5a8343e..3d5f173 100644 --- a/src/main/java/com/ffii/fpsms/modules/stock/entity/SupervisionApprovalLog.kt +++ b/src/main/java/com/ffii/fpsms/modules/stock/entity/EscalationLog.kt @@ -1,6 +1,8 @@ package com.ffii.fpsms.modules.stock.entity import com.ffii.core.entity.BaseEntity +import com.ffii.fpsms.modules.stock.enums.EscalationLogStatus +import com.ffii.fpsms.modules.stock.enums.EscalationLogStatusConverter import com.ffii.fpsms.modules.user.entity.User import jakarta.persistence.* import jakarta.validation.constraints.NotNull @@ -8,15 +10,14 @@ import jakarta.validation.constraints.Size import java.time.LocalDateTime @Entity -@Table(name = "supervision_approval_log") -open class SupervisionApprovalLog : BaseEntity() { +@Table(name = "escalation_log") +open class EscalationLog : BaseEntity() { @NotNull @ManyToOne @JoinColumn(name = "personInCharge", nullable = false, referencedColumnName = "id") open var personInCharge: User? = null @Size(max = 100) - @NotNull @Column(name = "type", nullable = false, length = 100) open var type: String? = null @@ -31,13 +32,19 @@ open class SupervisionApprovalLog : BaseEntity() { @Column(name = "recordDate") open var recordDate: LocalDateTime? = null - @Size(max = 100) @NotNull + @Convert(converter = EscalationLogStatusConverter::class) @Column(name = "status", nullable = false, length = 100) - open var status: String? = null + open var status: EscalationLogStatus? = null @Size(max = 500) @NotNull @Column(name = "reason", nullable = false, length = 500) open var reason: String? = null + + @Column(name = "qcTotalCount") + open var qcTotalCount: Int? = null + + @Column(name = "qcFailCount") + open var qcFailCount: Int? = null } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/stock/entity/EscalationLogInfo.kt b/src/main/java/com/ffii/fpsms/modules/stock/entity/EscalationLogInfo.kt new file mode 100644 index 0000000..4b1e7cd --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/stock/entity/EscalationLogInfo.kt @@ -0,0 +1,57 @@ +package com.ffii.fpsms.modules.stock.entity + +import org.springframework.beans.factory.annotation.Value +import java.math.BigDecimal +import java.time.LocalDateTime + +interface EscalationLogInfo { + val id: Long? + + @get:Value("#{target.stockInLine?.stockIn?.purchaseOrder?.id}") + val poId: Long? + + @get:Value("#{target.stockInLine?.purchaseOrderLine?.id}") + val polId: Long? + + @get:Value("#{target.stockInLine?.stockIn?.code}") + val poCode: String? + + @get:Value("#{target.stockInLine?.id}") + val stockInLineId: Long? + + @get:Value("#{target.stockOutLine?.id}") + val stockOutLineId: Long? + + @get:Value("#{target.stockInLine?.dnNo}") + val dnNo: String? + + @get:Value("#{target.stockInLine?.dnDate}") + val dnDate: LocalDateTime? + + @get:Value("#{target.stockInLine?.item?.code}") + val itemCode: String? + + @get:Value("#{target.stockInLine?.item?.name}") + val itemName: String? + + @get:Value("#{target.stockInLine?.demandQty}") + val demandQty: BigDecimal? + + @get:Value("#{target.stockInLine?.acceptedQty}") + val acceptedQty: BigDecimal? + + @get:Value("#{target.personInCharge?.id}") + val personInChargeId: Long? + @get:Value("#{target.personInCharge?.name}") + val personInChargeName: String? + @get:Value("#{target.personInCharge?.title}") + val personInChargeTitle: String? + @get:Value("#{target.personInCharge?.department}") + val personInChargeDepartment: String? + + // @get:Value("#{target.type}") +// val escalationLevel: String + val qcTotalCount: Int?; + val qcFailCount: Int?; + val reason: String?; +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/stock/entity/EscalationLogRepository.kt b/src/main/java/com/ffii/fpsms/modules/stock/entity/EscalationLogRepository.kt new file mode 100644 index 0000000..4fe53bf --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/stock/entity/EscalationLogRepository.kt @@ -0,0 +1,15 @@ +package com.ffii.fpsms.modules.stock.entity + +import com.ffii.core.support.AbstractRepository +import org.springframework.stereotype.Repository +import java.io.Serializable + +@Repository +interface EscalationLogRepository: AbstractRepository { +// fun findAllByPersonInCharge(personInCharge: Long): List + fun findAllInfoByDeletedFalse(): List + + fun findAllInfoByDeletedFalseAndStockInLineIdIn(stockInLineIds: List): List + + fun findAllInfoByDeletedFalseAndPersonInChargeIdIs(personInChargeId: Serializable): List +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/stock/entity/SupervisionApprovalLogInfo.kt b/src/main/java/com/ffii/fpsms/modules/stock/entity/SupervisionApprovalLogInfo.kt deleted file mode 100644 index 1ddf36a..0000000 --- a/src/main/java/com/ffii/fpsms/modules/stock/entity/SupervisionApprovalLogInfo.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.ffii.fpsms.modules.stock.entity - -import org.springframework.beans.factory.annotation.Value - -interface SupervisionApprovalLogInfo { - val id: Long - @get:Value("#{target.stockInLine?.stockIn.purchaseOrder.id}") - val poId: Long - @get:Value("#{target.stockInLine?.purchaseOrderLine.id}") - val polId: Long - @get:Value("#{target.stockInLine?.stockIn.code}") - val poCode: String - @get:Value("#{target.stockInLine?.id}") - val stockInLineId: Long - @get:Value("#{target.stockInLine?.item.name}") - val itemName: String - @get:Value("#{target.type}") - val escalationLevel: String - val reason: String -} \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/stock/entity/SupervisionApprovalLogRepository.kt b/src/main/java/com/ffii/fpsms/modules/stock/entity/SupervisionApprovalLogRepository.kt deleted file mode 100644 index 10d64aa..0000000 --- a/src/main/java/com/ffii/fpsms/modules/stock/entity/SupervisionApprovalLogRepository.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.ffii.fpsms.modules.stock.entity - -import com.ffii.core.support.AbstractRepository -import com.ffii.fpsms.modules.user.entity.User -import org.springframework.stereotype.Repository - -@Repository -interface SupervisionApprovalLogRepository: AbstractRepository { -// fun findAllByPersonInCharge(personInCharge: Long): List - fun findAllInfoByDeletedFalse(): List -} \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/stock/enums/EscalationLogEnum.kt b/src/main/java/com/ffii/fpsms/modules/stock/enums/EscalationLogEnum.kt new file mode 100644 index 0000000..65aa291 --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/stock/enums/EscalationLogEnum.kt @@ -0,0 +1,7 @@ +package com.ffii.fpsms.modules.stock.enums + +enum class EscalationLogStatus(val value: String) { + PENDING("pending"), + ACCEPTED("accepted"), + REJECTED("rejected"), +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/stock/enums/EscalationLogEnumConverter.kt b/src/main/java/com/ffii/fpsms/modules/stock/enums/EscalationLogEnumConverter.kt new file mode 100644 index 0000000..d7c5471 --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/stock/enums/EscalationLogEnumConverter.kt @@ -0,0 +1,18 @@ +package com.ffii.fpsms.modules.stock.enums + +import jakarta.persistence.AttributeConverter +import jakarta.persistence.Converter + +// Escalation Log Status +@Converter(autoApply = true) +class EscalationLogStatusConverter : AttributeConverter{ + override fun convertToDatabaseColumn(status: EscalationLogStatus?): String? { + return status?.value + } + + override fun convertToEntityAttribute(value: String?): EscalationLogStatus? { + return value?.let { v -> + EscalationLogStatus.entries.find { it.value == v } + } + } +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/stock/service/EscalationLogService.kt b/src/main/java/com/ffii/fpsms/modules/stock/service/EscalationLogService.kt new file mode 100644 index 0000000..51ff04a --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/stock/service/EscalationLogService.kt @@ -0,0 +1,88 @@ +package com.ffii.fpsms.modules.stock.service + +import com.ffii.core.support.AbstractBaseEntityService +import com.ffii.core.support.JdbcDao +import com.ffii.fpsms.modules.common.SecurityUtils +import com.ffii.fpsms.modules.stock.entity.* +import com.ffii.fpsms.modules.stock.enums.EscalationLogStatus +import com.ffii.fpsms.modules.stock.web.model.SaveEscalationLogRequest +import com.ffii.fpsms.modules.stock.web.model.SaveEscalationLogResponse +import com.ffii.fpsms.modules.user.service.UserService +import org.springframework.stereotype.Service +import kotlin.jvm.optionals.getOrNull + +@Service +open class EscalationLogService( + private val jdbcDao: JdbcDao, + private val escalationLogRepository: EscalationLogRepository, + private val userService: UserService, + private val stockInLineService: StockInLineService, + private val stockOutLineService: StockOutLineService, + private val stockInLineRepository: StockInLineRepository, + private val stockOutLIneRepository: StockOutLIneRepository, +): AbstractBaseEntityService(jdbcDao, escalationLogRepository) { + open fun getAllLogs(): List { + return escalationLogRepository.findAll() + } + + open fun getLogsByStockInLines(stockInLineIds: List?): List { + val logs = stockInLineIds?.let { escalationLogRepository.findAllInfoByDeletedFalseAndStockInLineIdIn(it) } ?: listOf() + return logs + } + + open fun getLogsByStockInLine(stockInLineId: Long?): List { + val logs = stockInLineId?.let { getLogsByStockInLines(listOf(it)) } ?: listOf() + return logs + } + + open fun getLogsByUser(): List { + val user = SecurityUtils.getUser().orElseThrow() + val logs = user.id?.let { escalationLogRepository.findAllInfoByDeletedFalseAndPersonInChargeIdIs(it) } ?: listOf() + return logs + } + + open fun saveEscalationLog(request: SaveEscalationLogRequest): SaveEscalationLogResponse{ + val escalationLog = request.id?.let { escalationLogRepository.findById(it).getOrNull() } ?: EscalationLog() + val personInCharge = request.personInChargeId.let { userService.getUserById(it) } + if (personInCharge == null && escalationLog.personInCharge == null) { + throw NoSuchElementException("Person In Charge is null."); + } + + val stockInLine = request.stockInLineId?.let { stockInLineRepository.findById(it).getOrNull() } + val stockOutLine = request.stockOutLineId?.let { stockOutLIneRepository.findById(it).getOrNull() } + if (stockInLine == null && stockOutLine == null) { + throw NoSuchElementException("Both stock in line and stock out line are null."); + } + + val status = request.status.let { status -> EscalationLogStatus.entries.find{ it.value == status} } + + escalationLog.apply { + this.personInCharge = personInCharge + type = request.type + this.stockInLine = stockInLine + this.stockOutLine = stockOutLine + recordDate = request.recordDate + this.status = status + reason = request.reason + qcTotalCount = request.qcTotalCount + qcFailCount = request.qcFailCount + } + + val response = escalationLogRepository.save(escalationLog).let { + SaveEscalationLogResponse( + id = it.id, + stockInLineId = it.stockInLine?.id, + stockOutLineId = it.stockOutLine?.id, + personInChargeId = it.personInCharge?.id, + personInChargeName = it.personInCharge?.name, + personInChargeTitle = it.personInCharge?.title, + personInChargeDepartment = it.personInCharge?.department, + recordDate = it.recordDate, + status = it.status?.value, + reason = it.reason, + ) + } + + return response + } +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/stock/service/SupervisionApprovalLogService.kt b/src/main/java/com/ffii/fpsms/modules/stock/service/SupervisionApprovalLogService.kt deleted file mode 100644 index fa52e67..0000000 --- a/src/main/java/com/ffii/fpsms/modules/stock/service/SupervisionApprovalLogService.kt +++ /dev/null @@ -1,38 +0,0 @@ -package com.ffii.fpsms.modules.stock.service - -import com.ffii.core.support.AbstractBaseEntityService -import com.ffii.core.support.JdbcDao -import com.ffii.fpsms.modules.common.SecurityUtils -import com.ffii.fpsms.modules.stock.entity.SupervisionApprovalLog -import com.ffii.fpsms.modules.stock.entity.SupervisionApprovalLogInfo -import com.ffii.fpsms.modules.stock.entity.SupervisionApprovalLogRepository -import org.springframework.stereotype.Service - -@Service -open class SupervisionApprovalLogService( - private val jdbcDao: JdbcDao, - private val supervisionApprovalLogRepository: SupervisionApprovalLogRepository - ): AbstractBaseEntityService(jdbcDao, supervisionApprovalLogRepository) { - open fun getAllLogs(): List { - return supervisionApprovalLogRepository.findAll() - } - - open fun getStockInLog(): List { - val stockInLog = getAllLogs().filter { - it.stockInLine != null - } - return stockInLog - } - open fun getLogByUser(): List { - val user = SecurityUtils.getUser().orElseThrow() - val filterLog = supervisionApprovalLogRepository - .findAllInfoByDeletedFalse() -// .findAll() -// .findAllByPersonInCharge(user.id!!).filter { -// it.stockInLine != null -// } - println("filterLog") - println(filterLog) - return filterLog - } -} \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/stock/web/EscalationLogController.kt b/src/main/java/com/ffii/fpsms/modules/stock/web/EscalationLogController.kt new file mode 100644 index 0000000..36df776 --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/stock/web/EscalationLogController.kt @@ -0,0 +1,40 @@ +package com.ffii.fpsms.modules.stock.web + +import com.ffii.fpsms.modules.stock.entity.EscalationLogInfo +import com.ffii.fpsms.modules.stock.service.EscalationLogService +import com.ffii.fpsms.modules.stock.web.model.SaveEscalationLogRequest +import com.ffii.fpsms.modules.stock.web.model.SaveEscalationLogResponse +import com.ffii.fpsms.modules.stock.web.model.SearchEscalationLogRequest +import jakarta.validation.Valid +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.ModelAttribute +import org.springframework.web.bind.annotation.PostMapping +import org.springframework.web.bind.annotation.RequestBody +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RestController + +@RestController +@RequestMapping("/escalationLog") +class EscalationLogController( + private val escalationLogService: EscalationLogService +) { + @GetMapping("/stockInLines") + fun getLogsByStockInLines(@ModelAttribute request: SearchEscalationLogRequest): List { + return escalationLogService.getLogsByStockInLines(request.stockInLineIds); + } + + @GetMapping("/stockInLine") + fun getLogsByStockInLine(@ModelAttribute request: SearchEscalationLogRequest): List { + return escalationLogService.getLogsByStockInLine(request.stockInLineId); + } + + @GetMapping("/user") + fun getLogsByUser(): List { + return escalationLogService.getLogsByUser(); + } + + @PostMapping("/save") + fun saveEscalationLog(@Valid @RequestBody request: SaveEscalationLogRequest): SaveEscalationLogResponse { + return escalationLogService.saveEscalationLog(request); + } +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/stock/web/SupervisionApprovalLogController.kt b/src/main/java/com/ffii/fpsms/modules/stock/web/SupervisionApprovalLogController.kt deleted file mode 100644 index a0a1364..0000000 --- a/src/main/java/com/ffii/fpsms/modules/stock/web/SupervisionApprovalLogController.kt +++ /dev/null @@ -1,19 +0,0 @@ -package com.ffii.fpsms.modules.stock.web - -import com.ffii.fpsms.modules.stock.entity.SupervisionApprovalLog -import com.ffii.fpsms.modules.stock.entity.SupervisionApprovalLogInfo -import com.ffii.fpsms.modules.stock.service.SupervisionApprovalLogService -import org.springframework.web.bind.annotation.GetMapping -import org.springframework.web.bind.annotation.RequestMapping -import org.springframework.web.bind.annotation.RestController - -@RestController -@RequestMapping("/supervisionApprovalLog") -class SupervisionApprovalLogController( - private val supervisionApprovalLogService: SupervisionApprovalLogService -) { - @GetMapping("/stock-in") - fun getStockInQcLog(): List { - return supervisionApprovalLogService.getLogByUser() - } -} \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/stock/web/model/SaveEscalationLogRequest.kt b/src/main/java/com/ffii/fpsms/modules/stock/web/model/SaveEscalationLogRequest.kt new file mode 100644 index 0000000..c3bf6ef --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/stock/web/model/SaveEscalationLogRequest.kt @@ -0,0 +1,20 @@ +package com.ffii.fpsms.modules.stock.web.model + +import jakarta.validation.constraints.NotBlank +import jakarta.validation.constraints.NotNull +import java.time.LocalDateTime + +data class SaveEscalationLogRequest( + val id: Long?, + @field:NotNull(message = "Person In Charge cannot be empty") + val personInChargeId: Long, + val type: String?, + val stockInLineId: Long?, + val stockOutLineId: Long?, + val recordDate: LocalDateTime?, + @field:NotBlank(message = "Status cannot be empty") + val status: String, + val reason: String?, + val qcTotalCount: Int?, + val qcFailCount: Int?, +) diff --git a/src/main/java/com/ffii/fpsms/modules/stock/web/model/SaveEscalationLogResponse.kt b/src/main/java/com/ffii/fpsms/modules/stock/web/model/SaveEscalationLogResponse.kt new file mode 100644 index 0000000..557df4c --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/stock/web/model/SaveEscalationLogResponse.kt @@ -0,0 +1,16 @@ +package com.ffii.fpsms.modules.stock.web.model + +import java.time.LocalDateTime + +data class SaveEscalationLogResponse( + val id: Long?, + val stockInLineId: Long?, + val stockOutLineId: Long?, + val personInChargeId: Long?, + val personInChargeName: String?, + val personInChargeTitle: String?, + val personInChargeDepartment: String?, + val recordDate: LocalDateTime?, + val status: String?, + val reason: String?, +) diff --git a/src/main/java/com/ffii/fpsms/modules/stock/web/model/SearchEscalationLogRequest.kt b/src/main/java/com/ffii/fpsms/modules/stock/web/model/SearchEscalationLogRequest.kt new file mode 100644 index 0000000..92c3469 --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/stock/web/model/SearchEscalationLogRequest.kt @@ -0,0 +1,7 @@ +package com.ffii.fpsms.modules.stock.web.model + +data class SearchEscalationLogRequest( + val stockInLineIds: List?, + val stockInLineId: Long?, + val userId: Long?, +) diff --git a/src/main/resources/db/changelog/changes/20250818_01_cyril/01_update_supervision_approval_log.sql b/src/main/resources/db/changelog/changes/20250818_01_cyril/01_update_supervision_approval_log.sql new file mode 100644 index 0000000..3f3e060 --- /dev/null +++ b/src/main/resources/db/changelog/changes/20250818_01_cyril/01_update_supervision_approval_log.sql @@ -0,0 +1,6 @@ +-- liquibase formatted sql +-- changeset cyril:update_supervision_approval_log + +ALTER TABLE `supervision_approval_log` + ADD COLUMN `qcFailCount` INT NULL AFTER `reason`, + ADD COLUMN `qcTotalCount` INT NULL AFTER `qcFailCount`; diff --git a/src/main/resources/db/changelog/changes/20250818_01_cyril/02_update_supervision_approval_log.sql b/src/main/resources/db/changelog/changes/20250818_01_cyril/02_update_supervision_approval_log.sql new file mode 100644 index 0000000..b9409ab --- /dev/null +++ b/src/main/resources/db/changelog/changes/20250818_01_cyril/02_update_supervision_approval_log.sql @@ -0,0 +1,5 @@ +-- liquibase formatted sql +-- changeset cyril:update_supervision_approval_log + +ALTER TABLE `supervision_approval_log` + CHANGE COLUMN `type` `type` VARCHAR(100) NULL; diff --git a/src/main/resources/db/changelog/changes/20250818_01_cyril/03_rename_supervision_approval_log.sql b/src/main/resources/db/changelog/changes/20250818_01_cyril/03_rename_supervision_approval_log.sql new file mode 100644 index 0000000..12950a0 --- /dev/null +++ b/src/main/resources/db/changelog/changes/20250818_01_cyril/03_rename_supervision_approval_log.sql @@ -0,0 +1,5 @@ +-- liquibase formatted sql +-- changeset cyril:rename_supervision_approval_log + +ALTER TABLE `supervision_approval_log` + RENAME TO `escalation_log` ; From 7d3f0c20ba0bf5ec33da5b31df9e40f823bcc710 Mon Sep 17 00:00:00 2001 From: kelvinsuen Date: Thu, 21 Aug 2025 13:58:09 +0800 Subject: [PATCH 4/8] update qc result --- .../ffii/fpsms/modules/qc/entity/QcResult.kt | 4 ++++ .../qc/entity/projection/QcResultInfo.kt | 18 +++++++++++------- .../modules/qc/service/QcResultService.kt | 1 + .../qc/web/model/SaveQcResultRequest.kt | 1 + .../stock/service/StockInLineService.kt | 1 + .../20250820_01_kelvin/01_update_qc_result.sql | 4 ++++ 6 files changed, 22 insertions(+), 7 deletions(-) create mode 100644 src/main/resources/db/changelog/changes/20250820_01_kelvin/01_update_qc_result.sql diff --git a/src/main/java/com/ffii/fpsms/modules/qc/entity/QcResult.kt b/src/main/java/com/ffii/fpsms/modules/qc/entity/QcResult.kt index fcc4a13..bd5391a 100644 --- a/src/main/java/com/ffii/fpsms/modules/qc/entity/QcResult.kt +++ b/src/main/java/com/ffii/fpsms/modules/qc/entity/QcResult.kt @@ -37,4 +37,8 @@ open class QcResult: BaseEntity() { @Column(name = "remarks") open var remarks: String? = null + + @NotNull + @Column(name = "qcPassed") + open var qcPassed: Boolean = true } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/qc/entity/projection/QcResultInfo.kt b/src/main/java/com/ffii/fpsms/modules/qc/entity/projection/QcResultInfo.kt index c89f1f5..0d6350c 100644 --- a/src/main/java/com/ffii/fpsms/modules/qc/entity/projection/QcResultInfo.kt +++ b/src/main/java/com/ffii/fpsms/modules/qc/entity/projection/QcResultInfo.kt @@ -4,15 +4,19 @@ import org.springframework.beans.factory.annotation.Value interface QcResultInfo { val id: Long - @get:Value("#{target.qcItem.id}") - val qcItemId: Long - @get:Value("#{target.qcItem.name}") - val name: String - @get:Value("#{target.qcItem.code}") - val code: String + @get:Value("#{target.qcItem?.id}") + val qcItemId: Long? + @get:Value("#{target.qcItem?.code}") + val code: String? + @get:Value("#{target.qcItem?.name}") + val name: String? + @get:Value("#{target.qcItem?.description}") + val description: String? + val remarks: String? @get:Value("#{target.stockInLine?.id}") val stockInLineId: Long? @get:Value("#{target.stockOutLine?.id}") val stockOutLineId: Long? - val failQty: Double + val failQty: Double? + val qcPassed: Boolean } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/qc/service/QcResultService.kt b/src/main/java/com/ffii/fpsms/modules/qc/service/QcResultService.kt index ed86038..76c38ed 100644 --- a/src/main/java/com/ffii/fpsms/modules/qc/service/QcResultService.kt +++ b/src/main/java/com/ffii/fpsms/modules/qc/service/QcResultService.kt @@ -42,6 +42,7 @@ open class QcResultService( this.failQty = request.failQty this.type = request.type this.remarks = request.remarks + this.qcPassed = request.qcPassed } val savedQcResult = saveAndFlush(qcResult) return MessageResponse( diff --git a/src/main/java/com/ffii/fpsms/modules/qc/web/model/SaveQcResultRequest.kt b/src/main/java/com/ffii/fpsms/modules/qc/web/model/SaveQcResultRequest.kt index bda1695..344f3ba 100644 --- a/src/main/java/com/ffii/fpsms/modules/qc/web/model/SaveQcResultRequest.kt +++ b/src/main/java/com/ffii/fpsms/modules/qc/web/model/SaveQcResultRequest.kt @@ -7,6 +7,7 @@ data class SaveQcResultRequest( val stockInLineId: Long?, val stockOutLineId: Long?, val failQty: Double, + val qcPassed: Boolean, val type: String?, val remarks: String?, ) diff --git a/src/main/java/com/ffii/fpsms/modules/stock/service/StockInLineService.kt b/src/main/java/com/ffii/fpsms/modules/stock/service/StockInLineService.kt index 9450014..f1705d9 100644 --- a/src/main/java/com/ffii/fpsms/modules/stock/service/StockInLineService.kt +++ b/src/main/java/com/ffii/fpsms/modules/stock/service/StockInLineService.kt @@ -171,6 +171,7 @@ open class StockInLineService( this.item = item this.stockInLine = stockInLine this.failQty = it.failQty + this.qcPassed = it.qcPassed this.type = "qc" // default as qc for now this.remarks = it.remarks } diff --git a/src/main/resources/db/changelog/changes/20250820_01_kelvin/01_update_qc_result.sql b/src/main/resources/db/changelog/changes/20250820_01_kelvin/01_update_qc_result.sql new file mode 100644 index 0000000..c8535fa --- /dev/null +++ b/src/main/resources/db/changelog/changes/20250820_01_kelvin/01_update_qc_result.sql @@ -0,0 +1,4 @@ +-- liquibase formatted sql +-- changeset kelvin:update qc result table +ALTER TABLE `qc_result` +ADD COLUMN `qcPassed` TINYINT(1) NOT NULL DEFAULT 1 AFTER `remarks`; From 022050346d66dfb3a402567c90499762c87574af Mon Sep 17 00:00:00 2001 From: "cyril.tsui" Date: Thu, 21 Aug 2025 18:31:27 +0800 Subject: [PATCH 5/8] add escalation user list --- .../modules/user/entity/UserRepository.java | 3 +++ .../user/entity/projections/UserCombo.java | 19 +++++++++++++++++++ .../modules/user/service/UserService.java | 5 +++++ .../modules/user/web/UserController.java | 6 ++++++ 4 files changed, 33 insertions(+) create mode 100644 src/main/java/com/ffii/fpsms/modules/user/entity/projections/UserCombo.java diff --git a/src/main/java/com/ffii/fpsms/modules/user/entity/UserRepository.java b/src/main/java/com/ffii/fpsms/modules/user/entity/UserRepository.java index 79ae635..597ce7e 100644 --- a/src/main/java/com/ffii/fpsms/modules/user/entity/UserRepository.java +++ b/src/main/java/com/ffii/fpsms/modules/user/entity/UserRepository.java @@ -3,6 +3,7 @@ package com.ffii.fpsms.modules.user.entity; import java.util.List; import java.util.Optional; +import com.ffii.fpsms.modules.user.entity.projections.UserCombo; import org.springframework.data.repository.query.Param; import com.ffii.core.support.AbstractRepository; @@ -12,4 +13,6 @@ public interface UserRepository extends AbstractRepository { List findByName(@Param("name") String name); Optional findByUsernameAndDeletedFalse(String username); + + List findUserComboByTitleNotNullAndDepartmentNotNull(); } diff --git a/src/main/java/com/ffii/fpsms/modules/user/entity/projections/UserCombo.java b/src/main/java/com/ffii/fpsms/modules/user/entity/projections/UserCombo.java new file mode 100644 index 0000000..1d117c0 --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/user/entity/projections/UserCombo.java @@ -0,0 +1,19 @@ +package com.ffii.fpsms.modules.user.entity.projections; + +import org.springframework.beans.factory.annotation.Value; + +public interface UserCombo { + Long getId(); + + @Value("#{target.id}") + Long getValue(); + + @Value("#{target.department} - #{target.title} - #{target.name}") + String getLabel(); + + String getName(); + + String getDepartment(); + + String getTitle(); +} diff --git a/src/main/java/com/ffii/fpsms/modules/user/service/UserService.java b/src/main/java/com/ffii/fpsms/modules/user/service/UserService.java index 7b4aa87..e316b4a 100644 --- a/src/main/java/com/ffii/fpsms/modules/user/service/UserService.java +++ b/src/main/java/com/ffii/fpsms/modules/user/service/UserService.java @@ -8,6 +8,7 @@ import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; +import com.ffii.fpsms.modules.user.entity.projections.UserCombo; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; @@ -269,4 +270,8 @@ public class UserService extends AbstractBaseEntityService getEscalationCombo() { + return userRepository.findUserComboByTitleNotNullAndDepartmentNotNull(); + } } diff --git a/src/main/java/com/ffii/fpsms/modules/user/web/UserController.java b/src/main/java/com/ffii/fpsms/modules/user/web/UserController.java index 6aa3656..8fc4b50 100644 --- a/src/main/java/com/ffii/fpsms/modules/user/web/UserController.java +++ b/src/main/java/com/ffii/fpsms/modules/user/web/UserController.java @@ -5,6 +5,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import com.ffii.fpsms.modules.user.entity.projections.UserCombo; import com.ffii.fpsms.modules.user.service.pojo.UserRecord; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -72,6 +73,11 @@ public class UserController{ return ResponseEntity.ok(userService.search(req)); } + @GetMapping("/escalation-combo") + public ResponseEntity> escalationCombo() { + return ResponseEntity.ok(userService.getEscalationCombo()); + } + @GetMapping("/name-list") public ResponseEntity>> namelist() { SearchUserReq req = new SearchUserReq(); From 89ec70ef7dda3349d85a979daf5b907fc487f298 Mon Sep 17 00:00:00 2001 From: "cyril.tsui" Date: Fri, 22 Aug 2025 18:17:00 +0800 Subject: [PATCH 6/8] update escalation --- .../modules/stock/entity/EscalationLogInfo.kt | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/main/java/com/ffii/fpsms/modules/stock/entity/EscalationLogInfo.kt b/src/main/java/com/ffii/fpsms/modules/stock/entity/EscalationLogInfo.kt index 4b1e7cd..88c928d 100644 --- a/src/main/java/com/ffii/fpsms/modules/stock/entity/EscalationLogInfo.kt +++ b/src/main/java/com/ffii/fpsms/modules/stock/entity/EscalationLogInfo.kt @@ -40,8 +40,22 @@ interface EscalationLogInfo { @get:Value("#{target.stockInLine?.acceptedQty}") val acceptedQty: BigDecimal? + @get:Value("#{target.stockInLine?.item?.itemUoms.^[purchaseUnit == true && deleted == false]?.uom.code}") + val purchaseUomCode: String? + + @get:Value("#{target.stockInLine?.item?.itemUoms.^[purchaseUnit == true && deleted == false]?.uom.udfudesc}") + val purchaseUomDesc: String? + + @get:Value("#{target.stockInLine?.item?.itemUoms.^[stockUnit == true && deleted == false]?.uom.code}") + val stockUomCode: String? + + @get:Value("#{target.stockInLine?.item?.itemUoms.^[stockUnit == true && deleted == false]?.uom.udfudesc}") + val stockUomDesc: String? + @get:Value("#{target.personInCharge?.id}") val personInChargeId: Long? + @get:Value("#{target.personInCharge?.department} - #{target.personInCharge?.title} - #{target.personInCharge?.name}") + val personInCharge: String? @get:Value("#{target.personInCharge?.name}") val personInChargeName: String? @get:Value("#{target.personInCharge?.title}") From 1ec389acfe015a832ad4b1f2209ccec1917d1ff7 Mon Sep 17 00:00:00 2001 From: "cyril.tsui" Date: Sun, 24 Aug 2025 00:49:25 +0800 Subject: [PATCH 7/8] [mail tempalte] add po mail template --- .../mail/entity/MailTemplateRepository.kt | 2 + .../mail/service/MailTemplateService.kt | 118 ++++++++++++++++++ .../common/mail/web/MailTemplateController.kt | 19 +++ .../models/DownloadMailTemplateResponse.kt | 24 ++++ .../qc/entity/projection/QcResultInfo.kt | 3 + .../stock/entity/InventoryLotRepository.kt | 2 + .../stock/service/InventoryLotService.kt | 4 + 7 files changed, 172 insertions(+) create mode 100644 src/main/java/com/ffii/fpsms/modules/common/mail/web/models/DownloadMailTemplateResponse.kt diff --git a/src/main/java/com/ffii/fpsms/modules/common/mail/entity/MailTemplateRepository.kt b/src/main/java/com/ffii/fpsms/modules/common/mail/entity/MailTemplateRepository.kt index 8287e36..65fffbd 100644 --- a/src/main/java/com/ffii/fpsms/modules/common/mail/entity/MailTemplateRepository.kt +++ b/src/main/java/com/ffii/fpsms/modules/common/mail/entity/MailTemplateRepository.kt @@ -9,4 +9,6 @@ interface MailTemplateRepository : AbstractRepository { fun findAllByDeletedIsFalse(): List fun findByIdAndDeletedIsFalse(id: Serializable): MailTemplate? + + fun findByCodeAndDeletedIsFalse(code: String): MailTemplate? } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/common/mail/service/MailTemplateService.kt b/src/main/java/com/ffii/fpsms/modules/common/mail/service/MailTemplateService.kt index ffb1ea0..da156c3 100644 --- a/src/main/java/com/ffii/fpsms/modules/common/mail/service/MailTemplateService.kt +++ b/src/main/java/com/ffii/fpsms/modules/common/mail/service/MailTemplateService.kt @@ -2,13 +2,29 @@ package com.ffii.fpsms.modules.common.mail.service import com.ffii.fpsms.modules.common.mail.entity.MailTemplate import com.ffii.fpsms.modules.common.mail.entity.MailTemplateRepository +import com.ffii.fpsms.modules.common.mail.web.models.DownloadMailTemplateResponse import com.ffii.fpsms.modules.common.mail.web.models.MailTemplateRequest +import com.ffii.fpsms.modules.qc.service.QcResultService +import com.ffii.fpsms.modules.stock.entity.StockInLineRepository +import com.ffii.fpsms.modules.stock.service.InventoryLotService +import com.itextpdf.html2pdf.ConverterProperties +import com.itextpdf.html2pdf.HtmlConverter +import com.itextpdf.layout.font.FontProvider +import org.jsoup.Jsoup import org.springframework.stereotype.Service +import java.io.ByteArrayOutputStream +import java.math.BigDecimal +import java.time.format.DateTimeFormatter +import kotlin.jvm.optionals.getOrNull @Service open class MailTemplateService( private val mailTemplateRepository: MailTemplateRepository, + private val stockInLineRepository: StockInLineRepository, + private val inventoryLotService: InventoryLotService, + private val qcResultService: QcResultService ) { + val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd") fun allMailTemplates(): List { return mailTemplateRepository.findAllByDeletedIsFalse(); } @@ -17,6 +33,10 @@ open class MailTemplateService( return mailTemplateRepository.findByIdAndDeletedIsFalse(id); } + fun findByCode(code: String): MailTemplate? { + return mailTemplateRepository.findByCodeAndDeletedIsFalse(code); + } + fun saveMailTemplate(request: MailTemplateRequest): MailTemplate { val mailTemplate = request.id?.let { findById(it) } ?: MailTemplate() @@ -59,4 +79,102 @@ open class MailTemplateService( mailTemplateRepository.saveAll(mailTemplates) } + + fun getMailTemplateForStockInLine(stockInLineId: Long): DownloadMailTemplateResponse { + val emailTemplate = findByCode("RM-01") ?: throw NoSuchElementException("No RP-01 Email Template") + val stockInLine = stockInLineRepository.findById(stockInLineId).getOrNull() ?: throw NoSuchElementException("Cant find this stock in line.") + + // Value + val zero = BigDecimal.ZERO + val po = stockInLine.purchaseOrder + val pol = stockInLine.purchaseOrderLine + val item = stockInLine.item + val supplierName = po?.supplier?.name ?: "N/A" + val supplierEmail = if((po?.supplier?.contactEmail).isNullOrEmpty()) "N/A" else po?.supplier?.contactEmail + val dnDate = formatter.format(stockInLine.dnDate) ?: "N/A" + val poNo = po?.code ?: "N/A" + val supplierId = po?.supplier?.code ?: "N/A" // Id? + val itemNo = item?.code ?: "N/A" + val itemQty = (pol?.qty ?: zero).toString() + val uom = pol?.uom?.udfudesc ?: "N/A" + val planDnDate = po?.estimatedArrivalDate?.let { formatter.format(it) } ?: "N/A" + val unitPrice = (pol?.up ?: zero).toString() + val receivedCompany = po?.shop?.name ?: "N/A" + val lotNo = stockInLine.productLotNo ?: "N/A" + var qcDate = "N/A" + val qcResult = qcResultService.getAllQcResultInfoByStockInLineId(stockInLineId).let { qcResults -> + val filteredResult = qcResults + .groupBy { Pair(it.stockInLineId, it.qcItemId) } + .mapValues { (_, group) -> + group.maxByOrNull { it.recordDate } + } + .values + .filterNotNull() + .filter { !it.qcPassed } + if (filteredResult.isNotEmpty()) { + qcDate = formatter.format(filteredResult.maxOf { it.recordDate }) + + val tempDoc = Jsoup.parse("") + val element = tempDoc.appendElement("ul") + for (result in filteredResult) { + element.appendElement("li") + .text("${result.name} - ${result.description}") + } + tempDoc.outerHtml() + } else { + "N/A" + } + } ?: "N/A" + val acceptedQty = (stockInLine.acceptedQty ?: zero).minus(stockInLine.demandQty ?: zero).toString() + val rejectedQty = (stockInLine.demandQty ?: zero).toString() // reject? + val nonDelieveredQty = (zero).toString() + + // HTML + val to = supplierEmail + val toHtmlStr = Jsoup.parse("").appendElement("p").text("To: $to").outerHtml() + + val subject = (emailTemplate.subjectCht ?: "N/A") +// .replace("{supplierName}", supplierName) + .replace("{poNo}", poNo) + .replace("{itemNo}", itemNo) + val subjectHtmlStr = Jsoup.parse("").appendElement("p").text("Subject: $subject").outerHtml() + + val content = (emailTemplate.contentCht ?: "N/A") + .replace("{supplierName}", supplierName) + .replace("{dnDate}", dnDate) + .replace("{poNo}", poNo) + .replace("{supplierId}", supplierId) + .replace("{itemNo}", itemNo) + .replace("{itemQty}", itemQty) + .replace("{uom}", uom) + .replace("{planDnDate}", planDnDate) + .replace("{unitPrice}", unitPrice) + .replace("{receivedCompany}", receivedCompany) + .replace("{lotNo}", lotNo) + .replace("{qcResult}", qcResult) + .replace("{acceptedQty}", acceptedQty) + .replace("{nonDelieveredQty}", nonDelieveredQty) + .replace("{rejectedQty}", rejectedQty) + .replace("{qcDate}", qcDate) + val contentHtmlStr = Jsoup.parse("").appendElement("p").append("Content: $content").outerHtml() + + // Result + val resultHtmlStr = toHtmlStr + subjectHtmlStr + contentHtmlStr +// println(resultHtmlStr) + + val resultPdf = ByteArrayOutputStream() + val fp = FontProvider(); + fp.addStandardPdfFonts() + fp.addFont("/fonts/msjh_0.ttf") + val converterProperties = ConverterProperties() + converterProperties.apply { + fontProvider = fp + } + HtmlConverter.convertToPdf(resultHtmlStr, resultPdf, converterProperties) + + return DownloadMailTemplateResponse( + file = resultPdf.toByteArray(), + fileName = subject ?: "N/A" + ); + } } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/common/mail/web/MailTemplateController.kt b/src/main/java/com/ffii/fpsms/modules/common/mail/web/MailTemplateController.kt index f37cb33..63b80f2 100644 --- a/src/main/java/com/ffii/fpsms/modules/common/mail/web/MailTemplateController.kt +++ b/src/main/java/com/ffii/fpsms/modules/common/mail/web/MailTemplateController.kt @@ -3,10 +3,15 @@ package com.ffii.fpsms.modules.common.mail.web import com.ffii.fpsms.modules.common.mail.entity.MailTemplate import com.ffii.fpsms.modules.common.mail.web.models.MailTemplateRequest import com.ffii.fpsms.modules.common.mail.service.MailTemplateService +import org.springframework.http.HttpHeaders +import org.springframework.http.MediaType +import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.PostMapping import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RestController +import java.net.URLEncoder @RequestMapping("/mailTemplates") @RestController @@ -22,4 +27,18 @@ class MailTemplateController( fun saveMailTemplate(request: MailTemplateRequest): MailTemplate { return mailTemplateService.saveMailTemplate(request); } + + @GetMapping("/getMailTemplateForStockInLine/{stockInLineId}") + fun getMailTemplateForStockInLine(@PathVariable stockInLineId: Long): ResponseEntity { + val response = mailTemplateService.getMailTemplateForStockInLine(stockInLineId) + val headers = HttpHeaders().apply { + contentType = MediaType.APPLICATION_PDF +// add("Content-Disposition", "attachment; filename=${response.fileName}") + add("filename", "${response.fileName}.pdf") + } + + return ResponseEntity.ok() + .headers(headers) + .body(response.file) + } } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/common/mail/web/models/DownloadMailTemplateResponse.kt b/src/main/java/com/ffii/fpsms/modules/common/mail/web/models/DownloadMailTemplateResponse.kt new file mode 100644 index 0000000..920b313 --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/common/mail/web/models/DownloadMailTemplateResponse.kt @@ -0,0 +1,24 @@ +package com.ffii.fpsms.modules.common.mail.web.models + +data class DownloadMailTemplateResponse( + val file: ByteArray, + val fileName: String, +) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as DownloadMailTemplateResponse + + if (!file.contentEquals(other.file)) return false + if (fileName != other.fileName) return false + + return true + } + + override fun hashCode(): Int { + var result = file.contentHashCode() + result = 31 * result + fileName.hashCode() + return result + } +} diff --git a/src/main/java/com/ffii/fpsms/modules/qc/entity/projection/QcResultInfo.kt b/src/main/java/com/ffii/fpsms/modules/qc/entity/projection/QcResultInfo.kt index 0d6350c..670655a 100644 --- a/src/main/java/com/ffii/fpsms/modules/qc/entity/projection/QcResultInfo.kt +++ b/src/main/java/com/ffii/fpsms/modules/qc/entity/projection/QcResultInfo.kt @@ -1,6 +1,7 @@ package com.ffii.fpsms.modules.qc.entity.projection import org.springframework.beans.factory.annotation.Value +import java.time.LocalDateTime interface QcResultInfo { val id: Long @@ -19,4 +20,6 @@ interface QcResultInfo { val stockOutLineId: Long? val failQty: Double? val qcPassed: Boolean + @get:Value("#{target.modified}") + val recordDate: LocalDateTime } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/stock/entity/InventoryLotRepository.kt b/src/main/java/com/ffii/fpsms/modules/stock/entity/InventoryLotRepository.kt index f61518b..5fbe1fe 100644 --- a/src/main/java/com/ffii/fpsms/modules/stock/entity/InventoryLotRepository.kt +++ b/src/main/java/com/ffii/fpsms/modules/stock/entity/InventoryLotRepository.kt @@ -2,7 +2,9 @@ package com.ffii.fpsms.modules.stock.entity import com.ffii.core.support.AbstractRepository import org.springframework.stereotype.Repository +import java.io.Serializable @Repository interface InventoryLotRepository: AbstractRepository { + fun findByStockInLineIdAndDeletedFalse(stockInLineId: Serializable): InventoryLot? } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/stock/service/InventoryLotService.kt b/src/main/java/com/ffii/fpsms/modules/stock/service/InventoryLotService.kt index 80ffda4..170a342 100644 --- a/src/main/java/com/ffii/fpsms/modules/stock/service/InventoryLotService.kt +++ b/src/main/java/com/ffii/fpsms/modules/stock/service/InventoryLotService.kt @@ -20,6 +20,10 @@ open class InventoryLotService( private val inventoryLotRepository: InventoryLotRepository, ): AbstractBaseEntityService(jdbcDao, inventoryLotRepository) { + open fun findByStockInLineId(stockInLineId: Long): InventoryLot? { + return inventoryLotRepository.findByStockInLineIdAndDeletedFalse(stockInLineId) + } + @Throws(IOException::class) @Transactional open fun createOrUpdate(request: SaveInventoryRequest): MessageResponse { From 91fff4fddd3e53f20d7c7d51050eae16a30cbfb5 Mon Sep 17 00:00:00 2001 From: "cyril.tsui" Date: Mon, 25 Aug 2025 12:49:36 +0800 Subject: [PATCH 8/8] rename --- .../modules/stock/entity/EscalationLog.kt | 4 ++-- .../modules/stock/entity/EscalationLogInfo.kt | 20 +++++++++---------- .../stock/entity/EscalationLogRepository.kt | 4 ++-- .../stock/service/EscalationLogService.kt | 16 +++++++-------- .../web/model/SaveEscalationLogRequest.kt | 2 +- .../web/model/SaveEscalationLogResponse.kt | 8 ++++---- .../01_update_escalation_log.sql | 11 ++++++++++ 7 files changed, 38 insertions(+), 27 deletions(-) create mode 100644 src/main/resources/db/changelog/changes/20250825_01_cyril/01_update_escalation_log.sql diff --git a/src/main/java/com/ffii/fpsms/modules/stock/entity/EscalationLog.kt b/src/main/java/com/ffii/fpsms/modules/stock/entity/EscalationLog.kt index 3d5f173..4180652 100644 --- a/src/main/java/com/ffii/fpsms/modules/stock/entity/EscalationLog.kt +++ b/src/main/java/com/ffii/fpsms/modules/stock/entity/EscalationLog.kt @@ -14,8 +14,8 @@ import java.time.LocalDateTime open class EscalationLog : BaseEntity() { @NotNull @ManyToOne - @JoinColumn(name = "personInCharge", nullable = false, referencedColumnName = "id") - open var personInCharge: User? = null + @JoinColumn(name = "handlerId", nullable = false, referencedColumnName = "id") + open var handler: User? = null @Size(max = 100) @Column(name = "type", nullable = false, length = 100) diff --git a/src/main/java/com/ffii/fpsms/modules/stock/entity/EscalationLogInfo.kt b/src/main/java/com/ffii/fpsms/modules/stock/entity/EscalationLogInfo.kt index 88c928d..c11da8d 100644 --- a/src/main/java/com/ffii/fpsms/modules/stock/entity/EscalationLogInfo.kt +++ b/src/main/java/com/ffii/fpsms/modules/stock/entity/EscalationLogInfo.kt @@ -52,16 +52,16 @@ interface EscalationLogInfo { @get:Value("#{target.stockInLine?.item?.itemUoms.^[stockUnit == true && deleted == false]?.uom.udfudesc}") val stockUomDesc: String? - @get:Value("#{target.personInCharge?.id}") - val personInChargeId: Long? - @get:Value("#{target.personInCharge?.department} - #{target.personInCharge?.title} - #{target.personInCharge?.name}") - val personInCharge: String? - @get:Value("#{target.personInCharge?.name}") - val personInChargeName: String? - @get:Value("#{target.personInCharge?.title}") - val personInChargeTitle: String? - @get:Value("#{target.personInCharge?.department}") - val personInChargeDepartment: String? + @get:Value("#{target.handler?.id}") + val handlerId: Long? + @get:Value("#{target.handler?.department} - #{target.handler?.title} - #{target.handler?.name}") + val handler: String? + @get:Value("#{target.handler?.name}") + val handlerName: String? + @get:Value("#{target.handler?.title}") + val handlerTitle: String? + @get:Value("#{target.handler?.department}") + val handlerDepartment: String? // @get:Value("#{target.type}") // val escalationLevel: String diff --git a/src/main/java/com/ffii/fpsms/modules/stock/entity/EscalationLogRepository.kt b/src/main/java/com/ffii/fpsms/modules/stock/entity/EscalationLogRepository.kt index 4fe53bf..e3d2b00 100644 --- a/src/main/java/com/ffii/fpsms/modules/stock/entity/EscalationLogRepository.kt +++ b/src/main/java/com/ffii/fpsms/modules/stock/entity/EscalationLogRepository.kt @@ -6,10 +6,10 @@ import java.io.Serializable @Repository interface EscalationLogRepository: AbstractRepository { -// fun findAllByPersonInCharge(personInCharge: Long): List +// fun findAllByHandler(handler: Long): List fun findAllInfoByDeletedFalse(): List fun findAllInfoByDeletedFalseAndStockInLineIdIn(stockInLineIds: List): List - fun findAllInfoByDeletedFalseAndPersonInChargeIdIs(personInChargeId: Serializable): List + fun findAllInfoByDeletedFalseAndHandlerIdIs(handlerId: Serializable): List } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/stock/service/EscalationLogService.kt b/src/main/java/com/ffii/fpsms/modules/stock/service/EscalationLogService.kt index 51ff04a..de86a1a 100644 --- a/src/main/java/com/ffii/fpsms/modules/stock/service/EscalationLogService.kt +++ b/src/main/java/com/ffii/fpsms/modules/stock/service/EscalationLogService.kt @@ -37,14 +37,14 @@ open class EscalationLogService( open fun getLogsByUser(): List { val user = SecurityUtils.getUser().orElseThrow() - val logs = user.id?.let { escalationLogRepository.findAllInfoByDeletedFalseAndPersonInChargeIdIs(it) } ?: listOf() + val logs = user.id?.let { escalationLogRepository.findAllInfoByDeletedFalseAndHandlerIdIs(it) } ?: listOf() return logs } open fun saveEscalationLog(request: SaveEscalationLogRequest): SaveEscalationLogResponse{ val escalationLog = request.id?.let { escalationLogRepository.findById(it).getOrNull() } ?: EscalationLog() - val personInCharge = request.personInChargeId.let { userService.getUserById(it) } - if (personInCharge == null && escalationLog.personInCharge == null) { + val handler = request.handlerId.let { userService.getUserById(it) } + if (handler == null && escalationLog.handler == null) { throw NoSuchElementException("Person In Charge is null."); } @@ -57,7 +57,7 @@ open class EscalationLogService( val status = request.status.let { status -> EscalationLogStatus.entries.find{ it.value == status} } escalationLog.apply { - this.personInCharge = personInCharge + this.handler = handler type = request.type this.stockInLine = stockInLine this.stockOutLine = stockOutLine @@ -73,10 +73,10 @@ open class EscalationLogService( id = it.id, stockInLineId = it.stockInLine?.id, stockOutLineId = it.stockOutLine?.id, - personInChargeId = it.personInCharge?.id, - personInChargeName = it.personInCharge?.name, - personInChargeTitle = it.personInCharge?.title, - personInChargeDepartment = it.personInCharge?.department, + handlerId = it.handler?.id, + handlerName = it.handler?.name, + handlerTitle = it.handler?.title, + handlerDepartment = it.handler?.department, recordDate = it.recordDate, status = it.status?.value, reason = it.reason, diff --git a/src/main/java/com/ffii/fpsms/modules/stock/web/model/SaveEscalationLogRequest.kt b/src/main/java/com/ffii/fpsms/modules/stock/web/model/SaveEscalationLogRequest.kt index c3bf6ef..9b00c57 100644 --- a/src/main/java/com/ffii/fpsms/modules/stock/web/model/SaveEscalationLogRequest.kt +++ b/src/main/java/com/ffii/fpsms/modules/stock/web/model/SaveEscalationLogRequest.kt @@ -7,7 +7,7 @@ import java.time.LocalDateTime data class SaveEscalationLogRequest( val id: Long?, @field:NotNull(message = "Person In Charge cannot be empty") - val personInChargeId: Long, + val handlerId: Long, val type: String?, val stockInLineId: Long?, val stockOutLineId: Long?, diff --git a/src/main/java/com/ffii/fpsms/modules/stock/web/model/SaveEscalationLogResponse.kt b/src/main/java/com/ffii/fpsms/modules/stock/web/model/SaveEscalationLogResponse.kt index 557df4c..b1d924c 100644 --- a/src/main/java/com/ffii/fpsms/modules/stock/web/model/SaveEscalationLogResponse.kt +++ b/src/main/java/com/ffii/fpsms/modules/stock/web/model/SaveEscalationLogResponse.kt @@ -6,10 +6,10 @@ data class SaveEscalationLogResponse( val id: Long?, val stockInLineId: Long?, val stockOutLineId: Long?, - val personInChargeId: Long?, - val personInChargeName: String?, - val personInChargeTitle: String?, - val personInChargeDepartment: String?, + val handlerId: Long?, + val handlerName: String?, + val handlerTitle: String?, + val handlerDepartment: String?, val recordDate: LocalDateTime?, val status: String?, val reason: String?, diff --git a/src/main/resources/db/changelog/changes/20250825_01_cyril/01_update_escalation_log.sql b/src/main/resources/db/changelog/changes/20250825_01_cyril/01_update_escalation_log.sql new file mode 100644 index 0000000..74acd9f --- /dev/null +++ b/src/main/resources/db/changelog/changes/20250825_01_cyril/01_update_escalation_log.sql @@ -0,0 +1,11 @@ +-- liquibase formatted sql +-- changeset cyril:rename_escalation_log + +ALTER TABLE `escalation_log` + DROP FOREIGN KEY `FK_SUPERVISION_APPROVAL_LOG_ON_PERSONINCHARGE`; +ALTER TABLE `escalation_log` + CHANGE COLUMN `personInCharge` `handlerId` INT NOT NULL ; +ALTER TABLE `escalation_log` + ADD CONSTRAINT `FK_SUPERVISION_APPROVAL_LOG_ON_PERSONINCHARGE` + FOREIGN KEY (`handlerId`) + REFERENCES `user` (`id`);