|
|
|
@@ -3,11 +3,33 @@ package com.ffii.fpsms.modules.report.web |
|
|
|
import net.sf.jasperreports.engine.* |
|
|
|
import org.springframework.http.* |
|
|
|
import org.springframework.web.bind.annotation.* |
|
|
|
import java.io.ByteArrayOutputStream |
|
|
|
import java.time.LocalDate |
|
|
|
import java.time.LocalTime |
|
|
|
import java.time.format.DateTimeFormatter |
|
|
|
import com.ffii.fpsms.modules.report.service.SemiFGProductionAnalysisReportService |
|
|
|
import com.ffii.fpsms.modules.report.service.ReportService |
|
|
|
import org.apache.poi.ss.usermodel.BorderStyle |
|
|
|
import org.apache.poi.ss.usermodel.FillPatternType |
|
|
|
import org.apache.poi.ss.usermodel.HorizontalAlignment |
|
|
|
import org.apache.poi.ss.usermodel.IndexedColors |
|
|
|
import org.apache.poi.ss.util.WorkbookUtil |
|
|
|
import org.apache.poi.ss.usermodel.VerticalAlignment |
|
|
|
import org.apache.poi.ss.usermodel.DataValidationConstraint |
|
|
|
import org.apache.poi.ss.usermodel.DataValidationHelper |
|
|
|
import org.apache.poi.ss.usermodel.SheetVisibility |
|
|
|
import org.apache.poi.ss.util.CellRangeAddress |
|
|
|
import org.apache.poi.ss.util.CellRangeAddressList |
|
|
|
import org.apache.poi.ss.util.CellReference |
|
|
|
import org.apache.poi.xddf.usermodel.chart.AxisPosition |
|
|
|
import org.apache.poi.xddf.usermodel.chart.BarDirection |
|
|
|
import org.apache.poi.xddf.usermodel.chart.ChartTypes |
|
|
|
import org.apache.poi.xddf.usermodel.chart.LegendPosition |
|
|
|
import org.apache.poi.xddf.usermodel.chart.XDDFBarChartData |
|
|
|
import org.apache.poi.xddf.usermodel.chart.XDDFDataSourcesFactory |
|
|
|
import org.apache.poi.xssf.usermodel.XSSFClientAnchor |
|
|
|
import org.apache.poi.xssf.usermodel.XSSFWorkbook |
|
|
|
import kotlin.math.roundToLong |
|
|
|
|
|
|
|
@RestController |
|
|
|
@RequestMapping("/report") |
|
|
|
@@ -50,20 +72,382 @@ class SemiFGProductionAnalysisReportController( |
|
|
|
) |
|
|
|
|
|
|
|
val pdfBytes = reportService.createPdfResponse( |
|
|
|
"/jasper/SemiFGProductionAnalysisReport.jrxml", |
|
|
|
parameters, |
|
|
|
"/jasper/SemiFGProductionAnalysisReport.jrxml", |
|
|
|
parameters, |
|
|
|
dbData |
|
|
|
) |
|
|
|
|
|
|
|
val headers = HttpHeaders().apply { |
|
|
|
contentType = MediaType.APPLICATION_PDF |
|
|
|
setContentDispositionFormData("attachment", "SemiFGProductionAnalysisReport.pdf") |
|
|
|
set("filename", "SemiFGProductionAnalysisReport.pdf") |
|
|
|
set("filename", "SemiFGProductionAnalysisReport.pdf") |
|
|
|
} |
|
|
|
|
|
|
|
return ResponseEntity(pdfBytes, headers, HttpStatus.OK) |
|
|
|
} |
|
|
|
|
|
|
|
@GetMapping("/print-semi-fg-production-analysis-excel") |
|
|
|
fun exportSemiFGProductionAnalysisReportExcel( |
|
|
|
@RequestParam(required = false) stockCategory: String?, |
|
|
|
@RequestParam(required = false) stockSubCategory: String?, |
|
|
|
@RequestParam(required = false) itemCode: String?, |
|
|
|
@RequestParam(required = false) year: String?, |
|
|
|
@RequestParam(required = false) lastOutDateStart: String?, |
|
|
|
@RequestParam(required = false) lastOutDateEnd: String? |
|
|
|
): ResponseEntity<ByteArray> { |
|
|
|
val dbData = semiFGProductionAnalysisReportService.searchSemiFGProductionAnalysisReport( |
|
|
|
stockCategory, |
|
|
|
stockSubCategory, |
|
|
|
itemCode, |
|
|
|
year, |
|
|
|
lastOutDateStart, |
|
|
|
lastOutDateEnd |
|
|
|
) |
|
|
|
|
|
|
|
val excelBytes = createSemiFGProductionAnalysisExcel( |
|
|
|
dbData = dbData, |
|
|
|
reportTitle = "成品/半成品生產分析報告", |
|
|
|
year = year |
|
|
|
) |
|
|
|
|
|
|
|
val headers = HttpHeaders().apply { |
|
|
|
contentType = MediaType.parseMediaType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") |
|
|
|
setContentDispositionFormData("attachment", "SemiFGProductionAnalysisReport.xlsx") |
|
|
|
set("filename", "SemiFGProductionAnalysisReport.xlsx") |
|
|
|
} |
|
|
|
|
|
|
|
return ResponseEntity(excelBytes, headers, HttpStatus.OK) |
|
|
|
} |
|
|
|
|
|
|
|
private fun createSemiFGProductionAnalysisExcel( |
|
|
|
dbData: List<Map<String, Any>>, |
|
|
|
reportTitle: String, |
|
|
|
year: String? |
|
|
|
): ByteArray { |
|
|
|
val workbook = XSSFWorkbook() |
|
|
|
val safeSheetName = WorkbookUtil.createSafeSheetName(reportTitle) |
|
|
|
val sheet = workbook.createSheet(safeSheetName) |
|
|
|
val totalColumns = 16 |
|
|
|
var rowIndex = 0 |
|
|
|
|
|
|
|
val titleStyle = workbook.createCellStyle().apply { |
|
|
|
alignment = HorizontalAlignment.CENTER |
|
|
|
verticalAlignment = VerticalAlignment.CENTER |
|
|
|
} |
|
|
|
val titleFont = workbook.createFont().apply { |
|
|
|
bold = true |
|
|
|
fontHeightInPoints = 16 |
|
|
|
} |
|
|
|
titleStyle.setFont(titleFont) |
|
|
|
|
|
|
|
val infoStyle = workbook.createCellStyle().apply { |
|
|
|
alignment = HorizontalAlignment.LEFT |
|
|
|
verticalAlignment = VerticalAlignment.CENTER |
|
|
|
} |
|
|
|
|
|
|
|
val headerStyle = workbook.createCellStyle().apply { |
|
|
|
alignment = HorizontalAlignment.CENTER |
|
|
|
verticalAlignment = VerticalAlignment.CENTER |
|
|
|
fillForegroundColor = IndexedColors.GREY_25_PERCENT.index |
|
|
|
fillPattern = FillPatternType.SOLID_FOREGROUND |
|
|
|
borderTop = BorderStyle.THIN |
|
|
|
borderBottom = BorderStyle.THIN |
|
|
|
borderLeft = BorderStyle.THIN |
|
|
|
borderRight = BorderStyle.THIN |
|
|
|
} |
|
|
|
val headerFont = workbook.createFont().apply { bold = true } |
|
|
|
headerStyle.setFont(headerFont) |
|
|
|
|
|
|
|
val textStyle = workbook.createCellStyle().apply { |
|
|
|
alignment = HorizontalAlignment.LEFT |
|
|
|
verticalAlignment = VerticalAlignment.CENTER |
|
|
|
borderTop = BorderStyle.THIN |
|
|
|
borderBottom = BorderStyle.THIN |
|
|
|
borderLeft = BorderStyle.THIN |
|
|
|
borderRight = BorderStyle.THIN |
|
|
|
} |
|
|
|
|
|
|
|
val numberStyle = workbook.createCellStyle().apply { |
|
|
|
alignment = HorizontalAlignment.RIGHT |
|
|
|
verticalAlignment = VerticalAlignment.CENTER |
|
|
|
borderTop = BorderStyle.THIN |
|
|
|
borderBottom = BorderStyle.THIN |
|
|
|
borderLeft = BorderStyle.THIN |
|
|
|
borderRight = BorderStyle.THIN |
|
|
|
dataFormat = workbook.creationHelper.createDataFormat().getFormat("#,##0") |
|
|
|
} |
|
|
|
|
|
|
|
val dashStyle = workbook.createCellStyle().apply { |
|
|
|
alignment = HorizontalAlignment.RIGHT |
|
|
|
verticalAlignment = VerticalAlignment.CENTER |
|
|
|
borderTop = BorderStyle.THIN |
|
|
|
borderBottom = BorderStyle.THIN |
|
|
|
borderLeft = BorderStyle.THIN |
|
|
|
borderRight = BorderStyle.THIN |
|
|
|
} |
|
|
|
|
|
|
|
val titleRow = sheet.createRow(rowIndex++) |
|
|
|
titleRow.heightInPoints = 28f |
|
|
|
val titleCell = titleRow.createCell(0) |
|
|
|
titleCell.setCellValue(reportTitle) |
|
|
|
titleCell.cellStyle = titleStyle |
|
|
|
sheet.addMergedRegion(CellRangeAddress(0, 0, 0, totalColumns - 1)) |
|
|
|
|
|
|
|
val infoRow = sheet.createRow(rowIndex++) |
|
|
|
val reportDateCell = infoRow.createCell(0) |
|
|
|
reportDateCell.setCellValue("報告日期:${LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))}") |
|
|
|
reportDateCell.cellStyle = infoStyle |
|
|
|
val reportTimeCell = infoRow.createCell(6) |
|
|
|
reportTimeCell.setCellValue("報告時間:${LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss"))}") |
|
|
|
reportTimeCell.cellStyle = infoStyle |
|
|
|
val reportYearCell = infoRow.createCell(11) |
|
|
|
reportYearCell.setCellValue("年份:${year?.takeIf { it.isNotBlank() && it != "All" } ?: "All"}") |
|
|
|
reportYearCell.cellStyle = infoStyle |
|
|
|
sheet.addMergedRegion(CellRangeAddress(1, 1, 0, 5)) |
|
|
|
sheet.addMergedRegion(CellRangeAddress(1, 1, 6, 10)) |
|
|
|
sheet.addMergedRegion(CellRangeAddress(1, 1, 11, 15)) |
|
|
|
|
|
|
|
rowIndex++ // spacer row |
|
|
|
|
|
|
|
val headers = listOf( |
|
|
|
"貨品編號", |
|
|
|
"貨品名稱", |
|
|
|
"單位", |
|
|
|
"一月", |
|
|
|
"二月", |
|
|
|
"三月", |
|
|
|
"四月", |
|
|
|
"五月", |
|
|
|
"六月", |
|
|
|
"七月", |
|
|
|
"八月", |
|
|
|
"九月", |
|
|
|
"十月", |
|
|
|
"十一月", |
|
|
|
"十二月", |
|
|
|
"總和" |
|
|
|
) |
|
|
|
|
|
|
|
val headerRow = sheet.createRow(rowIndex++) |
|
|
|
headers.forEachIndexed { index, name -> |
|
|
|
val cell = headerRow.createCell(index) |
|
|
|
cell.setCellValue(name) |
|
|
|
cell.cellStyle = headerStyle |
|
|
|
} |
|
|
|
|
|
|
|
if (dbData.isEmpty()) { |
|
|
|
val dataRow = sheet.createRow(rowIndex++) |
|
|
|
setTextCell(dataRow, 0, "-", textStyle) |
|
|
|
setTextCell(dataRow, 1, "-", textStyle) |
|
|
|
setTextCell(dataRow, 2, "-", textStyle) |
|
|
|
for (col in 3 until totalColumns) { |
|
|
|
setDashCell(dataRow, col, dashStyle) |
|
|
|
} |
|
|
|
} else { |
|
|
|
dbData.forEach { row -> |
|
|
|
val dataRow = sheet.createRow(rowIndex++) |
|
|
|
setTextCell(dataRow, 0, row["itemNo"]?.toString()?.ifBlank { "-" } ?: "-", textStyle) |
|
|
|
setTextCell(dataRow, 1, row["itemName"]?.toString()?.ifBlank { "-" } ?: "-", textStyle) |
|
|
|
setTextCell(dataRow, 2, row["unitOfMeasure"]?.toString()?.ifBlank { "-" } ?: "-", textStyle) |
|
|
|
setNumberCell(dataRow, 3, row["qtyJan"], numberStyle, dashStyle) |
|
|
|
setNumberCell(dataRow, 4, row["qtyFeb"], numberStyle, dashStyle) |
|
|
|
setNumberCell(dataRow, 5, row["qtyMar"], numberStyle, dashStyle) |
|
|
|
setNumberCell(dataRow, 6, row["qtyApr"], numberStyle, dashStyle) |
|
|
|
setNumberCell(dataRow, 7, row["qtyMay"], numberStyle, dashStyle) |
|
|
|
setNumberCell(dataRow, 8, row["qtyJun"], numberStyle, dashStyle) |
|
|
|
setNumberCell(dataRow, 9, row["qtyJul"], numberStyle, dashStyle) |
|
|
|
setNumberCell(dataRow, 10, row["qtyAug"], numberStyle, dashStyle) |
|
|
|
setNumberCell(dataRow, 11, row["qtySep"], numberStyle, dashStyle) |
|
|
|
setNumberCell(dataRow, 12, row["qtyOct"], numberStyle, dashStyle) |
|
|
|
setNumberCell(dataRow, 13, row["qtyNov"], numberStyle, dashStyle) |
|
|
|
setNumberCell(dataRow, 14, row["qtyDec"], numberStyle, dashStyle) |
|
|
|
setNumberCell(dataRow, 15, row["totalProductionQty"], numberStyle, dashStyle) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
val widths = intArrayOf(18, 24, 12, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 12) |
|
|
|
widths.forEachIndexed { idx, width -> sheet.setColumnWidth(idx, width * 256) } |
|
|
|
|
|
|
|
addSemiFGProductionChartSheets(workbook, dbData) |
|
|
|
|
|
|
|
val output = ByteArrayOutputStream() |
|
|
|
workbook.use { it.write(output) } |
|
|
|
return output.toByteArray() |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Adds a visible chart sheet with: |
|
|
|
* - Cell B1 dropdown to choose 貨品編號 or 「全部」(sum of all items) |
|
|
|
* - Formulas that pull monthly qty from a very-hidden data sheet |
|
|
|
* - Column bar chart bound to those formula cells (updates when selection changes) |
|
|
|
*/ |
|
|
|
private fun addSemiFGProductionChartSheets(workbook: XSSFWorkbook, dbData: List<Map<String, Any>>) { |
|
|
|
val monthKeys = listOf( |
|
|
|
"qtyJan", "qtyFeb", "qtyMar", "qtyApr", "qtyMay", "qtyJun", |
|
|
|
"qtyJul", "qtyAug", "qtySep", "qtyOct", "qtyNov", "qtyDec", |
|
|
|
) |
|
|
|
val monthLabels = listOf( |
|
|
|
"一月", "二月", "三月", "四月", "五月", "六月", |
|
|
|
"七月", "八月", "九月", "十月", "十一月", "十二月", |
|
|
|
) |
|
|
|
|
|
|
|
val dataSheetName = WorkbookUtil.createSafeSheetName("圖表數據") |
|
|
|
val dataSheet = workbook.createSheet(dataSheetName) |
|
|
|
|
|
|
|
val headerRow = dataSheet.createRow(0) |
|
|
|
headerRow.createCell(0).setCellValue("貨品編號") |
|
|
|
monthLabels.forEachIndexed { i, label -> |
|
|
|
headerRow.createCell(1 + i).setCellValue(label) |
|
|
|
} |
|
|
|
|
|
|
|
dbData.forEachIndexed { idx, row -> |
|
|
|
val r = dataSheet.createRow(1 + idx) |
|
|
|
r.createCell(0).setCellValue(row["itemNo"]?.toString() ?: "") |
|
|
|
monthKeys.forEachIndexed { mi, key -> |
|
|
|
r.createCell(1 + mi).setCellValue(parseNumericForExcelChart(row[key])) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
val zColIndex = CellReference.convertColStringToIndex("Z") |
|
|
|
val zHeader = dataSheet.getRow(0) ?: dataSheet.createRow(0) |
|
|
|
zHeader.createCell(zColIndex).setCellValue("全部") |
|
|
|
dbData.forEachIndexed { idx, row -> |
|
|
|
val r = dataSheet.getRow(1 + idx) ?: dataSheet.createRow(1 + idx) |
|
|
|
r.createCell(zColIndex).setCellValue(row["itemNo"]?.toString() ?: "") |
|
|
|
} |
|
|
|
|
|
|
|
val lastDataRow1Based = 1 + dbData.size |
|
|
|
val zListEnd1Based = 1 + dbData.size |
|
|
|
|
|
|
|
val chartSheetName = WorkbookUtil.createSafeSheetName("生產圖表") |
|
|
|
val chartSheet = workbook.createSheet(chartSheetName) |
|
|
|
|
|
|
|
val labelR = chartSheet.createRow(0) |
|
|
|
labelR.createCell(0).setCellValue("選擇貨品編號") |
|
|
|
val filterCell = labelR.createCell(1) |
|
|
|
val defaultItemNo = dbData.firstOrNull()?.get("itemNo")?.toString()?.trim() |
|
|
|
filterCell.setCellValue(defaultItemNo?.takeIf { it.isNotBlank() } ?: "全部") |
|
|
|
|
|
|
|
val dvHelper: DataValidationHelper = chartSheet.dataValidationHelper |
|
|
|
val listFormula = "'$dataSheetName'!\$Z\$1:\$Z\$$zListEnd1Based" |
|
|
|
val constraint: DataValidationConstraint = dvHelper.createFormulaListConstraint(listFormula) |
|
|
|
val regions = CellRangeAddressList(0, 0, 1, 1) |
|
|
|
val validation = dvHelper.createValidation(constraint, regions) |
|
|
|
validation.showErrorBox = true |
|
|
|
validation.createErrorBox("無效選擇", "請從清單中選擇貨品編號或「全部」。") |
|
|
|
validation.showPromptBox = true |
|
|
|
validation.createPromptBox("篩選", "選擇貨品編號檢視該品項各月數量;選擇「全部」顯示所有品項加總。") |
|
|
|
chartSheet.addValidationData(validation) |
|
|
|
|
|
|
|
val catRowIndex = 3 |
|
|
|
val valRowIndex = 4 |
|
|
|
val firstMonthCol = 1 |
|
|
|
val lastMonthCol = 12 |
|
|
|
|
|
|
|
val catRow = chartSheet.createRow(catRowIndex) |
|
|
|
val valRow = chartSheet.createRow(valRowIndex) |
|
|
|
for (i in 0 until 12) { |
|
|
|
catRow.createCell(firstMonthCol + i).setCellValue(monthLabels[i]) |
|
|
|
val colLetter = CellReference.convertNumToColString(firstMonthCol + i) |
|
|
|
val formula = if (lastDataRow1Based < 2) { |
|
|
|
"0" |
|
|
|
} else { |
|
|
|
"IF(TRIM(\$B\$1)=\"全部\",SUM('$dataSheetName'!$colLetter\$2:$colLetter\$$lastDataRow1Based),IFERROR(INDEX('$dataSheetName'!$colLetter\$2:$colLetter\$$lastDataRow1Based,MATCH(TRIM(\$B\$1),'$dataSheetName'!\$A\$2:\$A\$$lastDataRow1Based,0)),0))" |
|
|
|
} |
|
|
|
valRow.createCell(firstMonthCol + i).setCellFormula(formula) |
|
|
|
} |
|
|
|
catRow.createCell(lastMonthCol + 1).setCellValue("總和") |
|
|
|
valRow.createCell(lastMonthCol + 1).setCellFormula("SUM(B5:M5)") |
|
|
|
|
|
|
|
val drawing = chartSheet.createDrawingPatriarch() |
|
|
|
val anchor = XSSFClientAnchor(0, 0, 0, 0, 1, 6, 14, 28) |
|
|
|
val chart = drawing.createChart(anchor) |
|
|
|
chart.setTitleText("各月生產數量(依選擇之貨品編號)") |
|
|
|
chart.setTitleOverlay(false) |
|
|
|
|
|
|
|
val legend = chart.getOrAddLegend() |
|
|
|
legend.position = LegendPosition.BOTTOM |
|
|
|
|
|
|
|
val bottomAxis = chart.createCategoryAxis(AxisPosition.BOTTOM) |
|
|
|
bottomAxis.setTitle("月份") |
|
|
|
val leftAxis = chart.createValueAxis(AxisPosition.LEFT) |
|
|
|
leftAxis.setTitle("生產數量") |
|
|
|
|
|
|
|
val catRangeAddr = CellRangeAddress(catRowIndex, catRowIndex, firstMonthCol, lastMonthCol) |
|
|
|
val valRangeAddr = CellRangeAddress(valRowIndex, valRowIndex, firstMonthCol, lastMonthCol) |
|
|
|
val categories = XDDFDataSourcesFactory.fromStringCellRange(chartSheet, catRangeAddr) |
|
|
|
val values = XDDFDataSourcesFactory.fromNumericCellRange(chartSheet, valRangeAddr) |
|
|
|
|
|
|
|
val barData = chart.createData(ChartTypes.BAR, bottomAxis, leftAxis) as XDDFBarChartData |
|
|
|
barData.barDirection = BarDirection.COL |
|
|
|
val series = barData.addSeries(categories, values) |
|
|
|
series.setTitle("生產數量", null) |
|
|
|
chart.plot(barData) |
|
|
|
|
|
|
|
chartSheet.setColumnWidth(0, 18 * 256) |
|
|
|
chartSheet.setColumnWidth(1, 10 * 256) |
|
|
|
chartSheet.setColumnWidth(lastMonthCol + 1, 12 * 256) |
|
|
|
|
|
|
|
workbook.setSheetVisibility(workbook.getSheetIndex(dataSheet), SheetVisibility.VERY_HIDDEN) |
|
|
|
workbook.setSheetOrder(chartSheetName, 1) |
|
|
|
} |
|
|
|
|
|
|
|
private fun parseNumericForExcelChart(value: Any?): Double { |
|
|
|
if (value == null) return 0.0 |
|
|
|
return when (value) { |
|
|
|
is Number -> value.toDouble() |
|
|
|
is String -> { |
|
|
|
val s = value.replace(",", "").trim() |
|
|
|
if (s.isEmpty() || s == "-") return 0.0 |
|
|
|
s.toDoubleOrNull() ?: 0.0 |
|
|
|
} |
|
|
|
else -> value.toString().replace(",", "").trim().toDoubleOrNull() ?: 0.0 |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private fun setTextCell( |
|
|
|
row: org.apache.poi.ss.usermodel.Row, |
|
|
|
col: Int, |
|
|
|
value: Any?, |
|
|
|
style: org.apache.poi.ss.usermodel.CellStyle |
|
|
|
) { |
|
|
|
val cell = row.createCell(col) |
|
|
|
cell.setCellValue(value?.toString() ?: "") |
|
|
|
cell.cellStyle = style |
|
|
|
} |
|
|
|
|
|
|
|
private fun setNumberCell( |
|
|
|
row: org.apache.poi.ss.usermodel.Row, |
|
|
|
col: Int, |
|
|
|
value: Any?, |
|
|
|
style: org.apache.poi.ss.usermodel.CellStyle, |
|
|
|
dashStyle: org.apache.poi.ss.usermodel.CellStyle |
|
|
|
) { |
|
|
|
val cell = row.createCell(col) |
|
|
|
val parsed = when (value) { |
|
|
|
is Number -> value.toDouble() |
|
|
|
is String -> value.replace(",", "").toDoubleOrNull() ?: 0.0 |
|
|
|
else -> value?.toString()?.replace(",", "")?.toDoubleOrNull() ?: 0.0 |
|
|
|
} |
|
|
|
if (parsed == 0.0) { |
|
|
|
cell.setCellValue("-") |
|
|
|
cell.cellStyle = dashStyle |
|
|
|
} else { |
|
|
|
cell.setCellValue(parsed.roundToLong().toDouble()) |
|
|
|
cell.cellStyle = style |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private fun setDashCell( |
|
|
|
row: org.apache.poi.ss.usermodel.Row, |
|
|
|
col: Int, |
|
|
|
style: org.apache.poi.ss.usermodel.CellStyle |
|
|
|
) { |
|
|
|
val cell = row.createCell(col) |
|
|
|
cell.setCellValue("-") |
|
|
|
cell.cellStyle = style |
|
|
|
} |
|
|
|
|
|
|
|
@GetMapping("/semi-fg-item-codes") |
|
|
|
fun getSemiFGItemCodes( |
|
|
|
@RequestParam(required = false) stockCategory: String? |
|
|
|
|