diff --git a/migration/i7.1z/oracle/201804121850_Extend_Export_Feature.sql b/migration/i7.1z/oracle/201804121850_Extend_Export_Feature.sql
new file mode 100644
index 0000000000..c55a7184d2
--- /dev/null
+++ b/migration/i7.1z/oracle/201804121850_Extend_Export_Feature.sql
@@ -0,0 +1,11 @@
+SET SQLBLANKLINES ON
+SET DEFINE OFF
+
+-- Excel 2007 support in idempiere
+-- Apr 12, 2018 3:12:17 PM IST
+INSERT INTO AD_Message (MsgType,MsgText,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,AD_Message_ID,Value,EntityType,AD_Message_UU) VALUES ('E','xlsx - Excel file',0,0,'Y',TO_DATE('2018-04-12 15:12:16','YYYY-MM-DD HH24:MI:SS'),100,TO_DATE('2018-04-12 15:12:16','YYYY-MM-DD HH24:MI:SS'),100,200451,'FileXLSX','D','48cc5032-f2c7-4b27-99bc-05c6269d032b')
+;
+
+SELECT register_migration_script('201804121850_Extend_Export_Feature.sql') FROM dual
+;
+
diff --git a/migration/i7.1z/postgresql/201804121850_Extend_Export_Feature.sql b/migration/i7.1z/postgresql/201804121850_Extend_Export_Feature.sql
new file mode 100644
index 0000000000..5cd6a41fa5
--- /dev/null
+++ b/migration/i7.1z/postgresql/201804121850_Extend_Export_Feature.sql
@@ -0,0 +1,8 @@
+-- Excel 2007 support in idempiere
+-- Apr 12, 2018 3:12:17 PM IST
+INSERT INTO AD_Message (MsgType,MsgText,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,AD_Message_ID,Value,EntityType,AD_Message_UU) VALUES ('E','xlsx - Excel file',0,0,'Y',TO_TIMESTAMP('2018-04-12 15:12:16','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2018-04-12 15:12:16','YYYY-MM-DD HH24:MI:SS'),100,200451,'FileXLSX','D','48cc5032-f2c7-4b27-99bc-05c6269d032b')
+;
+
+SELECT register_migration_script('201804121850_Extend_Export_Feature.sql') FROM dual
+;
+
diff --git a/org.adempiere.base-feature/feature.xml b/org.adempiere.base-feature/feature.xml
index 2d13a1c841..e2e6a1a678 100644
--- a/org.adempiere.base-feature/feature.xml
+++ b/org.adempiere.base-feature/feature.xml
@@ -517,4 +517,11 @@
version="0.0.0"
unpack="false"/>
+
+
diff --git a/org.adempiere.base/plugin.xml b/org.adempiere.base/plugin.xml
index 6aa93bd88f..91943b4fc5 100644
--- a/org.adempiere.base/plugin.xml
+++ b/org.adempiere.base/plugin.xml
@@ -140,4 +140,13 @@
+
+
+
+
diff --git a/org.adempiere.base/src/org/adempiere/impexp/AbstractXLSXExporter.java b/org.adempiere.base/src/org/adempiere/impexp/AbstractXLSXExporter.java
new file mode 100644
index 0000000000..0c1919dbd6
--- /dev/null
+++ b/org.adempiere.base/src/org/adempiere/impexp/AbstractXLSXExporter.java
@@ -0,0 +1,570 @@
+/******************************************************************************
+ * Copyright (C) 2018 Logilite Technologies LLP *
+ * This program is free software; you can redistribute it and/or modify it *
+ * under the terms version 2 of the GNU General Public License as published *
+ * by the Free Software Foundation. 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 General Public License for more details. *
+ * You should have received a copy of the GNU General Public License along *
+ * with this program; if not, write to the Free Software Foundation, Inc., *
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. *
+ *****************************************************************************/
+package org.adempiere.impexp;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.OutputStream;
+import java.sql.Timestamp;
+import java.text.DecimalFormat;
+import java.text.NumberFormat;
+import java.util.HashMap;
+import java.util.Properties;
+import java.util.logging.Level;
+
+import org.apache.poi.hssf.usermodel.HSSFDataFormat;
+import org.apache.poi.hssf.usermodel.HSSFHeader;
+import org.apache.poi.ss.usermodel.BorderStyle;
+import org.apache.poi.ss.usermodel.Footer;
+import org.apache.poi.ss.usermodel.Header;
+import org.apache.poi.xssf.usermodel.XSSFCell;
+import org.apache.poi.xssf.usermodel.XSSFCellStyle;
+import org.apache.poi.xssf.usermodel.XSSFDataFormat;
+import org.apache.poi.xssf.usermodel.XSSFFont;
+import org.apache.poi.xssf.usermodel.XSSFPrintSetup;
+import org.apache.poi.xssf.usermodel.XSSFRichTextString;
+import org.apache.poi.xssf.usermodel.XSSFRow;
+import org.apache.poi.xssf.usermodel.XSSFSheet;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+import org.compiere.util.CLogger;
+import org.compiere.util.DisplayType;
+import org.compiere.util.Env;
+import org.compiere.util.Ini;
+import org.compiere.util.Language;
+import org.compiere.util.Msg;
+import org.compiere.util.Util;
+
+/**
+ * Abstract MS Excel Format (xlsx) Exporter
+ *
+ * @author Deepak Pansheriya
+ */
+public abstract class AbstractXLSXExporter
+{
+ /**
+ * Is the current Row a Function Row
+ *
+ * @return true if function row
+ */
+ public abstract boolean isFunctionRow();
+
+ /**
+ * Get Columns Count
+ *
+ * @return number of columns
+ */
+ public abstract int getColumnCount();
+
+ /**
+ * Get Rows Count
+ *
+ * @return number of rows
+ */
+ public abstract int getRowCount();
+
+ /**
+ * Set current row
+ *
+ * @param row row index
+ */
+ protected abstract void setCurrentRow(int row);
+
+ /**
+ * @return current row index
+ */
+ protected abstract int getCurrentRow();
+
+ /**
+ * Check if column is printed (displayed)
+ *
+ * @param col column index
+ * @return true if is visible
+ */
+ public abstract boolean isColumnPrinted(int col);
+
+ /**
+ * Get column header name
+ *
+ * @param col column index
+ * @return header name
+ */
+ public abstract String getHeaderName(int col);
+
+ /**
+ * Get cell display type (see {@link DisplayType})
+ *
+ * @param row row index
+ * @param col column index
+ * @return display type
+ */
+ public abstract int getDisplayType(int row, int col);
+
+ /**
+ * Get cell value
+ *
+ * @param row row index
+ * @param col column index
+ * @return cell value
+ */
+ public abstract Object getValueAt(int row, int col);
+
+ /**
+ * Check if there is a page break on given cell
+ *
+ * @param row row index
+ * @param col column index
+ * @return true if there is a page break
+ */
+ public abstract boolean isPageBreak(int row, int col);
+
+ /** Logger */
+ protected final CLogger log = CLogger.getCLogger(getClass());
+ //
+ private XSSFWorkbook m_workbook;
+ private XSSFDataFormat m_dataFormat;
+ private XSSFFont m_fontHeader = null;
+ private XSSFFont m_fontDefault = null;
+ private Language m_lang = null;
+ private int m_sheetCount = 0;
+ //
+ private int m_colSplit = 1;
+ private int m_rowSplit = 1;
+ private boolean currentRowOnly = false;
+ /** Styles cache */
+ private HashMap m_styles = new HashMap();
+
+ protected Boolean[] colSuppressRepeats;
+
+ public AbstractXLSXExporter()
+ {
+ m_workbook = new XSSFWorkbook();
+ m_dataFormat = m_workbook.createDataFormat();
+ }
+
+ protected Properties getCtx()
+ {
+ return Env.getCtx();
+ }
+
+ protected void setFreezePane(int colSplit, int rowSplit)
+ {
+ m_colSplit = colSplit;
+ m_rowSplit = rowSplit;
+ }
+
+ private String fixString(String str)
+ {
+ // ms excel doesn't support UTF8 charset
+ return Util.stripDiacritics(str);
+ }
+
+ protected Language getLanguage()
+ {
+ if (m_lang == null)
+ m_lang = Env.getLanguage(getCtx());
+ return m_lang;
+ }
+
+ private XSSFFont getFont(boolean isHeader)
+ {
+ XSSFFont font = null;
+ if (isHeader)
+ {
+ if (m_fontHeader == null)
+ {
+ m_fontHeader = m_workbook.createFont();
+ m_fontHeader.setBold(true);
+ }
+ font = m_fontHeader;
+ }
+ else if (isFunctionRow())
+ {
+ font = m_workbook.createFont();
+ font.setBold(true);
+ font.setItalic(true);
+ }
+ else
+ {
+ if (m_fontDefault == null)
+ {
+ m_fontDefault = m_workbook.createFont();
+ }
+ font = m_fontDefault;
+ }
+ return font;
+ }
+
+ /**
+ * Get Excel number format string by given {@link NumberFormat}
+ *
+ * @param df number format
+ * @param isHighlightNegativeNumbers highlight negative numbers using RED
+ * color
+ * @return number excel format string
+ */
+ private String getFormatString(NumberFormat df, boolean isHighlightNegativeNumbers)
+ {
+ StringBuffer format = new StringBuffer();
+ int integerDigitsMin = df.getMinimumIntegerDigits();
+ int integerDigitsMax = df.getMaximumIntegerDigits();
+ for (int i = 0; i < integerDigitsMax; i++)
+ {
+ if (i < integerDigitsMin)
+ format.insert(0, "0");
+ else
+ format.insert(0, "#");
+ if (i == 2)
+ {
+ format.insert(0, ",");
+ }
+ }
+ int fractionDigitsMin = df.getMinimumFractionDigits();
+ int fractionDigitsMax = df.getMaximumFractionDigits();
+ for (int i = 0; i < fractionDigitsMax; i++)
+ {
+ if (i == 0)
+ format.append(".");
+ if (i < fractionDigitsMin)
+ format.append("0");
+ else
+ format.append("#");
+ }
+ if (isHighlightNegativeNumbers)
+ {
+ String f = format.toString();
+ format = new StringBuffer(f).append(";[RED]-").append(f);
+ }
+ //
+ if (log.isLoggable(Level.FINEST))
+ log.finest("NumberFormat: " + format);
+ return format.toString();
+
+ }
+
+ private XSSFCellStyle getStyle(int row, int col)
+ {
+ int displayType = getDisplayType(row, col);
+ String key = "cell-" + col + "-" + displayType;
+ XSSFCellStyle cs = m_styles.get(key);
+ if (cs == null)
+ {
+ boolean isHighlightNegativeNumbers = true;
+ cs = m_workbook.createCellStyle();
+ XSSFFont font = getFont(false);
+ cs.setFont(font);
+ // Border
+ cs.setBorderLeft(BorderStyle.THIN);
+ cs.setBorderTop(BorderStyle.THIN);
+ cs.setBorderRight(BorderStyle.THIN);
+ cs.setBorderBottom(BorderStyle.THIN);
+ //
+ if (DisplayType.isDate(displayType))
+ {
+ cs.setDataFormat(m_dataFormat.getFormat(DisplayType.getDateFormat(getLanguage()).toPattern()));
+ }
+ else if (DisplayType.isNumeric(displayType))
+ {
+ DecimalFormat df = DisplayType.getNumberFormat(displayType, getLanguage());
+ String format = getFormatString(df, isHighlightNegativeNumbers);
+ cs.setDataFormat(m_dataFormat.getFormat(format));
+ }
+ m_styles.put(key, cs);
+ }
+ return cs;
+ }
+
+ private XSSFCellStyle getHeaderStyle(int col)
+ {
+ String key = "header-" + col;
+ XSSFCellStyle cs_header = m_styles.get(key);
+ if (cs_header == null)
+ {
+ XSSFFont font_header = getFont(true);
+ cs_header = m_workbook.createCellStyle();
+ cs_header.setFont(font_header);
+ cs_header.setBorderLeft(BorderStyle.MEDIUM);
+ cs_header.setBorderTop(BorderStyle.MEDIUM);
+ cs_header.setBorderRight(BorderStyle.MEDIUM);
+ cs_header.setBorderBottom(BorderStyle.MEDIUM);
+ cs_header.setDataFormat(HSSFDataFormat.getBuiltinFormat("text"));
+ cs_header.setWrapText(true);
+ m_styles.put(key, cs_header);
+ }
+ return cs_header;
+ }
+
+ private void fixColumnWidth(XSSFSheet sheet, int lastColumnIndex)
+ {
+ for (short colnum = 0; colnum < lastColumnIndex; colnum++)
+ {
+ sheet.autoSizeColumn(colnum);
+ }
+ }
+
+ private void closeTableSheet(XSSFSheet prevSheet, String prevSheetName, int colCount)
+ {
+ if (prevSheet == null)
+ return;
+ //
+ fixColumnWidth(prevSheet, colCount);
+ if (m_colSplit >= 0 || m_rowSplit >= 0)
+ prevSheet.createFreezePane(m_colSplit >= 0 ? m_colSplit : 0, m_rowSplit >= 0 ? m_rowSplit : 0);
+ if (!Util.isEmpty(prevSheetName, true) && m_sheetCount > 0)
+ {
+ int prevSheetIndex = m_sheetCount - 1;
+ try
+ {
+ m_workbook.setSheetName(prevSheetIndex, prevSheetName);
+ }
+ catch (Exception e)
+ {
+ log.log(Level.WARNING, "Error setting sheet " + prevSheetIndex + " name to " + prevSheetName, e);
+ }
+ }
+ }
+
+ private XSSFSheet createTableSheet()
+ {
+ XSSFSheet sheet = m_workbook.createSheet();
+ formatPage(sheet);
+ createHeaderFooter(sheet);
+ createTableHeader(sheet);
+ m_sheetCount++;
+ //
+ return sheet;
+ }
+
+ private void createTableHeader(XSSFSheet sheet)
+ {
+ int colnumMax = 0;
+
+ XSSFRow row = sheet.createRow(0);
+ // for all columns
+ int colnum = 0;
+ for (int col = 0; col < getColumnCount(); col++)
+ {
+ if (colnum > colnumMax)
+ colnumMax = colnum;
+ //
+ if (isColumnPrinted(col))
+ {
+ XSSFCell cell = row.createCell(colnum);
+ // header row
+ XSSFCellStyle style = getHeaderStyle(col);
+ cell.setCellStyle(style);
+ String str = fixString(getHeaderName(col));
+ cell.setCellValue(new XSSFRichTextString(str));
+ colnum++;
+ } // printed
+ } // for all columns
+ }
+
+ protected void createHeaderFooter(XSSFSheet sheet)
+ {
+ // Sheet Header
+ Header header = sheet.getHeader();
+ header.setRight(HSSFHeader.page() + " / " + HSSFHeader.numPages());
+ // Sheet Footer
+ Footer footer = sheet.getFooter();
+ footer.setLeft(Env.getStandardReportFooterTrademarkText());
+ footer.setCenter(Env.getHeader(getCtx(), 0));
+ Timestamp now = new Timestamp(System.currentTimeMillis());
+ footer.setRight(DisplayType.getDateFormat(DisplayType.DateTime, getLanguage()).format(now));
+ }
+
+ protected void formatPage(XSSFSheet sheet)
+ {
+ sheet.setFitToPage(true);
+ // Print Setup
+ XSSFPrintSetup ps = sheet.getPrintSetup();
+ ps.setFitWidth((short) 1);
+ ps.setNoColor(true);
+ ps.setPaperSize(XSSFPrintSetup.A4_PAPERSIZE);
+ ps.setLandscape(false);
+ }
+
+ protected boolean isCurrentRowOnly()
+ {
+ return currentRowOnly;
+ }
+
+ protected void setCurrentRowOnly(boolean b)
+ {
+ currentRowOnly = b;
+ }
+
+ /**
+ * Export to given stream
+ *
+ * @param out
+ * @throws Exception
+ */
+ private void export(OutputStream out) throws Exception
+ {
+ XSSFSheet sheet = createTableSheet();
+ String sheetName = null;
+ //
+ int colnumMax = 0;
+ int rownum = isCurrentRowOnly() ? getCurrentRow() : 0;
+ int lastRowNum = isCurrentRowOnly() ? getCurrentRow() + 1 : getRowCount();
+ Object[] preValues = null;
+ int printColIndex = -1;
+ if (colSuppressRepeats != null)
+ {
+ preValues = new Object[colSuppressRepeats.length];
+ }
+
+ for (int xls_rownum = 1; rownum < lastRowNum; rownum++, xls_rownum++)
+ {
+ if (!isCurrentRowOnly())
+ setCurrentRow(rownum);
+
+ boolean isPageBreak = false;
+ XSSFRow row = sheet.createRow(xls_rownum);
+ printColIndex = -1;
+ // for all columns
+ int colnum = 0;
+ for (int col = 0; col < getColumnCount(); col++)
+ {
+ if (colnum > colnumMax)
+ colnumMax = colnum;
+ //
+ if (isColumnPrinted(col))
+ {
+ printColIndex++;
+ XSSFCell cell = row.createCell(colnum);
+
+ // line row
+ Object obj = getValueAt(rownum, col);
+ int displayType = getDisplayType(rownum, col);
+ if (obj == null)
+ {
+ if (colSuppressRepeats != null && colSuppressRepeats[printColIndex])
+ {
+ preValues[printColIndex] = null;
+ }
+ }
+ else if (colSuppressRepeats != null && colSuppressRepeats[printColIndex]
+ && obj.equals(preValues[printColIndex]))
+ {
+ // suppress
+ }
+ else if (DisplayType.isDate(displayType))
+ {
+ Timestamp value = (Timestamp) obj;
+ cell.setCellValue(value);
+ }
+ else if (DisplayType.isNumeric(displayType))
+ {
+ double value = 0;
+ if (obj instanceof Number)
+ {
+ value = ((Number) obj).doubleValue();
+ }
+ cell.setCellValue(value);
+ }
+ else if (DisplayType.YesNo == displayType)
+ {
+ boolean value = false;
+ if (obj instanceof Boolean)
+ value = (Boolean) obj;
+ else
+ value = "Y".equals(obj);
+ cell.setCellValue(new XSSFRichTextString(Msg.getMsg(getLanguage(), value == true ? "Y" : "N")));
+ }
+ else
+ {
+ String value = fixString(obj.toString()); // formatted
+ cell.setCellValue(new XSSFRichTextString(value));
+ }
+ //
+ XSSFCellStyle style = getStyle(rownum, col);
+ cell.setCellStyle(style);
+ // Page break
+ if (isPageBreak(rownum, col))
+ {
+ isPageBreak = true;
+ sheetName = fixString(cell.getRichStringCellValue().getString());
+ }
+ //
+ colnum++;
+ if (colSuppressRepeats != null)
+ preValues[printColIndex] = obj;
+ } // printed
+ } // for all columns
+ //
+ // Page Break
+ if (isPageBreak)
+ {
+ closeTableSheet(sheet, sheetName, colnumMax);
+ sheet = createTableSheet();
+ xls_rownum = 0;
+ isPageBreak = false;
+ }
+ } // for all rows
+ closeTableSheet(sheet, sheetName, colnumMax);
+ //
+
+ if (out != null)
+ {
+ m_workbook.write(out);
+ out.close();
+ }
+
+ //
+ // Workbook Info
+ if (log.isLoggable(Level.FINE))
+ {
+ log.fine("Sheets #" + m_sheetCount);
+ log.fine("Styles used #" + m_styles.size());
+ }
+ }
+
+ /**
+ * Export to file
+ *
+ * @param file
+ * @param language reporting language
+ * @throws Exception
+ */
+ public void export(File file, Language language) throws Exception
+ {
+ export(file, language, true);
+ }
+
+ /**
+ * Export to file
+ *
+ * @param file
+ * @param language reporting language
+ * @param autoOpen auto open file after generated
+ * @throws Exception
+ */
+ public void export(File file, Language language, boolean autoOpen) throws Exception
+ {
+ m_lang = language;
+ if (file == null)
+ file = File.createTempFile("Report_", ".xlsx");
+ FileOutputStream out = new FileOutputStream(file);
+ export(out);
+ if (autoOpen && Ini.isClient())
+ Env.startBrowser(file.toURI().toString());
+ }
+
+ public void exportToWorkbook(XSSFWorkbook workbook, Language language) throws Exception
+ {
+ m_lang = language;
+ m_workbook = workbook;
+ export(null);
+ }
+}
diff --git a/org.adempiere.base/src/org/adempiere/impexp/GridTabXLSXExporter.java b/org.adempiere.base/src/org/adempiere/impexp/GridTabXLSXExporter.java
new file mode 100644
index 0000000000..b77891c4b6
--- /dev/null
+++ b/org.adempiere.base/src/org/adempiere/impexp/GridTabXLSXExporter.java
@@ -0,0 +1,202 @@
+/******************************************************************************
+ * Copyright (C) 2018 Logilite Technologies LLP *
+ * This program is free software; you can redistribute it and/or modify it *
+ * under the terms version 2 of the GNU General Public License as published *
+ * by the Free Software Foundation. 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 General Public License for more details. *
+ * You should have received a copy of the GNU General Public License along *
+ * with this program; if not, write to the Free Software Foundation, Inc., *
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. *
+ *****************************************************************************/
+package org.adempiere.impexp;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.List;
+
+import org.adempiere.base.IGridTabExporter;
+import org.adempiere.exceptions.AdempiereException;
+import org.compiere.model.GridField;
+import org.compiere.model.GridTab;
+import org.compiere.model.Lookup;
+import org.compiere.model.MLookup;
+import org.compiere.model.MLookupFactory;
+import org.compiere.util.DisplayType;
+import org.compiere.util.Env;
+import org.compiere.util.Msg;
+import org.compiere.util.Util;
+
+/**
+ * Excel Exporter Adapter for GridTab
+ *
+ * @author Deepak Pansheriya
+ */
+public class GridTabXLSXExporter extends AbstractXLSXExporter implements IGridTabExporter
+{
+ private GridTab m_tab = null;
+
+ public GridTabXLSXExporter()
+ {
+ setFreezePane(0, 1);
+ }
+
+ @Override
+ public int getColumnCount()
+ {
+ return m_tab.getFieldCount();
+ }
+
+ @Override
+ public int getDisplayType(int row, int col)
+ {
+ return m_tab.getField(col).getDisplayType();
+ }
+
+ @Override
+ public String getHeaderName(int col)
+ {
+ return m_tab.getField(col).getHeader();
+ }
+
+ @Override
+ public int getRowCount()
+ {
+ return m_tab.getRowCount();
+ }
+
+ @Override
+ public Object getValueAt(int row, int col)
+ {
+ GridField f = m_tab.getField(col);
+ Object key = m_tab.getValue(row, f.getColumnName());
+ Object value = key;
+ Lookup lookup = f.getLookup();
+ if (lookup != null)
+ {
+ ;
+ }
+ else if (f.getDisplayType() == DisplayType.Button)
+ {
+ lookup = getButtonLookup(f);
+ }
+
+ if (lookup != null)
+ {
+ value = lookup.getDisplay(key);
+ }
+ return value;
+ }
+
+ @Override
+ public boolean isColumnPrinted(int col)
+ {
+ GridField f = m_tab.getField(col);
+ // Hide not displayed fields
+ if (!f.isDisplayed())
+ return false;
+ // Hide encrypted fields
+ if (f.isEncrypted())
+ return false;
+ // Hide simple button fields without a value
+ if (f.getDisplayType() == DisplayType.Button && f.getAD_Reference_Value_ID() == 0)
+ return false;
+ return true;
+ }
+
+ @Override
+ public boolean isFunctionRow()
+ {
+ return false;
+ }
+
+ @Override
+ public boolean isPageBreak(int row, int col)
+ {
+ return false;
+ }
+
+ @Override
+ protected void setCurrentRow(int row)
+ {
+ ; // nothing
+ }
+
+ protected int getCurrentRow()
+ {
+ return m_tab.getCurrentRow();
+ }
+
+ private HashMap m_buttonLookups = new HashMap();
+
+ private MLookup getButtonLookup(GridField mField)
+ {
+ MLookup lookup = m_buttonLookups.get(mField.getColumnName());
+ if (lookup != null)
+ return lookup;
+ // TODO: refactor with org.compiere.grid.ed.VButton.setField(GridField)
+ if (mField.getColumnName().endsWith("_ID") && !mField.getColumnName().equals("Record_ID"))
+ {
+ lookup = MLookupFactory.get(Env.getCtx(), mField.getWindowNo(), 0, mField.getAD_Column_ID(),
+ DisplayType.Search);
+ }
+ else if (mField.getAD_Reference_Value_ID() != 0)
+ {
+ // Assuming List
+ lookup = MLookupFactory.get(Env.getCtx(), mField.getWindowNo(), 0, mField.getAD_Column_ID(),
+ DisplayType.List);
+ }
+ //
+ m_buttonLookups.put(mField.getColumnName(), lookup);
+ return lookup;
+ }
+
+ @Override
+ public void export(GridTab gridTab, List childs, boolean currentRowOnly, File file, int indxDetailSelected)
+ {
+ m_tab = gridTab;
+ setCurrentRowOnly(currentRowOnly);
+ try
+ {
+ export(file, null);
+ }
+ catch (Exception e)
+ {
+ throw new AdempiereException(e);
+ }
+ }
+
+ @Override
+ public String getFileExtension()
+ {
+ return "xlsx";
+ }
+
+ @Override
+ public String getFileExtensionLabel()
+ {
+ return Msg.getMsg(Env.getCtx(), "FileXLSX");
+ }
+
+ @Override
+ public String getContentType()
+ {
+ return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
+ }
+
+ @Override
+ public String getSuggestedFileName(GridTab gridTab)
+ {
+ return gridTab.getName() + "." + getFileExtension();
+ }
+
+ /**
+ * {@inheritDoc} no detail tab is support to export with excel
+ */
+ @Override
+ public boolean isExportableTab(GridTab gridTab)
+ {
+ return false;
+ }
+}
diff --git a/org.adempiere.base/src/org/adempiere/print/export/PrintDataXLSXExporter.java b/org.adempiere.base/src/org/adempiere/print/export/PrintDataXLSXExporter.java
new file mode 100644
index 0000000000..93fed0da02
--- /dev/null
+++ b/org.adempiere.base/src/org/adempiere/print/export/PrintDataXLSXExporter.java
@@ -0,0 +1,216 @@
+/******************************************************************************
+ * Copyright (C) 2018 Logilite Technologies LLP *
+ * This program is free software; you can redistribute it and/or modify it *
+ * under the terms version 2 of the GNU General Public License as published *
+ * by the Free Software Foundation. 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 General Public License for more details. *
+ * You should have received a copy of the GNU General Public License along *
+ * with this program; if not, write to the Free Software Foundation, Inc., *
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. *
+ *****************************************************************************/
+package org.adempiere.print.export;
+
+import java.sql.Timestamp;
+import java.util.Date;
+
+import javax.print.attribute.standard.MediaSizeName;
+
+import org.adempiere.impexp.AbstractXLSXExporter;
+import org.apache.poi.hssf.usermodel.HSSFPrintSetup;
+import org.apache.poi.hssf.usermodel.HSSFSheet;
+import org.apache.poi.xssf.usermodel.XSSFSheet;
+import org.compiere.print.MPrintFormat;
+import org.compiere.print.MPrintFormatItem;
+import org.compiere.print.MPrintPaper;
+import org.compiere.print.PrintData;
+import org.compiere.print.PrintDataElement;
+
+/**
+ * Export PrintData to Excel (XLSX) file
+ *
+ * @author Deepak Pansheriya
+ */
+public class PrintDataXLSXExporter extends AbstractXLSXExporter
+{
+ private PrintData m_printData;
+ private MPrintFormat m_printFormat;
+
+ public PrintDataXLSXExporter(PrintData printData, MPrintFormat printFormat, Boolean[] colSuppressRepeats)
+ {
+ super();
+ this.m_printData = printData;
+ this.m_printFormat = printFormat;
+ this.colSuppressRepeats = colSuppressRepeats;
+ }
+
+ @Override
+ public int getColumnCount()
+ {
+ return m_printFormat.getItemCount();
+ }
+
+ private PrintDataElement getPDE(int row, int col)
+ {
+ if (m_printData.getRowIndex() != row)
+ m_printData.setRowIndex(row);
+ //
+ MPrintFormatItem item = m_printFormat.getItem(col);
+ int AD_Column_ID = item.getAD_Column_ID();
+ Object obj = null;
+ if (AD_Column_ID > 0)
+ obj = m_printData.getNode(Integer.valueOf(AD_Column_ID));
+ if (obj != null && obj instanceof PrintDataElement)
+ {
+ return (PrintDataElement) obj;
+ }
+ return null;
+ }
+
+ @Override
+ public int getDisplayType(int row, int col)
+ {
+ PrintDataElement pde = getPDE(row, col);
+ if (pde != null)
+ {
+ return pde.getDisplayType();
+ }
+ return -1;
+ //
+ }
+
+ @Override
+ public Object getValueAt(int row, int col)
+ {
+ PrintDataElement pde = getPDE(row, col);
+ Object value = null;
+ if (pde == null)
+ ;
+ else if (pde.isDate())
+ {
+ Object o = pde.getValue();
+ if (o instanceof Date)
+ value = new Timestamp(((Date) o).getTime());
+ else
+ value = (Timestamp) pde.getValue();
+ }
+ else if (pde.isNumeric())
+ {
+ Object o = pde.getValue();
+ if (o instanceof Number)
+ {
+ value = ((Number) o).doubleValue();
+ }
+ }
+ else if (pde.isYesNo())
+ {
+ value = pde.getValue();
+ }
+ else if (pde.isPKey())
+ {
+ value = pde.getValueAsString();
+ }
+ else
+ {
+ value = pde.getValueDisplay(getLanguage());
+ }
+ //
+ return value;
+ }
+
+ @Override
+ public String getHeaderName(int col)
+ {
+ return m_printFormat.getItem(col).getPrintName(getLanguage());
+ }
+
+ @Override
+ public int getRowCount()
+ {
+ return m_printData.getRowCount();
+ }
+
+ @Override
+ public boolean isColumnPrinted(int col)
+ {
+ MPrintFormatItem item = m_printFormat.getItem(col);
+ return item.isPrinted();
+ }
+
+ @Override
+ public boolean isPageBreak(int row, int col)
+ {
+ PrintDataElement pde = getPDE(row, col);
+ return pde != null ? pde.isPageBreak() : false;
+ }
+
+ @Override
+ protected void setCurrentRow(int row)
+ {
+ m_printData.setRowIndex(row);
+ }
+
+ protected int getCurrentRow()
+ {
+ return m_printData.getRowIndex();
+ }
+
+ @Override
+ public boolean isFunctionRow()
+ {
+ return m_printData.isFunctionRow();
+ }
+
+ @Override
+ protected void formatPage(XSSFSheet sheet)
+ {
+ super.formatPage(sheet);
+ MPrintPaper paper = MPrintPaper.get(this.m_printFormat.getAD_PrintPaper_ID());
+
+ // Set paper size:
+ short paperSize = -1;
+ MediaSizeName mediaSizeName = paper.getMediaSize().getMediaSizeName();
+ if (MediaSizeName.NA_LETTER.equals(mediaSizeName))
+ {
+ paperSize = HSSFPrintSetup.LETTER_PAPERSIZE;
+ }
+ else if (MediaSizeName.NA_LEGAL.equals(mediaSizeName))
+ {
+ paperSize = HSSFPrintSetup.LEGAL_PAPERSIZE;
+ }
+ else if (MediaSizeName.EXECUTIVE.equals(mediaSizeName))
+ {
+ paperSize = HSSFPrintSetup.EXECUTIVE_PAPERSIZE;
+ }
+ else if (MediaSizeName.ISO_A4.equals(mediaSizeName))
+ {
+ paperSize = HSSFPrintSetup.A4_PAPERSIZE;
+ }
+ else if (MediaSizeName.ISO_A5.equals(mediaSizeName))
+ {
+ paperSize = HSSFPrintSetup.A5_PAPERSIZE;
+ }
+ else if (MediaSizeName.NA_NUMBER_10_ENVELOPE.equals(mediaSizeName))
+ {
+ paperSize = HSSFPrintSetup.ENVELOPE_10_PAPERSIZE;
+ }
+ else if (MediaSizeName.MONARCH_ENVELOPE.equals(mediaSizeName))
+ {
+ paperSize = HSSFPrintSetup.ENVELOPE_MONARCH_PAPERSIZE;
+ }
+ if (paperSize != -1)
+ {
+ sheet.getPrintSetup().setPaperSize(paperSize);
+ }
+
+ // Set Landscape/Portrait:
+ sheet.getPrintSetup().setLandscape(paper.isLandscape());
+
+ // Set Paper Margin:
+ sheet.setMargin(HSSFSheet.TopMargin, ((double) paper.getMarginTop()) / 72);
+ sheet.setMargin(HSSFSheet.RightMargin, ((double) paper.getMarginRight()) / 72);
+ sheet.setMargin(HSSFSheet.LeftMargin, ((double) paper.getMarginLeft()) / 72);
+ sheet.setMargin(HSSFSheet.BottomMargin, ((double) paper.getMarginBottom()) / 72);
+ }
+}
diff --git a/org.adempiere.base/src/org/compiere/print/ReportEngine.java b/org.adempiere.base/src/org/compiere/print/ReportEngine.java
index ebec1e8a73..de3c2098a1 100644
--- a/org.adempiere.base/src/org/compiere/print/ReportEngine.java
+++ b/org.adempiere.base/src/org/compiere/print/ReportEngine.java
@@ -63,6 +63,7 @@ import javax.xml.transform.stream.StreamResult;
import org.adempiere.exceptions.AdempiereException;
import org.adempiere.pdf.Document;
import org.adempiere.print.export.PrintDataExcelExporter;
+import org.adempiere.print.export.PrintDataXLSXExporter;
import org.apache.ecs.XhtmlDocument;
import org.apache.ecs.xhtml.a;
import org.apache.ecs.xhtml.script;
@@ -1483,6 +1484,20 @@ queued-job-count = 0 (class javax.print.attribute.standard.QueuedJobCount)
exp.export(outFile, language);
}
+ /**
+ * Create ExcelX file
+ * @param outFile output file
+ * @param language
+ * @throws Exception if error
+ */
+ public void createXLSX(File outFile, Language language)
+ throws Exception
+ {
+ Boolean [] colSuppressRepeats = m_layout == null || m_layout.colSuppressRepeats == null? LayoutEngine.getColSuppressRepeats(m_printFormat):m_layout.colSuppressRepeats;
+ PrintDataXLSXExporter exp = new PrintDataXLSXExporter(getPrintData(), getPrintFormat(), colSuppressRepeats);
+ exp.export(outFile, language);
+ }
+
/**************************************************************************
* Get Report Engine for process info
* @param ctx context
diff --git a/org.adempiere.server-feature/setup/configuration/config.ini b/org.adempiere.server-feature/setup/configuration/config.ini
index 695a4073eb..a945a57992 100644
--- a/org.adempiere.server-feature/setup/configuration/config.ini
+++ b/org.adempiere.server-feature/setup/configuration/config.ini
@@ -67,7 +67,8 @@ osgi.bundles=org.eclipse.equinox.ds@1:start,\
com.google.zxing.core,\
org.apache.geronimo.specs.geronimo-j2ee-management_1.1_spec,\
jakarta.xml.bind-api,\
- org.eclipse.osgi@start
+ org.eclipse.osgi@start,\
+ org.dom4j
osgi.framework.extensions=
osgi.bundles.defaultStartLevel=4
osgi.compatibility.bootdelegation=true
diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WReportCustomization.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WReportCustomization.java
index 05eb5e57ae..7a020531c7 100644
--- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WReportCustomization.java
+++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WReportCustomization.java
@@ -470,6 +470,7 @@ public class WReportCustomization implements IFormController,EventListener
cboExportType.appendItem("txt" + " - " + Msg.getMsg(Env.getCtx(), "FileTXT"), "txt");
cboExportType.appendItem("ssv" + " - " + Msg.getMsg(Env.getCtx(), "FileSSV"), "ssv");
cboExportType.appendItem("csv" + " - " + Msg.getMsg(Env.getCtx(), "FileCSV"), "csv");
+ cboExportType.appendItem("xlsx" + " - " + Msg.getMsg(Env.getCtx(), "FileXLSX"), "xlsx");
ListItem li = cboExportType.appendItem("xls" + " - " + Msg.getMsg(Env.getCtx(), "FileXLS"), "xls");
cboExportType.setSelectedItem(li);
cboExportType.setVisible(false);
@@ -440,6 +441,11 @@ public class ReportAction implements EventListener
inputFile = File.createTempFile("Export", ".xls");
re.createXLS(inputFile, re.getPrintFormat().getLanguage());
}
+ else if (ext.equals("xlsx"))
+ {
+ inputFile = File.createTempFile("Export", ".xlsx");
+ re.createXLSX(inputFile, re.getPrintFormat().getLanguage());
+ }
else
{
FDialog.error(0, winReport, "FileInvalidExtension");
diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/window/ZkReportViewer.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/window/ZkReportViewer.java
index 0b81c48813..abc3dc3f08 100644
--- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/window/ZkReportViewer.java
+++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/window/ZkReportViewer.java
@@ -280,6 +280,7 @@ public class ZkReportViewer extends Window implements EventListener, ITab
{
previewType.appendItem("Excel", "XLS");
previewType.appendItem("CSV", "CSV");
+ previewType.appendItem("Excel X", "XLSX");
}
toolBar.appendChild(previewType);
@@ -658,7 +659,9 @@ public class ZkReportViewer extends Window implements EventListener, ITab
future = Adempiere.getThreadPoolExecutor().submit(new DesktopRunnable(new XLSRendererRunnable(this),getDesktop()));
} else if ("CSV".equals(previewType.getSelectedItem().getValue())) {
future = Adempiere.getThreadPoolExecutor().submit(new DesktopRunnable(new CSVRendererRunnable(this),getDesktop()));
- }
+ } else if ("XLSX".equals(previewType.getSelectedItem().getValue())) {
+ future = Adempiere.getThreadPoolExecutor().submit(new DesktopRunnable(new XLSXRendererRunnable(this),getDesktop()));
+ }
}
private void onPreviewReport() {
@@ -1135,6 +1138,7 @@ public class ZkReportViewer extends Window implements EventListener, ITab
cboType.appendItem("ssv" + " - " + Msg.getMsg(Env.getCtx(), "FileSSV"), "ssv");
cboType.appendItem("csv" + " - " + Msg.getMsg(Env.getCtx(), "FileCSV"), "csv");
cboType.appendItem("xls" + " - " + Msg.getMsg(Env.getCtx(), "FileXLS"), "xls");
+ cboType.appendItem("xlsx" + " - " + Msg.getMsg(Env.getCtx(), "FileXLSX"), "xlsx");
cboType.setSelectedItem(li);
Hbox hb = new Hbox();
@@ -1218,6 +1222,11 @@ public class ZkReportViewer extends Window implements EventListener, ITab
m_reportEngine.createHTML(sw, false, m_reportEngine.getPrintFormat().getLanguage(), new HTMLExtension(contextPath, "rp", this.getUuid()), true);
data = sw.getBuffer().toString().getBytes();
}
+ else if (ext.equals("xlsx"))
+ {
+ inputFile = File.createTempFile("Export", ".xlsx");
+ m_reportEngine.createXLSX(inputFile, m_reportEngine.getPrintFormat().getLanguage());
+ }
else if (ext.equals("xls"))
{
inputFile = File.createTempFile("Export", ".xls");
@@ -1724,4 +1733,61 @@ public class ZkReportViewer extends Window implements EventListener, ITab
}
}
+
+ protected static class XLSXRendererRunnable extends ContextRunnable implements IServerPushCallback
+ {
+
+ private ZkReportViewer viewer;
+
+ public XLSXRendererRunnable(ZkReportViewer viewer)
+ {
+ super();
+ this.viewer = viewer;
+ }
+
+ @Override
+ protected void doRun()
+ {
+ try
+ {
+ if (!ArchiveEngine.isValid(viewer.m_reportEngine.getLayout()))
+ log.warning("Cannot archive Document");
+ String path = System.getProperty("java.io.tmpdir");
+ String prefix = viewer.makePrefix(viewer.m_reportEngine.getName());
+ if (log.isLoggable(Level.FINE))
+ {
+ log.log(Level.FINE, "Path=" + path + " Prefix=" + prefix);
+ }
+ File file = File.createTempFile(prefix, ".xlsx", new File(path));
+ viewer.m_reportEngine.createXLSX(file, viewer.m_reportEngine.getPrintFormat().getLanguage());
+ viewer.media = new AMedia(file.getName(), "xlsx",
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", file, true);
+ }
+ catch (Exception e)
+ {
+ if (e instanceof RuntimeException)
+ throw (RuntimeException) e;
+ else
+ throw new RuntimeException(e);
+ }
+ finally
+ {
+ Desktop desktop = AEnv.getDesktop();
+ if (desktop != null && desktop.isAlive())
+ {
+ new ServerPushTemplate(desktop).executeAsync(this);
+ }
+ }
+ }
+
+ @Override
+ public void updateUI()
+ {
+ viewer.labelDrill.setVisible(false);
+ viewer.comboDrill.setVisible(false);
+ viewer.onPreviewReport();
+ }
+
+ }
+
}