Sfoglia il codice sorgente

Merge remote-tracking branch 'origin/master'

# Conflicts:
#	src/main/java/com/ffii/fpsms/modules/user/web/UserController.java
master
CANCERYS\kw093 1 giorno fa
parent
commit
9e49d332b4
36 ha cambiato i file con 569 aggiunte e 113 eliminazioni
  1. +2
    -0
      src/main/java/com/ffii/fpsms/modules/common/mail/entity/MailTemplateRepository.kt
  2. +118
    -0
      src/main/java/com/ffii/fpsms/modules/common/mail/service/MailTemplateService.kt
  3. +19
    -0
      src/main/java/com/ffii/fpsms/modules/common/mail/web/MailTemplateController.kt
  4. +24
    -0
      src/main/java/com/ffii/fpsms/modules/common/mail/web/models/DownloadMailTemplateResponse.kt
  5. +4
    -0
      src/main/java/com/ffii/fpsms/modules/qc/entity/QcResult.kt
  6. +14
    -7
      src/main/java/com/ffii/fpsms/modules/qc/entity/projection/QcResultInfo.kt
  7. +1
    -0
      src/main/java/com/ffii/fpsms/modules/qc/service/QcResultService.kt
  8. +1
    -0
      src/main/java/com/ffii/fpsms/modules/qc/web/model/SaveQcResultRequest.kt
  9. +14
    -7
      src/main/java/com/ffii/fpsms/modules/stock/entity/EscalationLog.kt
  10. +71
    -0
      src/main/java/com/ffii/fpsms/modules/stock/entity/EscalationLogInfo.kt
  11. +15
    -0
      src/main/java/com/ffii/fpsms/modules/stock/entity/EscalationLogRepository.kt
  12. +2
    -0
      src/main/java/com/ffii/fpsms/modules/stock/entity/InventoryLotRepository.kt
  13. +0
    -20
      src/main/java/com/ffii/fpsms/modules/stock/entity/SupervisionApprovalLogInfo.kt
  14. +0
    -11
      src/main/java/com/ffii/fpsms/modules/stock/entity/SupervisionApprovalLogRepository.kt
  15. +10
    -6
      src/main/java/com/ffii/fpsms/modules/stock/entity/projection/InventoryInfo.kt
  16. +8
    -4
      src/main/java/com/ffii/fpsms/modules/stock/entity/projection/InventoryLotLineInfo.kt
  17. +7
    -0
      src/main/java/com/ffii/fpsms/modules/stock/enums/EscalationLogEnum.kt
  18. +18
    -0
      src/main/java/com/ffii/fpsms/modules/stock/enums/EscalationLogEnumConverter.kt
  19. +88
    -0
      src/main/java/com/ffii/fpsms/modules/stock/service/EscalationLogService.kt
  20. +4
    -0
      src/main/java/com/ffii/fpsms/modules/stock/service/InventoryLotService.kt
  21. +1
    -0
      src/main/java/com/ffii/fpsms/modules/stock/service/StockInLineService.kt
  22. +0
    -38
      src/main/java/com/ffii/fpsms/modules/stock/service/SupervisionApprovalLogService.kt
  23. +40
    -0
      src/main/java/com/ffii/fpsms/modules/stock/web/EscalationLogController.kt
  24. +0
    -19
      src/main/java/com/ffii/fpsms/modules/stock/web/SupervisionApprovalLogController.kt
  25. +20
    -0
      src/main/java/com/ffii/fpsms/modules/stock/web/model/SaveEscalationLogRequest.kt
  26. +16
    -0
      src/main/java/com/ffii/fpsms/modules/stock/web/model/SaveEscalationLogResponse.kt
  27. +7
    -0
      src/main/java/com/ffii/fpsms/modules/stock/web/model/SearchEscalationLogRequest.kt
  28. +3
    -0
      src/main/java/com/ffii/fpsms/modules/user/entity/UserRepository.java
  29. +19
    -0
      src/main/java/com/ffii/fpsms/modules/user/entity/projections/UserCombo.java
  30. +5
    -0
      src/main/java/com/ffii/fpsms/modules/user/service/UserService.java
  31. +7
    -1
      src/main/java/com/ffii/fpsms/modules/user/web/UserController.java
  32. +6
    -0
      src/main/resources/db/changelog/changes/20250818_01_cyril/01_update_supervision_approval_log.sql
  33. +5
    -0
      src/main/resources/db/changelog/changes/20250818_01_cyril/02_update_supervision_approval_log.sql
  34. +5
    -0
      src/main/resources/db/changelog/changes/20250818_01_cyril/03_rename_supervision_approval_log.sql
  35. +4
    -0
      src/main/resources/db/changelog/changes/20250820_01_kelvin/01_update_qc_result.sql
  36. +11
    -0
      src/main/resources/db/changelog/changes/20250825_01_cyril/01_update_escalation_log.sql

