From 1ec389acfe015a832ad4b1f2209ccec1917d1ff7 Mon Sep 17 00:00:00 2001 From: "cyril.tsui" Date: Sun, 24 Aug 2025 00:49:25 +0800 Subject: [PATCH] [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 {