diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/reporting/ExportStreamerInterface.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/reporting/ExportStreamerInterface.java
index 4e2f7e02..eebfe4e0 100644
--- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/reporting/ExportStreamerInterface.java
+++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/reporting/ExportStreamerInterface.java
@@ -54,6 +54,14 @@ public interface ExportStreamerInterface
// noop in base class
}
+ /***************************************************************************
+ **
+ ***************************************************************************/
+ default void setExportStyleCustomizer(ExportStyleCustomizerInterface exportStyleCustomizer)
+ {
+ // noop in base class
+ }
+
/*******************************************************************************
** Called once per sheet, before any rows are available. Meant to write a
** header, for example.
diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/reporting/ExportStyleCustomizerInterface.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/reporting/ExportStyleCustomizerInterface.java
new file mode 100644
index 00000000..23d15ef3
--- /dev/null
+++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/reporting/ExportStyleCustomizerInterface.java
@@ -0,0 +1,35 @@
+/*
+ * QQQ - Low-code Application Framework for Engineers.
+ * Copyright (C) 2021-2025. Kingsrook, LLC
+ * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
+ * contact@kingsrook.com
+ * https://github.com/Kingsrook/
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package com.kingsrook.qqq.backend.core.actions.reporting;
+
+
+/*******************************************************************************
+ ** interface for classes that can be used to customize visual style aspects of
+ ** exports/reports.
+ **
+ ** Anticipates very different sub-interfaces based on the file type being generated,
+ ** and the capabilities of each. e.g., excel (bolds, fonts, cell merging) vs
+ ** json (different structure of objects).
+ *******************************************************************************/
+public interface ExportStyleCustomizerInterface
+{
+}
diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/reporting/GenerateReportAction.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/reporting/GenerateReportAction.java
index b1e28903..07863fd0 100644
--- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/reporting/GenerateReportAction.java
+++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/reporting/GenerateReportAction.java
@@ -163,6 +163,12 @@ public class GenerateReportAction extends AbstractQActionFunction
-
- """);
+ """);
+ if(styleCustomizerInterface != null && view != null)
+ {
+ List columnWidths = styleCustomizerInterface.getColumnWidthsForView(view);
+ if(CollectionUtils.nullSafeHasContents(columnWidths))
+ {
+ writer.write("");
+ for(int i = 0; i < columnWidths.size(); i++)
+ {
+ writer.write("""
+
+ """.formatted(i + 1, i + 1, columnWidths.get(i)));
+ }
+ writer.write("");
+ }
+ }
+
+ writer.write("");
}
@@ -67,11 +86,25 @@ public class StreamedSheetWriter
/*******************************************************************************
**
*******************************************************************************/
- public void endSheet() throws IOException
+ public void endSheet(QReportView view, ExcelPoiStyleCustomizerInterface styleCustomizerInterface) throws IOException
{
- writer.write("""
-
- """);
+ writer.write("");
+
+ if(styleCustomizerInterface != null && view != null)
+ {
+ List mergedRanges = styleCustomizerInterface.getMergedRangesForView(view);
+ if(CollectionUtils.nullSafeHasContents(mergedRanges))
+ {
+ writer.write(String.format("", mergedRanges.size()));
+ for(String range : mergedRanges)
+ {
+ writer.write(String.format("", range));
+ }
+ writer.write("");
+ }
+ }
+
+ writer.write("");
}
@@ -151,7 +184,7 @@ public class StreamedSheetWriter
{
rs.append(""");
}
- else if (c < 32 && c != '\t' && c != '\n')
+ else if(c < 32 && c != '\t' && c != '\n')
{
rs.append(' ');
}
diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/reporting/ReportInput.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/reporting/ReportInput.java
index be901e94..6c26a7b9 100644
--- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/reporting/ReportInput.java
+++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/reporting/ReportInput.java
@@ -28,6 +28,7 @@ import java.util.Map;
import java.util.function.Supplier;
import com.kingsrook.qqq.backend.core.actions.reporting.ExportStreamerInterface;
import com.kingsrook.qqq.backend.core.model.actions.AbstractTableActionInput;
+import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
import com.kingsrook.qqq.backend.core.model.metadata.reporting.QReportMetaData;
@@ -44,6 +45,7 @@ public class ReportInput extends AbstractTableActionInput
private ReportDestination reportDestination;
private Supplier extends ExportStreamerInterface> overrideExportStreamerSupplier;
+ private QCodeReference exportStyleCustomizer;
@@ -208,4 +210,35 @@ public class ReportInput extends AbstractTableActionInput
return (this);
}
+
+
+ /*******************************************************************************
+ ** Getter for exportStyleCustomizer
+ *******************************************************************************/
+ public QCodeReference getExportStyleCustomizer()
+ {
+ return (this.exportStyleCustomizer);
+ }
+
+
+
+ /*******************************************************************************
+ ** Setter for exportStyleCustomizer
+ *******************************************************************************/
+ public void setExportStyleCustomizer(QCodeReference exportStyleCustomizer)
+ {
+ this.exportStyleCustomizer = exportStyleCustomizer;
+ }
+
+
+
+ /*******************************************************************************
+ ** Fluent setter for exportStyleCustomizer
+ *******************************************************************************/
+ public ReportInput withExportStyleCustomizer(QCodeReference exportStyleCustomizer)
+ {
+ this.exportStyleCustomizer = exportStyleCustomizer;
+ return (this);
+ }
+
}
diff --git a/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/actions/reporting/excel/TestExcelStyler.java b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/actions/reporting/excel/TestExcelStyler.java
new file mode 100644
index 00000000..c45061db
--- /dev/null
+++ b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/actions/reporting/excel/TestExcelStyler.java
@@ -0,0 +1,76 @@
+/*
+ * QQQ - Low-code Application Framework for Engineers.
+ * Copyright (C) 2021-2025. Kingsrook, LLC
+ * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
+ * contact@kingsrook.com
+ * https://github.com/Kingsrook/
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package com.kingsrook.qqq.backend.core.actions.reporting.excel;
+
+
+import java.util.List;
+import java.util.Map;
+import com.kingsrook.qqq.backend.core.actions.reporting.excel.poi.ExcelPoiStyleCustomizerInterface;
+import com.kingsrook.qqq.backend.core.model.metadata.reporting.QReportView;
+import org.apache.poi.ss.usermodel.CreationHelper;
+import org.apache.poi.ss.usermodel.Font;
+import org.apache.poi.xssf.usermodel.XSSFCellStyle;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+
+
+/*******************************************************************************
+ **
+ *******************************************************************************/
+public class TestExcelStyler implements ExcelPoiStyleCustomizerInterface
+{
+
+ /***************************************************************************
+ **
+ ***************************************************************************/
+ @Override
+ public List getColumnWidthsForView(QReportView view)
+ {
+ return List.of(60, 50, 40);
+ }
+
+
+
+ /***************************************************************************
+ **
+ ***************************************************************************/
+ @Override
+ public List getMergedRangesForView(QReportView view)
+ {
+ return List.of("A1:B1");
+ }
+
+
+
+ /***************************************************************************
+ **
+ ***************************************************************************/
+ @Override
+ public void customizeStyles(Map styles, XSSFWorkbook workbook, CreationHelper createHelper)
+ {
+ Font font = workbook.createFont();
+ font.setFontHeightInPoints((short) 16);
+ font.setBold(true);
+ XSSFCellStyle cellStyle = workbook.createCellStyle();
+ cellStyle.setFont(font);
+ styles.put("header", cellStyle);
+ }
+}