From e592845c41f0c19b2fbc14246e8d0529a398a38f Mon Sep 17 00:00:00 2001 From: "vluk@2fi-solutions.com.hk" Date: Mon, 23 Feb 2026 15:48:19 +0800 Subject: [PATCH] no message --- .../modules/report/service/ReportService.kt | 72 ++++--------------- .../modules/report/web/ReportController.kt | 19 ++++- 2 files changed, 32 insertions(+), 59 deletions(-) diff --git a/src/main/java/com/ffii/fpsms/modules/report/service/ReportService.kt b/src/main/java/com/ffii/fpsms/modules/report/service/ReportService.kt index a579b55..d46faa7 100644 --- a/src/main/java/com/ffii/fpsms/modules/report/service/ReportService.kt +++ b/src/main/java/com/ffii/fpsms/modules/report/service/ReportService.kt @@ -6,11 +6,9 @@ import net.sf.jasperreports.engine.data.JRMapCollectionDataSource import java.io.ByteArrayOutputStream import java.io.InputStream import com.ffii.core.support.JdbcDao -import org.apache.poi.ss.usermodel.Cell -import org.apache.poi.ss.usermodel.CellType -import org.apache.poi.ss.usermodel.Row -import org.apache.poi.ss.usermodel.Sheet -import org.apache.poi.xssf.usermodel.XSSFWorkbook +import net.sf.jasperreports.engine.export.ooxml.JRXlsxExporter +import net.sf.jasperreports.export.SimpleExporterInput +import net.sf.jasperreports.export.SimpleOutputStreamExporterOutput @Service open class ReportService( @@ -933,61 +931,19 @@ fun searchMaterialStockOutTraceabilityReport( return jdbcDao.queryForList("$finalSql ORDER BY itemNo, lotNo", args) } /** - * Builds Stock Balance Report as Excel (.xlsx) from the same data used for the PDF report. + * Compiles and fills a Jasper Report, then exports to Excel (.xlsx). Same layout/columns as the report template. */ - fun createStockBalanceExcel(dataList: List>): ByteArray { - val workbook = XSSFWorkbook() - val sheet: Sheet = workbook.createSheet("Stock Balance") - val columns = listOf( - "itemNo" to "Item No", - "itemName" to "Item Name", - "unitOfMeasure" to "Unit", - "lotNo" to "Lot No", - "expiryDate" to "Expiry Date", - "openingBalance" to "Opening Balance", - "cumStockIn" to "Cum Stock In", - "cumStockOut" to "Cum Stock Out", - "currentBalance" to "Current Balance", - "storeLocation" to "Store Location", - "lastInDate" to "Last In Date", - "lastOutDate" to "Last Out Date", - "lastMovementDate" to "Last Movement Date", - "misInputAndLost" to "Mis Input And Lost", - "defectiveGoods" to "Defective Goods", - "variance" to "Variance", - "cumStockInByPurchaseOrder" to "Cum Stock In (PO)", - "cumStockInByJobOrder" to "Cum Stock In (JO)", - "cumStockInByStockTake" to "Cum Stock In (Stock Take)", - "cumStockInByAdj" to "Cum Stock In (Adj)", - "cumStockOutMissQty" to "Cum Stock Out Miss", - "cumStockOutBadQty" to "Cum Stock Out Bad", - "cumStockOutAdj" to "Cum Stock Out Adj", - "cumStockOutAdjStockTake" to "Cum Stock Out Adj Stock Take", - "cumStockOutStockTake" to "Cum Stock Out Stock Take", - "cumStockOutByDO" to "Cum Stock Out (DO)", - "cumStockOutByJO" to "Cum Stock Out (JO)", - "cumStockOutByConsumable" to "Cum Stock Out (Consumable)" - ) - val headerRow: Row = sheet.createRow(0) - columns.forEachIndexed { index, (_, label) -> - val cell: Cell = headerRow.createCell(index, CellType.STRING) - cell.setCellValue(label) - } - dataList.forEachIndexed { rowIndex, rowData -> - val row: Row = sheet.createRow(rowIndex + 1) - columns.forEachIndexed { colIndex, (key, _) -> - val cell: Cell = row.createCell(colIndex) - val value = rowData[key] - when (value) { - null -> cell.setCellValue("") - is Number -> cell.setCellValue(value.toDouble()) - else -> cell.setCellValue(value.toString()) - } - } - } - columns.indices.forEach { sheet.autoSizeColumn(it) } + fun createExcelResponse(templatePath: String, params: Map, dataList: List>): ByteArray { + val stream = this::class.java.getResourceAsStream(templatePath) + ?: throw RuntimeException("Report template not found: $templatePath") + val jasperReport = JasperCompileManager.compileReport(stream) + val dataSource = JRMapCollectionDataSource(dataList) + val jasperPrint = JasperFillManager.fillReport(jasperReport, params, dataSource) val out = ByteArrayOutputStream() - workbook.use { it.write(out) } + val exporter = JRXlsxExporter() + exporter.setExporterInput(SimpleExporterInput(jasperPrint)) + exporter.setExporterOutput(SimpleOutputStreamExporterOutput(out)) + exporter.exportReport() return out.toByteArray() } diff --git a/src/main/java/com/ffii/fpsms/modules/report/web/ReportController.kt b/src/main/java/com/ffii/fpsms/modules/report/web/ReportController.kt index 35e5872..710fdac 100644 --- a/src/main/java/com/ffii/fpsms/modules/report/web/ReportController.kt +++ b/src/main/java/com/ffii/fpsms/modules/report/web/ReportController.kt @@ -268,6 +268,19 @@ class ReportController( @RequestParam(required = false) lastOutDateStart: String?, @RequestParam(required = false) lastOutDateEnd: String? ): ResponseEntity { + val parameters = mutableMapOf() + parameters["stockCategory"] = stockCategory ?: "All" + parameters["itemNo"] = itemCode ?: "All" + parameters["reportDate"] = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")) + parameters["reportTime"] = LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")) + parameters["storeLocation"] = storeLocation ?: "" + parameters["balanceFilterStart"] = balanceFilterStart ?: "" + parameters["balanceFilterEnd"] = balanceFilterEnd ?: "" + parameters["lastInDateStart"] = lastInDateStart ?: "" + parameters["lastInDateEnd"] = lastInDateEnd ?: "" + parameters["lastOutDateStart"] = lastOutDateStart ?: "" + parameters["lastOutDateEnd"] = lastOutDateEnd ?: "" + val dbData = reportService.searchStockBalanceReport( stockCategory, itemCode, @@ -280,7 +293,11 @@ class ReportController( lastOutDateEnd ) - val excelBytes = reportService.createStockBalanceExcel(dbData) + val excelBytes = reportService.createExcelResponse( + "/jasper/StockBalanceReport.jrxml", + parameters, + dbData + ) val headers = HttpHeaders().apply { contentType = MediaType.parseMediaType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")