+ 2
- 0
src/main/java/com/ffii/fpsms/modules/common/mail/entity/MailTemplateRepository.kt Vedi File

@@ -9,4 +9,6 @@ interface MailTemplateRepository : AbstractRepository<MailTemplate, Long> {
fun findAllByDeletedIsFalse(): List<MailTemplate>

fun findByIdAndDeletedIsFalse(id: Serializable): MailTemplate?

fun findByCodeAndDeletedIsFalse(code: String): MailTemplate?
}

+ 118
- 0
src/main/java/com/ffii/fpsms/modules/common/mail/service/MailTemplateService.kt Vedi File

@@ -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<MailTemplate> {
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"
);
}
}

+ 19
- 0
src/main/java/com/ffii/fpsms/modules/common/mail/web/MailTemplateController.kt Vedi File

@@ -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<ByteArray> {
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)
}
}

+ 24
- 0
src/main/java/com/ffii/fpsms/modules/common/mail/web/models/DownloadMailTemplateResponse.kt Vedi File

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

+ 4
- 0
src/main/java/com/ffii/fpsms/modules/qc/entity/QcResult.kt Vedi File

@@ -37,4 +37,8 @@ open class QcResult: BaseEntity<Long>() {

@Column(name = "remarks")
open var remarks: String? = null

@NotNull
@Column(name = "qcPassed")
open var qcPassed: Boolean = true
}

+ 14
- 7
src/main/java/com/ffii/fpsms/modules/qc/entity/projection/QcResultInfo.kt Vedi File

@@ -1,18 +1,25 @@
package com.ffii.fpsms.modules.qc.entity.projection

import org.springframework.beans.factory.annotation.Value
import java.time.LocalDateTime

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
@get:Value("#{target.modified}")
val recordDate: LocalDateTime
}

+ 1
- 0
src/main/java/com/ffii/fpsms/modules/qc/service/QcResultService.kt Vedi File

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


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

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

src/main/java/com/ffii/fpsms/modules/stock/entity/SupervisionApprovalLog.kt → src/main/java/com/ffii/fpsms/modules/stock/entity/EscalationLog.kt Vedi File

@@ -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<Long>() {
@Table(name = "escalation_log")
open class EscalationLog : BaseEntity<Long>() {
@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)
@NotNull
@Column(name = "type", nullable = false, length = 100)
open var type: String? = null

@@ -31,13 +32,19 @@ open class SupervisionApprovalLog : BaseEntity<Long>() {
@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
}

+ 71
- 0
src/main/java/com/ffii/fpsms/modules/stock/entity/EscalationLogInfo.kt Vedi File

@@ -0,0 +1,71 @@
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.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.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
val qcTotalCount: Int?;
val qcFailCount: Int?;
val reason: String?;
}

+ 15
- 0
src/main/java/com/ffii/fpsms/modules/stock/entity/EscalationLogRepository.kt Vedi File

@@ -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<EscalationLog, Long> {
// fun findAllByHandler(handler: Long): List<SupervisionApprovalLog>
fun findAllInfoByDeletedFalse(): List<EscalationLogInfo>

fun findAllInfoByDeletedFalseAndStockInLineIdIn(stockInLineIds: List<Serializable>): List<EscalationLogInfo>

fun findAllInfoByDeletedFalseAndHandlerIdIs(handlerId: Serializable): List<EscalationLogInfo>
}

+ 2
- 0
src/main/java/com/ffii/fpsms/modules/stock/entity/InventoryLotRepository.kt Vedi File

@@ -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<InventoryLot, Long> {
fun findByStockInLineIdAndDeletedFalse(stockInLineId: Serializable): InventoryLot?
}

+ 0
- 20
src/main/java/com/ffii/fpsms/modules/stock/entity/SupervisionApprovalLogInfo.kt Vedi File

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

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

@@ -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<SupervisionApprovalLog, Long> {
// fun findAllByPersonInCharge(personInCharge: Long): List<SupervisionApprovalLog>
fun findAllInfoByDeletedFalse(): List<SupervisionApprovalLogInfo>
}

+ 10
- 6
src/main/java/com/ffii/fpsms/modules/stock/entity/projection/InventoryInfo.kt Vedi File

