| @@ -647,7 +647,8 @@ open class ProductionScheduleService( | |||||
| 0 AS batchNeed, | 0 AS batchNeed, | ||||
| i.pendingJobQty, | i.pendingJobQty, | ||||
| ((i.stockQty * 1.0) + ifnull(i.pendingJobQty, 0) ) / i.avgQtyLastMonth as daysLeft, | ((i.stockQty * 1.0) + ifnull(i.pendingJobQty, 0) ) / i.avgQtyLastMonth as daysLeft, | ||||
| -- i.baseScore as priority, | |||||
| 25 + 25 + markDark + markFloat + markDense + markAS + markTimeSequence + markComplexity as priority, | 25 + 25 + markDark + markFloat + markDense + markAS + markTimeSequence + markComplexity as priority, | ||||
| i.* | i.* | ||||
| FROM | FROM | ||||
| @@ -664,14 +665,14 @@ open class ProductionScheduleService( | |||||
| do.deleted = 0 and | do.deleted = 0 and | ||||
| dol.itemId = items.id | dol.itemId = items.id | ||||
| -- AND MONTH(do.estimatedArrivalDate) = MONTH(DATE_SUB(NOW(), INTERVAL 1 MONTH)) | -- AND MONTH(do.estimatedArrivalDate) = MONTH(DATE_SUB(NOW(), INTERVAL 1 MONTH)) | ||||
| AND do.estimatedArrivalDate >= '2026-01-12' AND do.estimatedArrivalDate < '2026-01-16' | |||||
| AND do.estimatedArrivalDate >= '2026-01-08' AND do.estimatedArrivalDate < '2026-01-18' | |||||
| GROUP BY do.estimatedArrivalDate) AS d) AS avgQtyLastMonth, | GROUP BY do.estimatedArrivalDate) AS d) AS avgQtyLastMonth, | ||||
| (select sum(reqQty) from job_order where bomId = bom.id and status != 'completed') AS pendingJobQty, | (select sum(reqQty) from job_order where bomId = bom.id and status != 'completed') AS pendingJobQty, | ||||
| CASE WHEN item_fake_onhand.onHandQty is not null THEN item_fake_onhand.onHandQty | CASE WHEN item_fake_onhand.onHandQty is not null THEN item_fake_onhand.onHandQty | ||||
| ELSE inventory.onHandQty - 500 END AS stockQty, | ELSE inventory.onHandQty - 500 END AS stockQty, | ||||
| bom.baseScore, | |||||
| bom.outputQty, | bom.outputQty, | ||||
| bom.outputQtyUom, | bom.outputQtyUom, | ||||
| (SELECT | (SELECT | ||||
| @@ -820,10 +821,12 @@ open class ProductionScheduleService( | |||||
| tempPriority++ | tempPriority++ | ||||
| } | } | ||||
| saveDetailedScheduleOutput(sortedOutputList, accProdCount, fgCount, produceAt) | |||||
| println("[INFO] sortedOutputList.size " + sortedOutputList.size + " have bee skipped"); | |||||
| //ignore the -1 date | |||||
| //saveDetailedScheduleOutput(sortedOutputList, accProdCount, fgCount, produceAt) | |||||
| //do for 9 days to predict | //do for 9 days to predict | ||||
| for (i in 1..(days + 1)) { | |||||
| for (i in 0..(days)) { | |||||
| val targetDate = produceAt.plusDays(i.toLong()) | val targetDate = produceAt.plusDays(i.toLong()) | ||||
| val isSat = targetDate.dayOfWeek == DayOfWeek.SATURDAY | val isSat = targetDate.dayOfWeek == DayOfWeek.SATURDAY | ||||
| val isFri = targetDate.dayOfWeek == DayOfWeek.FRIDAY | val isFri = targetDate.dayOfWeek == DayOfWeek.FRIDAY | ||||
| @@ -837,7 +840,7 @@ open class ProductionScheduleService( | |||||
| fgCount = 0 | fgCount = 0 | ||||
| accProdCount = 0.0 | accProdCount = 0.0 | ||||
| sortedOutputList.forEach { record -> | sortedOutputList.forEach { record -> | ||||
| if(i > 1) | |||||
| if(i > 0) | |||||
| record.stockQty = record.stockQty + (record.outputQty * record.needNoOfJobOrder.toInt()) - record.avgQtyLastMonth | record.stockQty = record.stockQty + (record.outputQty * record.needNoOfJobOrder.toInt()) - record.avgQtyLastMonth | ||||
| else | else | ||||
| record.stockQty = record.stockQty + (record.outputQty * record.needNoOfJobOrder.toInt()) | record.stockQty = record.stockQty + (record.outputQty * record.needNoOfJobOrder.toInt()) | ||||
| @@ -1411,7 +1414,7 @@ open class ProductionScheduleService( | |||||
| ): ByteArray { | ): ByteArray { | ||||
| val workbook = XSSFWorkbook() | val workbook = XSSFWorkbook() | ||||
| // Header style | |||||
| // ── Common Styles ── | |||||
| val headerStyle = workbook.createCellStyle().apply { | val headerStyle = workbook.createCellStyle().apply { | ||||
| fillForegroundColor = IndexedColors.GREY_25_PERCENT.index | fillForegroundColor = IndexedColors.GREY_25_PERCENT.index | ||||
| fillPattern = FillPatternType.SOLID_FOREGROUND | fillPattern = FillPatternType.SOLID_FOREGROUND | ||||
| @@ -1422,13 +1425,26 @@ open class ProductionScheduleService( | |||||
| setFont(font) | setFont(font) | ||||
| } | } | ||||
| // Body style | |||||
| val wrapStyle = workbook.createCellStyle().apply { | val wrapStyle = workbook.createCellStyle().apply { | ||||
| wrapText = true | wrapText = true | ||||
| verticalAlignment = VerticalAlignment.TOP | verticalAlignment = VerticalAlignment.TOP | ||||
| } | } | ||||
| // Group production lines by date | |||||
| // Number style with thousands separator (integer) | |||||
| val numberStyle = workbook.createCellStyle().apply { | |||||
| wrapText = true | |||||
| verticalAlignment = VerticalAlignment.TOP | |||||
| dataFormat = workbook.createDataFormat().getFormat("#,##0") | |||||
| } | |||||
| val numberDigitStyle = workbook.createCellStyle().apply { | |||||
| wrapText = true | |||||
| verticalAlignment = VerticalAlignment.TOP | |||||
| dataFormat = workbook.createDataFormat().getFormat("#,##0.0") | |||||
| } | |||||
| // ── Group production lines by date ── | |||||
| val groupedData = lines.groupBy { | val groupedData = lines.groupBy { | ||||
| val produceAt = it["produceAt"] | val produceAt = it["produceAt"] | ||||
| when (produceAt) { | when (produceAt) { | ||||
| @@ -1439,7 +1455,7 @@ open class ProductionScheduleService( | |||||
| } | } | ||||
| } | } | ||||
| // Production sheets (one per date) | |||||
| // ── Production sheets (one per date) ── | |||||
| groupedData.forEach { (dateKey, dailyLines) -> | groupedData.forEach { (dateKey, dailyLines) -> | ||||
| val safeSheetName = dateKey.replace(Regex("[/\\\\?*:\\[\\]]"), "-").take(31) | val safeSheetName = dateKey.replace(Regex("[/\\\\?*:\\[\\]]"), "-").take(31) | ||||
| val sheet = workbook.createSheet(safeSheetName) | val sheet = workbook.createSheet(safeSheetName) | ||||
| @@ -1461,38 +1477,42 @@ open class ProductionScheduleService( | |||||
| // Data rows | // Data rows | ||||
| dailyLines.forEachIndexed { index, line -> | dailyLines.forEachIndexed { index, line -> | ||||
| val row = sheet.createRow(index + 1) | val row = sheet.createRow(index + 1) | ||||
| row.heightInPoints = 35f // Slightly taller for portrait readability | |||||
| row.heightInPoints = 35f | |||||
| var j = 0; | |||||
| var j = 0 | |||||
| row.createCell(j++).apply { setCellValue(line["itemCode"]?.toString() ?: ""); cellStyle = wrapStyle } | row.createCell(j++).apply { setCellValue(line["itemCode"]?.toString() ?: ""); cellStyle = wrapStyle } | ||||
| row.createCell(j++).apply { setCellValue(line["itemName"]?.toString() ?: ""); cellStyle = wrapStyle } | row.createCell(j++).apply { setCellValue(line["itemName"]?.toString() ?: ""); cellStyle = wrapStyle } | ||||
| row.createCell(j++).apply { setCellValue(asDouble(line["avgQtyLastMonth"])); cellStyle = wrapStyle } | |||||
| row.createCell(j++).apply { setCellValue(asDouble(line["stockQty"])); cellStyle = wrapStyle } | |||||
| row.createCell(j++).apply { setCellValue(asDouble(line["daysLeft"])); cellStyle = wrapStyle } | |||||
| row.createCell(j++).apply { setCellValue(asDouble(line["outputdQty"] ?: line["outputQty"])); cellStyle = wrapStyle } | |||||
| row.createCell(j++).apply { setCellValue(asDouble(line["batchNeed"])); cellStyle = wrapStyle } | |||||
| row.createCell(j++).apply { setCellValue(asDouble(line["itemPriority"])); cellStyle = wrapStyle } | |||||
| row.createCell(j++).apply { setCellValue(asDouble(line["avgQtyLastMonth"])); cellStyle = numberStyle } | |||||
| row.createCell(j++).apply { setCellValue(asDouble(line["stockQty"])); cellStyle = numberStyle } | |||||
| row.createCell(j++).apply { setCellValue(asDouble(line["daysLeft"])); cellStyle = numberDigitStyle } | |||||
| row.createCell(j++).apply { setCellValue(asDouble(line["outputdQty"] ?: line["outputQty"])); cellStyle = numberStyle } | |||||
| row.createCell(j++).apply { setCellValue(asDouble(line["batchNeed"])); cellStyle = numberStyle } | |||||
| row.createCell(j++).apply { setCellValue(asDouble(line["itemPriority"])); cellStyle = numberStyle } | |||||
| } | } | ||||
| // Auto-size with wider limits for portrait | |||||
| for (i in headers.indices) { | |||||
| sheet.autoSizeColumn(i) | |||||
| val maxWidth = when (i) { | |||||
| 0 -> 35 * 256 // Item Name can be longer | |||||
| else -> 18 * 256 | |||||
| } | |||||
| if (sheet.getColumnWidth(i) > maxWidth) { | |||||
| sheet.setColumnWidth(i, maxWidth) | |||||
| } | |||||
| // ── FIXED COLUMN WIDTHS ── (much wider, especially for text columns) | |||||
| sheet.setColumnWidth(0, 24 * 256) // 物料編號 - wider for codes like ABC-12345-XYZ | |||||
| sheet.setColumnWidth(1, 90 * 256) // 物料名稱 - very generous for long Chinese descriptions | |||||
| sheet.setColumnWidth(2, 20 * 256) // 每日平均出貨量 | |||||
| sheet.setColumnWidth(3, 22 * 256) // 出貨前預計存貨量 | |||||
| sheet.setColumnWidth(4, 14 * 256) // 可用日 | |||||
| sheet.setColumnWidth(5, 18 * 256) // 每批數量 | |||||
| sheet.setColumnWidth(6, 18 * 256) // 生產量(批) | |||||
| sheet.setColumnWidth(7, 14 * 256) // 優先度 | |||||
| // Optional: still allow a little auto-sizing on the longest text column | |||||
| sheet.autoSizeColumn(1) | |||||
| // But cap it so it doesn't become excessively wide | |||||
| if (sheet.getColumnWidth(1) > 110 * 256) { | |||||
| sheet.setColumnWidth(1, 110 * 256) | |||||
| } | } | ||||
| // === PORTRAIT PRINT SETUP === | |||||
| // ── Print setup - Portrait ── | |||||
| val printSetup = sheet.printSetup | val printSetup = sheet.printSetup | ||||
| printSetup.paperSize = XSSFPrintSetup.A4_PAPERSIZE | printSetup.paperSize = XSSFPrintSetup.A4_PAPERSIZE | ||||
| printSetup.landscape = false // ← Portrait mode | |||||
| printSetup.fitWidth = 1.toShort() // Crucial: scale to fit width | |||||
| printSetup.fitHeight = 0.toShort() // Allow multiple pages tall | |||||
| printSetup.landscape = false | |||||
| printSetup.fitWidth = 1.toShort() | |||||
| printSetup.fitHeight = 0.toShort() | |||||
| sheet.fitToPage = true | sheet.fitToPage = true | ||||
| sheet.horizontallyCenter = true | sheet.horizontallyCenter = true | ||||
| sheet.setMargin(Sheet.LeftMargin, 0.5) | sheet.setMargin(Sheet.LeftMargin, 0.5) | ||||
| @@ -1501,11 +1521,11 @@ open class ProductionScheduleService( | |||||
| sheet.setMargin(Sheet.BottomMargin, 0.7) | sheet.setMargin(Sheet.BottomMargin, 0.7) | ||||
| } | } | ||||
| // === MATERIAL SUMMARY SHEET - PORTRAIT OPTIMIZED === | |||||
| val matSheet = workbook.createSheet("Material Summary") | |||||
| // ── MATERIAL SUMMARY SHEET ── | |||||
| val matSheet = workbook.createSheet("物料用量總結") | |||||
| val matHeaders = listOf( | val matHeaders = listOf( | ||||
| "物料編號", "物料名稱", "欠缺需量", "總用量", | |||||
| "物料編號", "物料名稱", "需求量", "總用量", | |||||
| "單位", "數量(採購單位)", "採購單位", | "單位", "數量(採購單位)", "採購單位", | ||||
| "已採購數量", "庫存數", "庫存單位", | "已採購數量", "庫存數", "庫存單位", | ||||
| "相關成品編號", "相關成品名稱" | "相關成品編號", "相關成品名稱" | ||||
| @@ -1547,35 +1567,45 @@ open class ProductionScheduleService( | |||||
| values.forEachIndexed { i, value -> | values.forEachIndexed { i, value -> | ||||
| val cell = row.createCell(i) | val cell = row.createCell(i) | ||||
| when (value) { | when (value) { | ||||
| is String -> cell.setCellValue(value) | |||||
| is Number -> cell.setCellValue(value.toDouble()) | |||||
| else -> cell.setCellValue("") | |||||
| is String -> { | |||||
| cell.setCellValue(value) | |||||
| cell.cellStyle = wrapStyle | |||||
| } | |||||
| is Number -> { | |||||
| cell.setCellValue(value.toDouble()) | |||||
| cell.cellStyle = when (i) { | |||||
| 2, 3, 5, 7, 8 -> numberStyle | |||||
| else -> wrapStyle | |||||
| } | |||||
| } | |||||
| else -> { | |||||
| cell.setCellValue("") | |||||
| cell.cellStyle = wrapStyle | |||||
| } | |||||
| } | } | ||||
| cell.cellStyle = wrapStyle | |||||
| } | } | ||||
| } | } | ||||
| // Manual column widths optimized for PORTRAIT A4 | |||||
| matSheet.setColumnWidth(0, 16 * 256) // Mat Code | |||||
| matSheet.setColumnWidth(1, 32 * 256) // Mat Name | |||||
| matSheet.setColumnWidth(2, 14 * 256) // Required Qty | |||||
| matSheet.setColumnWidth(3, 14 * 256) // Total Qty Need | |||||
| matSheet.setColumnWidth(4, 10 * 256) // UoM | |||||
| matSheet.setColumnWidth(5, 14 * 256) // Purchase Qty Need | |||||
| matSheet.setColumnWidth(6, 10 * 256) // Purchase UoM | |||||
| matSheet.setColumnWidth(7, 14 * 256) // Purchased Qty | |||||
| matSheet.setColumnWidth(8, 14 * 256) // On Hand Qty | |||||
| matSheet.setColumnWidth(9, 14 * 256) // Unavailable Qty | |||||
| matSheet.setColumnWidth(10, 22 * 256) // Related Item Code | |||||
| matSheet.setColumnWidth(11, 40 * 256) // Related Item Name (longest) | |||||
| // Column widths for material summary | |||||
| matSheet.setColumnWidth(0, 20 * 256) | |||||
| matSheet.setColumnWidth(1, 50 * 256) // 物料名稱 | |||||
| matSheet.setColumnWidth(2, 16 * 256) | |||||
| matSheet.setColumnWidth(3, 16 * 256) | |||||
| matSheet.setColumnWidth(4, 12 * 256) | |||||
| matSheet.setColumnWidth(5, 16 * 256) | |||||
| matSheet.setColumnWidth(6, 12 * 256) | |||||
| matSheet.setColumnWidth(7, 16 * 256) | |||||
| matSheet.setColumnWidth(8, 16 * 256) | |||||
| matSheet.setColumnWidth(9, 12 * 256) | |||||
| matSheet.setColumnWidth(10, 22 * 256) | |||||
| matSheet.setColumnWidth(11, 60 * 256) // 相關成品名稱 - wider | |||||
| // Portrait print setup | // Portrait print setup | ||||
| val matPrintSetup = matSheet.printSetup | val matPrintSetup = matSheet.printSetup | ||||
| matPrintSetup.paperSize = XSSFPrintSetup.A4_PAPERSIZE | matPrintSetup.paperSize = XSSFPrintSetup.A4_PAPERSIZE | ||||
| matPrintSetup.landscape = false // ← Portrait | |||||
| matPrintSetup.landscape = false | |||||
| matPrintSetup.fitWidth = 1.toShort() | matPrintSetup.fitWidth = 1.toShort() | ||||
| matPrintSetup.fitHeight = 0.toShort() | matPrintSetup.fitHeight = 0.toShort() | ||||
| matSheet.fitToPage = true | matSheet.fitToPage = true | ||||
| matSheet.horizontallyCenter = true | matSheet.horizontallyCenter = true | ||||
| matSheet.setMargin(Sheet.LeftMargin, 0.5) | matSheet.setMargin(Sheet.LeftMargin, 0.5) | ||||
| @@ -1583,59 +1613,41 @@ open class ProductionScheduleService( | |||||
| matSheet.setMargin(Sheet.TopMargin, 0.7) | matSheet.setMargin(Sheet.TopMargin, 0.7) | ||||
| matSheet.setMargin(Sheet.BottomMargin, 0.7) | matSheet.setMargin(Sheet.BottomMargin, 0.7) | ||||
| // === DAILY MATERIAL NEEDS SHEET - CLEAN & READABLE DATES === | |||||
| val dailySheet = workbook.createSheet("Daily Material Needs") | |||||
| // ── DAILY MATERIAL NEEDS SHEET ── | |||||
| val dailySheet = workbook.createSheet("每天物料用量") | |||||
| // Generate nicely formatted date headers: "10 Jan" on first row, day of week on second | |||||
| val dateFormatterDayMonth = DateTimeFormatter.ofPattern("d MMM", Locale.ENGLISH) | val dateFormatterDayMonth = DateTimeFormatter.ofPattern("d MMM", Locale.ENGLISH) | ||||
| val dateFormatterDayName = DateTimeFormatter.ofPattern("EEEE", Locale.ENGLISH) // Monday, Tuesday... | |||||
| val dateFormatterDayName = DateTimeFormatter.ofPattern("EEEE", Locale.ENGLISH) | |||||
| val dateInfo = (1..9).map { offset -> | val dateInfo = (1..9).map { offset -> | ||||
| val date = fromDate.plusDays(offset.toLong()) | val date = fromDate.plusDays(offset.toLong()) | ||||
| val dayMonth = date.format(dateFormatterDayMonth) // e.g., "10 Jan" | |||||
| val dayName = date.format(dateFormatterDayName) // e.g., "Monday" | |||||
| Pair(dayMonth, dayName) | |||||
| Pair(date.format(dateFormatterDayMonth), date.format(dateFormatterDayName)) | |||||
| } | } | ||||
| // Headers: Two rows for dates | |||||
| // Two-row headers | |||||
| val headerRow1 = dailySheet.createRow(0) | val headerRow1 = dailySheet.createRow(0) | ||||
| val headerRow2 = dailySheet.createRow(1) | val headerRow2 = dailySheet.createRow(1) | ||||
| // Base columns | |||||
| val baseHeaders = listOf("Mat Code", "Mat Name", "UoM") | |||||
| val baseHeaders = listOf("物料編號", "物料名稱", "採購單位") | |||||
| baseHeaders.forEachIndexed { i, title -> | baseHeaders.forEachIndexed { i, title -> | ||||
| val cell1 = headerRow1.createCell(i) | |||||
| cell1.setCellValue(title) | |||||
| cell1.cellStyle = headerStyle | |||||
| val cell2 = headerRow2.createCell(i) | |||||
| cell2.cellStyle = headerStyle // Empty but merged visually | |||||
| headerRow1.createCell(i).apply { setCellValue(title); cellStyle = headerStyle } | |||||
| headerRow2.createCell(i).apply { cellStyle = headerStyle } | |||||
| } | } | ||||
| // Date columns: two rows | |||||
| dateInfo.forEachIndexed { index, (dayMonth, dayName) -> | dateInfo.forEachIndexed { index, (dayMonth, dayName) -> | ||||
| val colIndex = baseHeaders.size + index | |||||
| // First row: "10 Jan" | |||||
| val cellDay = headerRow1.createCell(colIndex) | |||||
| cellDay.setCellValue(dayMonth) | |||||
| cellDay.cellStyle = headerStyle | |||||
| // Second row: "Monday" | |||||
| val cellWeekDay = headerRow2.createCell(colIndex) | |||||
| cellWeekDay.setCellValue(dayName) | |||||
| cellWeekDay.cellStyle = headerStyle | |||||
| val col = baseHeaders.size + index | |||||
| headerRow1.createCell(col).apply { setCellValue(dayMonth); cellStyle = headerStyle } | |||||
| headerRow2.createCell(col).apply { setCellValue(dayName); cellStyle = headerStyle } | |||||
| } | } | ||||
| // Merge the base header cells vertically (optional, for cleaner look) | |||||
| for (i in baseHeaders.indices) { | for (i in baseHeaders.indices) { | ||||
| dailySheet.addMergedRegion(CellRangeAddress(0, 1, i, i)) | dailySheet.addMergedRegion(CellRangeAddress(0, 1, i, i)) | ||||
| } | } | ||||
| // Now write data starting from row 2 (index 2) | |||||
| // Data rows | |||||
| lineDailys.forEachIndexed { index, rowData -> | lineDailys.forEachIndexed { index, rowData -> | ||||
| val row = dailySheet.createRow(index + 2) // Start after the two header rows | |||||
| val row = dailySheet.createRow(index + 2) | |||||
| row.heightInPoints = 30f | row.heightInPoints = 30f | ||||
| val baseValues = listOf<Any>( | val baseValues = listOf<Any>( | ||||
| @@ -1653,30 +1665,31 @@ open class ProductionScheduleService( | |||||
| allValues.forEachIndexed { i, value -> | allValues.forEachIndexed { i, value -> | ||||
| val cell = row.createCell(i) | val cell = row.createCell(i) | ||||
| when (value) { | when (value) { | ||||
| is String -> cell.setCellValue(value) | |||||
| is Number -> cell.setCellValue(value.toDouble()) | |||||
| else -> cell.setCellValue(0.0) | |||||
| is String -> { | |||||
| cell.setCellValue(value) | |||||
| cell.cellStyle = wrapStyle | |||||
| } | |||||
| is Number -> { | |||||
| cell.setCellValue(value.toDouble()) | |||||
| cell.cellStyle = if (i >= baseHeaders.size) numberStyle else wrapStyle | |||||
| } | |||||
| else -> { | |||||
| cell.setCellValue(0.0) | |||||
| cell.cellStyle = numberStyle | |||||
| } | |||||
| } | } | ||||
| cell.cellStyle = wrapStyle | |||||
| } | } | ||||
| } | } | ||||
| // === COLUMN WIDTHS === | |||||
| dailySheet.setColumnWidth(0, 16 * 256) // Mat Code | |||||
| dailySheet.setColumnWidth(1, 35 * 256) // Mat Name | |||||
| dailySheet.setColumnWidth(2, 10 * 256) // UoM | |||||
| // Date columns: slightly wider for readability | |||||
| for (i in baseHeaders.size until baseHeaders.size + 10) { | |||||
| dailySheet.setColumnWidth(i, 14 * 256) | |||||
| // Column widths for daily sheet | |||||
| dailySheet.setColumnWidth(0, 20 * 256) // 物料編號 | |||||
| dailySheet.setColumnWidth(1, 55 * 256) // 物料名稱 | |||||
| dailySheet.setColumnWidth(2, 12 * 256) // 採購單位 | |||||
| for (i in baseHeaders.size until baseHeaders.size + 9) { | |||||
| dailySheet.setColumnWidth(i, 16 * 256) // daily qty columns | |||||
| } | } | ||||
| // Auto-size base columns (optional improvement) | |||||
| for (i in 0..2) { | |||||
| dailySheet.autoSizeColumn(i) | |||||
| } | |||||
| // === PRINT SETUP - PORTRAIT === | |||||
| // Print setup | |||||
| val dailyPrintSetup = dailySheet.printSetup | val dailyPrintSetup = dailySheet.printSetup | ||||
| dailyPrintSetup.paperSize = XSSFPrintSetup.A4_PAPERSIZE | dailyPrintSetup.paperSize = XSSFPrintSetup.A4_PAPERSIZE | ||||
| dailyPrintSetup.landscape = false | dailyPrintSetup.landscape = false | ||||
| @@ -1687,10 +1700,13 @@ open class ProductionScheduleService( | |||||
| dailySheet.setMargin(Sheet.TopMargin, 0.7) | dailySheet.setMargin(Sheet.TopMargin, 0.7) | ||||
| dailySheet.setMargin(Sheet.BottomMargin, 0.7) | dailySheet.setMargin(Sheet.BottomMargin, 0.7) | ||||
| // Finalize | |||||
| // ── Write to ByteArray ── | |||||
| val out = ByteArrayOutputStream() | val out = ByteArrayOutputStream() | ||||
| workbook.use { it.write(out) } | |||||
| workbook.use { wb -> | |||||
| wb.write(out) | |||||
| } | |||||
| workbook.close() | workbook.close() | ||||
| return out.toByteArray() | return out.toByteArray() | ||||
| } | } | ||||