@@ -14,17 +14,21 @@ 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.^[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?


+ 8
- 4
src/main/java/com/ffii/fpsms/modules/stock/entity/projection/InventoryLotLineInfo.kt Vedi File

@@ -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.^[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)}")
@get:Value("#{(target.inQty ?: 0) - (target.outQty ?: 0) - (target.holdQty ?: 0)}")
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)}")
@get:Value("#{target.inQty ?: 0}")
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)}")
@get:Value("#{(target.outQty ?: 0)}")
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)}")
@get:Value("#{(target.holdQty ?: 0)}")
var holdQty: BigDecimal?
@get:Value("#{(target.inQty ?: 0) - (target.outQty ?: 0) - (target.holdQty ?: 0)}")
val qtyPerSmallestUnit: BigDecimal?


+ 7
- 0
src/main/java/com/ffii/fpsms/modules/stock/enums/EscalationLogEnum.kt Vedi File

@@ -0,0 +1,7 @@
package com.ffii.fpsms.modules.stock.enums

enum class EscalationLogStatus(val value: String) {
PENDING("pending"),
ACCEPTED("accepted"),
REJECTED("rejected"),
}

+ 18
- 0
src/main/java/com/ffii/fpsms/modules/stock/enums/EscalationLogEnumConverter.kt Vedi File

@@ -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<EscalationLogStatus, String>{
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 }
}
}
}

+ 88
- 0
src/main/java/com/ffii/fpsms/modules/stock/service/EscalationLogService.kt Vedi File

@@ -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<EscalationLog, Long, EscalationLogRepository>(jdbcDao, escalationLogRepository) {
open fun getAllLogs(): List<EscalationLog> {
return escalationLogRepository.findAll()
}

open fun getLogsByStockInLines(stockInLineIds: List<Long>?): List<EscalationLogInfo> {
val logs = stockInLineIds?.let { escalationLogRepository.findAllInfoByDeletedFalseAndStockInLineIdIn(it) } ?: listOf()
return logs
}

open fun getLogsByStockInLine(stockInLineId: Long?): List<EscalationLogInfo> {
val logs = stockInLineId?.let { getLogsByStockInLines(listOf(it)) } ?: listOf()
return logs
}

open fun getLogsByUser(): List<EscalationLogInfo> {
val user = SecurityUtils.getUser().orElseThrow()
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 handler = request.handlerId.let { userService.getUserById(it) }
if (handler == null && escalationLog.handler == 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.handler = handler
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,
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,
)
}

return response
}
}

+ 4
- 0
src/main/java/com/ffii/fpsms/modules/stock/service/InventoryLotService.kt Vedi File

@@ -20,6 +20,10 @@ open class InventoryLotService(
private val inventoryLotRepository: InventoryLotRepository,
): AbstractBaseEntityService<InventoryLot, Long, InventoryLotRepository>(jdbcDao, inventoryLotRepository) {

open fun findByStockInLineId(stockInLineId: Long): InventoryLot? {
return inventoryLotRepository.findByStockInLineIdAndDeletedFalse(stockInLineId)
}

@Throws(IOException::class)
@Transactional
open fun createOrUpdate(request: SaveInventoryRequest): MessageResponse {


+ 1
- 0
src/main/java/com/ffii/fpsms/modules/stock/service/StockInLineService.kt Vedi File

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


+ 0
- 38
src/main/java/com/ffii/fpsms/modules/stock/service/SupervisionApprovalLogService.kt Vedi File

@@ -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<SupervisionApprovalLog, Long, SupervisionApprovalLogRepository>(jdbcDao, supervisionApprovalLogRepository) {
open fun getAllLogs(): List<SupervisionApprovalLog> {
return supervisionApprovalLogRepository.findAll()
}

open fun getStockInLog(): List<SupervisionApprovalLog> {
val stockInLog = getAllLogs().filter {
it.stockInLine != null
}
return stockInLog
}
open fun getLogByUser(): List<SupervisionApprovalLogInfo> {
val user = SecurityUtils.getUser().orElseThrow()
val filterLog = supervisionApprovalLogRepository
.findAllInfoByDeletedFalse()
// .findAll()
// .findAllByPersonInCharge(user.id!!).filter {
// it.stockInLine != null
// }
println("filterLog")
println(filterLog)
return filterLog
}
}

+ 40
- 0
src/main/java/com/ffii/fpsms/modules/stock/web/EscalationLogController.kt Vedi File

@@ -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<EscalationLogInfo> {
return escalationLogService.getLogsByStockInLines(request.stockInLineIds);
}

@GetMapping("/stockInLine")
fun getLogsByStockInLine(@ModelAttribute request: SearchEscalationLogRequest): List<EscalationLogInfo> {
return escalationLogService.getLogsByStockInLine(request.stockInLineId);
}

@GetMapping("/user")
fun getLogsByUser(): List<EscalationLogInfo> {
return escalationLogService.getLogsByUser();
}

@PostMapping("/save")
fun saveEscalationLog(@Valid @RequestBody request: SaveEscalationLogRequest): SaveEscalationLogResponse {
return escalationLogService.saveEscalationLog(request);
}
}

+ 0
- 19
src/main/java/com/ffii/fpsms/modules/stock/web/SupervisionApprovalLogController.kt Vedi File

@@ -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<SupervisionApprovalLogInfo> {
return supervisionApprovalLogService.getLogByUser()
}
}

+ 20
- 0
src/main/java/com/ffii/fpsms/modules/stock/web/model/SaveEscalationLogRequest.kt Vedi File

@@ -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 handlerId: 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?,
)

+ 16
- 0
src/main/java/com/ffii/fpsms/modules/stock/web/model/SaveEscalationLogResponse.kt Vedi File

@@ -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 handlerId: Long?,
val handlerName: String?,
val handlerTitle: String?,
val handlerDepartment: String?,
val recordDate: LocalDateTime?,
val status: String?,
val reason: String?,
)

+ 7
- 0
src/main/java/com/ffii/fpsms/modules/stock/web/model/SearchEscalationLogRequest.kt Vedi File

@@ -0,0 +1,7 @@
package com.ffii.fpsms.modules.stock.web.model

data class SearchEscalationLogRequest(
val stockInLineIds: List<Long>?,
val stockInLineId: Long?,
val userId: Long?,
)

+ 3
- 0
src/main/java/com/ffii/fpsms/modules/user/entity/UserRepository.java Vedi File

@@ -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<User, Long> {
List<User> findByName(@Param("name") String name);

Optional<User> findByUsernameAndDeletedFalse(String username);

List<UserCombo> findUserComboByTitleNotNullAndDepartmentNotNull();
}

+ 19
- 0
src/main/java/com/ffii/fpsms/modules/user/entity/projections/UserCombo.java Vedi File

@@ -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();
}

+ 5
- 0
src/main/java/com/ffii/fpsms/modules/user/service/UserService.java Vedi File

@@ -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<User, Long, UserRepos
instance = save(instance);
return randomPassword;
}

public List<UserCombo> getEscalationCombo() {
return userRepository.findUserComboByTitleNotNullAndDepartmentNotNull();
}
}

+ 7
- 1
src/main/java/com/ffii/fpsms/modules/user/web/UserController.java Vedi File

@@ -5,6 +5,8 @@ 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;
import org.springframework.http.HttpStatus;
@@ -38,7 +40,6 @@ import com.ffii.fpsms.modules.user.req.NewUserReq;
import com.ffii.fpsms.modules.user.req.SearchUserReq;
import com.ffii.fpsms.modules.user.req.UpdateUserReq;
import com.ffii.fpsms.modules.user.service.UserService;
import com.ffii.fpsms.modules.user.service.pojo.UserRecord;
import com.ffii.fpsms.modules.user.service.res.LoadUserRes;

import jakarta.validation.Valid;
@@ -71,6 +72,11 @@ public class UserController{
return ResponseEntity.ok(userService.search(req));
}

@GetMapping("/escalation-combo")
public ResponseEntity<List<UserCombo>> escalationCombo() {
return ResponseEntity.ok(userService.getEscalationCombo());
}

@GetMapping("/name-list")
public ResponseEntity<List<Map<String, Object>>> namelist() {
SearchUserReq req = new SearchUserReq();


+ 6
- 0
src/main/resources/db/changelog/changes/20250818_01_cyril/01_update_supervision_approval_log.sql Vedi File

@@ -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`;

+ 5
- 0
src/main/resources/db/changelog/changes/20250818_01_cyril/02_update_supervision_approval_log.sql Vedi File

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

+ 5
- 0
src/main/resources/db/changelog/changes/20250818_01_cyril/03_rename_supervision_approval_log.sql Vedi File

@@ -0,0 +1,5 @@
-- liquibase formatted sql
-- changeset cyril:rename_supervision_approval_log

ALTER TABLE `supervision_approval_log`
RENAME TO `escalation_log` ;

+ 4
- 0
src/main/resources/db/changelog/changes/20250820_01_kelvin/01_update_qc_result.sql Vedi File

@@ -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`;

+ 11
- 0
src/main/resources/db/changelog/changes/20250825_01_cyril/01_update_escalation_log.sql Vedi File

@@ -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`);

Caricamento…
Annulla
Salva