From b188775e9a0cbfc6d3781bcce2ea87617046997b Mon Sep 17 00:00:00 2001 From: hengsin Date: Mon, 13 Mar 2023 22:57:45 +0800 Subject: [PATCH] IDEMPIERE-5624 Implement export data as SQL insert statement (#1721) --- .../oracle/202303111206_IDEMPIERE-5624.sql | 14 ++ .../202303111206_IDEMPIERE-5624.sql | 11 + org.adempiere.base/plugin.xml | 9 + .../org/adempiere/base/IGridTabExporter.java | 40 +++- .../impexp/GridTabSQLInsertExporter.java | 207 ++++++++++++++++++ .../src/org/compiere/model/GridTab.java | 3 - .../src/org/compiere/model/PO.java | 176 +++++++++------ .../src/org/compiere/model/POInfo.java | 11 + .../webui/panel/action/ExportAction.java | 118 ++++++---- 9 files changed, 467 insertions(+), 122 deletions(-) create mode 100644 migration/iD11/oracle/202303111206_IDEMPIERE-5624.sql create mode 100644 migration/iD11/postgresql/202303111206_IDEMPIERE-5624.sql create mode 100644 org.adempiere.base/src/org/adempiere/impexp/GridTabSQLInsertExporter.java diff --git a/migration/iD11/oracle/202303111206_IDEMPIERE-5624.sql b/migration/iD11/oracle/202303111206_IDEMPIERE-5624.sql new file mode 100644 index 0000000000..e5cc26889f --- /dev/null +++ b/migration/iD11/oracle/202303111206_IDEMPIERE-5624.sql @@ -0,0 +1,14 @@ +-- IDEMPIERE-5624 Implement export data as SQL insert statement +SELECT register_migration_script('202303111206_IDEMPIERE-5624.sql') FROM dual; + +SET SQLBLANKLINES ON +SET DEFINE OFF + +-- Mar 11, 2023, 12:06:48 PM MYT +INSERT INTO AD_Message (MsgType,MsgText,MsgTip,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,AD_Message_ID,Value,EntityType,AD_Message_UU) VALUES ('I','zip - SQL zip file','Zip archive of Oracle and PostgreSQL Insert SQL scripts',0,0,'Y',TO_TIMESTAMP('2023-03-11 12:06:46','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2023-03-11 12:06:46','YYYY-MM-DD HH24:MI:SS'),100,200825,'FileSQLInsertZip','D','9308f542-5ab1-4462-90a3-c0d70b2eb009') +; + +-- Mar 11, 2023, 12:11:29 PM MYT +UPDATE AD_Message SET MsgText='zip - SQL Insert scripts zip archive', MsgTip=NULL,Updated=TO_TIMESTAMP('2023-03-11 12:11:29','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Message_ID=200825 +; + diff --git a/migration/iD11/postgresql/202303111206_IDEMPIERE-5624.sql b/migration/iD11/postgresql/202303111206_IDEMPIERE-5624.sql new file mode 100644 index 0000000000..769b0d3b47 --- /dev/null +++ b/migration/iD11/postgresql/202303111206_IDEMPIERE-5624.sql @@ -0,0 +1,11 @@ +-- IDEMPIERE-5624 Implement export data as SQL insert statement +SELECT register_migration_script('202303111206_IDEMPIERE-5624.sql') FROM dual; + +-- Mar 11, 2023, 12:06:48 PM MYT +INSERT INTO AD_Message (MsgType,MsgText,MsgTip,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,AD_Message_ID,Value,EntityType,AD_Message_UU) VALUES ('I','zip - SQL zip file','Zip archive of Oracle and PostgreSQL Insert SQL scripts',0,0,'Y',TO_TIMESTAMP('2023-03-11 12:06:46','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2023-03-11 12:06:46','YYYY-MM-DD HH24:MI:SS'),100,200825,'FileSQLInsertZip','D','9308f542-5ab1-4462-90a3-c0d70b2eb009') +; + +-- Mar 11, 2023, 12:11:29 PM MYT +UPDATE AD_Message SET MsgText='zip - SQL Insert scripts zip archive', MsgTip=NULL,Updated=TO_TIMESTAMP('2023-03-11 12:11:29','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Message_ID=200825 +; + diff --git a/org.adempiere.base/plugin.xml b/org.adempiere.base/plugin.xml index 51cab1bbee..cc5faadfd3 100644 --- a/org.adempiere.base/plugin.xml +++ b/org.adempiere.base/plugin.xml @@ -119,4 +119,13 @@ priority="0"> + + + + diff --git a/org.adempiere.base/src/org/adempiere/base/IGridTabExporter.java b/org.adempiere.base/src/org/adempiere/base/IGridTabExporter.java index 3f14d0e17f..3c33a67f91 100644 --- a/org.adempiere.base/src/org/adempiere/base/IGridTabExporter.java +++ b/org.adempiere.base/src/org/adempiere/base/IGridTabExporter.java @@ -19,9 +19,8 @@ import java.util.List; import org.compiere.model.GridTab; /** - * + * Interface to export data from {@link GridTab} * @author hengsin - * */ public interface IGridTabExporter { @@ -31,21 +30,22 @@ public interface IGridTabExporter { * @param childs * @param isCurrentRowOnly * @param file + * @param indxDetailSelected index of selected child tab */ public void export(GridTab gridTab, List childs, boolean isCurrentRowOnly, File file, int indxDetailSelected); /** - * @return file extension + * @return file extension (csv, zip, ect) */ public String getFileExtension(); /** - * @return description for file extension + * @return label for file extension */ public String getFileExtensionLabel(); /** - * @return mime type + * @return mime content type */ public String getContentType(); @@ -55,9 +55,31 @@ public interface IGridTabExporter { public String getSuggestedFileName(GridTab gridTab); /** - * Check a tab (detail tab) is support to export in this exporter - * @param gridTab - * @return + * Check if exported support the export of a child tab + * @param childTab + * @return true if export is supported, false otherwise */ - public boolean isExportableTab (GridTab gridTab); + public boolean isExportableTab (GridTab childTab); + + /** + * @return true if exporter is available to role with advanced access only + */ + default boolean isAdvanced() { + return false; + } + + /** + * @return true if export of child tabs is supported only when current row only is on. + */ + default boolean isExportChildTabsForCurrentRowOnly() { + return false; + } + + /** + * Maximum deep of child tab supported by the exporter + * @return > 0 for maximum level of deep, <= 0 for unlimited level of deep + */ + default int maxDeepOfChildTab() { + return 0; + } } diff --git a/org.adempiere.base/src/org/adempiere/impexp/GridTabSQLInsertExporter.java b/org.adempiere.base/src/org/adempiere/impexp/GridTabSQLInsertExporter.java new file mode 100644 index 0000000000..402ac04bb0 --- /dev/null +++ b/org.adempiere.base/src/org/adempiere/impexp/GridTabSQLInsertExporter.java @@ -0,0 +1,207 @@ +/*********************************************************************** + * This file is part of iDempiere ERP Open Source * + * http://www.idempiere.org * + * * + * Copyright (C) Contributors * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * 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 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., 51 Franklin Street, Fifth Floor, Boston, * + * MA 02110-1301, USA. * + * * + * Contributors: * + * - hengsin * + **********************************************************************/ +package org.adempiere.impexp; + +import java.io.File; +import java.io.FileOutputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +import org.adempiere.base.IGridTabExporter; +import org.adempiere.exceptions.AdempiereException; +import org.compiere.db.Database; +import org.compiere.model.GridTab; +import org.compiere.model.MTable; +import org.compiere.model.PO; +import org.compiere.util.Env; +import org.compiere.util.Msg; + +/** + * Export data as SQL insert statement + * @author hengsin + */ +public class GridTabSQLInsertExporter implements IGridTabExporter { + + /** + * Default constructor + */ + public GridTabSQLInsertExporter() { + } + + @Override + public void export(GridTab gridTab, List childs, boolean isCurrentRowOnly, File file, + int indxDetailSelected) { + try (FileOutputStream fos = new FileOutputStream (file); ZipOutputStream zos = new ZipOutputStream(fos);){ + // create directory "oracle" + ZipEntry directoryEntry = new ZipEntry("oracle/"); + zos.putNextEntry(directoryEntry); + zos.closeEntry(); + + // create directory "postgresql" + directoryEntry = new ZipEntry("postgresql/"); + zos.putNextEntry(directoryEntry); + zos.closeEntry(); + + //export header tab + MTable table = MTable.get(gridTab.getAD_Table_ID()); + List oracles = new ArrayList<>(); + List pgs = new ArrayList<>(); + if (isCurrentRowOnly) { + PO po = getPO(gridTab, table, gridTab.getCurrentRow()); + if (po != null) + addSQLInsert(po, oracles, pgs); + } else { + for(int i = 0; i < gridTab.getRowCount(); i++) { + PO po = getPO(gridTab, table, i); + if (po != null) + addSQLInsert(po, oracles, pgs); + } + } + + ZipEntry fileEntry = new ZipEntry("oracle/" + table.getTableName() + ".sql"); + zos.putNextEntry(fileEntry); + for(String oracle : oracles) + zos.write((oracle+"\n;\n").getBytes()); + zos.closeEntry(); + + fileEntry = new ZipEntry("postgresql/" + table.getTableName() + ".sql"); + zos.putNextEntry(fileEntry); + for(String pg : pgs) + zos.write((pg+"\n;\n").getBytes()); + zos.closeEntry(); + + //export child tabs + if (isCurrentRowOnly) { + for(GridTab childTab : childs) { + if (!childTab.isLoadComplete()){ + childTab.initTab(false); + childTab.query(false, 0, 0); + } + if (childTab.getRowCount() == 0) + continue; + table = MTable.get(childTab.getAD_Table_ID()); + oracles = new ArrayList<>(); + pgs = new ArrayList<>(); + for(int i = 0; i < childTab.getRowCount(); i++) { + PO po = getPO(childTab, table, i); + if (po != null) + addSQLInsert(po, oracles, pgs); + } + fileEntry = new ZipEntry("oracle/" + table.getTableName() + ".sql"); + zos.putNextEntry(fileEntry); + for(String oracle : oracles) + zos.write((oracle+"\n;\n").getBytes()); + zos.closeEntry(); + + fileEntry = new ZipEntry("postgresql/" + table.getTableName() + ".sql"); + zos.putNextEntry(fileEntry); + for(String pg : pgs) + zos.write((pg+"\n;\n").getBytes()); + zos.closeEntry(); + } + } + } catch (Exception e) { + throw new AdempiereException(e); + } + } + + /** + * Create SQL insert script for po + * @param po + * @param oracles list to add oracle insert script + * @param pgs list to add postgresql insert script + */ + protected void addSQLInsert(PO po, List oracles, List pgs) { + String sql = po.toInsertSQL(); + String oracle = Database.getDatabase(Database.DB_ORACLE).convertStatement(sql); + String pg = Database.getDatabase(Database.DB_POSTGRESQL).convertStatement(sql); + oracles.add(oracle); + pgs.add(pg); + } + + /** + * Load PO by record ID or UUID + * @param gridTab + * @param table + * @param row + * @return PO + */ + protected PO getPO(GridTab gridTab, MTable table, int row) { + int recordId = gridTab.getKeyID(row); + if (recordId >= 0) { + return table.getPO(gridTab.getKeyID(row), null); + } else { + UUID uuid = gridTab.getTableModel().getUUID(row); + if (uuid != null) + return table.getPOByUU(uuid.toString(), null); + } + return null; + } + + @Override + public String getFileExtension() { + return "zip"; + } + + @Override + public String getFileExtensionLabel() { + return Msg.getMsg(Env.getCtx(), "FileSQLInsertZip"); + } + + @Override + public String getContentType() { + return "application/zip"; + } + + @Override + public String getSuggestedFileName(GridTab gridTab) { + return gridTab.getName() + "." + getFileExtension(); + } + + @Override + public boolean isExportableTab(GridTab gridTab) { + if (!gridTab.isDisplayed()) + return false; + return true; + } + + @Override + public boolean isAdvanced() { + return true; + } + + @Override + public boolean isExportChildTabsForCurrentRowOnly() { + return true; + } + + @Override + public int maxDeepOfChildTab() { + return 1; + } +} diff --git a/org.adempiere.base/src/org/compiere/model/GridTab.java b/org.adempiere.base/src/org/compiere/model/GridTab.java index 60b2410564..1f75b96317 100644 --- a/org.adempiere.base/src/org/compiere/model/GridTab.java +++ b/org.adempiere.base/src/org/compiere/model/GridTab.java @@ -1579,9 +1579,6 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable return true; // ** dynamic content ** - String parsed = Env.parseContext (m_vo.ctx, 0, dl, false, false).trim(); - if (parsed.length() == 0) - return true; boolean retValue = Evaluator.evaluateLogic(this, dl); if (log.isLoggable(Level.CONFIG)) log.config(m_vo.Name + " (" + dl + ") => " + retValue); return retValue; diff --git a/org.adempiere.base/src/org/compiere/model/PO.java b/org.adempiere.base/src/org/compiere/model/PO.java index bc54b23170..8dc80f54db 100644 --- a/org.adempiere.base/src/org/compiere/model/PO.java +++ b/org.adempiere.base/src/org/compiere/model/PO.java @@ -3177,7 +3177,6 @@ public abstract class PO } // saveNew private boolean doInsert(boolean withValues) { - int index; lobReset(); // Change Log @@ -3188,9 +3187,94 @@ public abstract class PO //params for insert statement List params = new ArrayList(); - + // SQL - StringBuilder sqlInsert = new StringBuilder("INSERT INTO "); + StringBuilder sqlInsert = new StringBuilder(); + AD_ChangeLog_ID = buildInsertSQL(sqlInsert, withValues, params, session, AD_ChangeLog_ID, false); + // + int no = withValues ? DB.executeUpdate(sqlInsert.toString(), m_trxName) + : DB.executeUpdate(sqlInsert.toString(), params.toArray(), false, m_trxName); + boolean ok = no == 1; + if (ok) + { + if (withValues && m_IDs.length == 1 && p_info.hasKeyColumn() + && m_KeyColumns[0].endsWith("_ID") && !Env.isUseCentralizedId(p_info.getTableName())) + { + int id = DB.getSQLValueEx(get_TrxName(), "SELECT " + m_KeyColumns[0] + " FROM " + + p_info.getTableName() + " WHERE " + getUUIDColumnName() + "=?", get_ValueAsString(getUUIDColumnName())); + m_IDs[0] = Integer.valueOf(id); + set_ValueNoCheck(m_KeyColumns[0], m_IDs[0]); + + int ki = p_info.getColumnIndex(m_KeyColumns[0]); + // Change Log - Only + String insertLog = MSysConfig.getValue(MSysConfig.SYSTEM_INSERT_CHANGELOG, "Y", getAD_Client_ID()); + if ( session != null + && m_IDs.length == 1 + && p_info.isAllowLogging(ki) // logging allowed + && !p_info.isEncrypted(ki) // not encrypted + && !p_info.isVirtualColumn(ki) // no virtual column + && !"Password".equals(p_info.getColumnName(ki)) + && (insertLog.equalsIgnoreCase("Y") + || (insertLog.equalsIgnoreCase("K") && p_info.getColumn(ki).IsKey)) + ) + { + // change log on new + MChangeLog cLog = session.changeLog ( + m_trxName, AD_ChangeLog_ID, + p_info.getAD_Table_ID(), p_info.getColumn(ki).AD_Column_ID, + get_ID(), getAD_Client_ID(), getAD_Org_ID(), null, id, MChangeLog.EVENTCHANGELOG_Insert); + if (cLog != null) + AD_ChangeLog_ID = cLog.getAD_ChangeLog_ID(); + } + } + ok = lobSave(); + if (!load(m_trxName)) // re-read Info + { + if (m_trxName == null) + log.log(Level.SEVERE, "reloading"); + else + log.log(Level.SEVERE, "[" + m_trxName + "] - reloading"); + ok = false;; + } + } + else + { + String msg = "Not inserted - "; + if (CLogMgt.isLevelFiner()) + msg += sqlInsert.toString(); + else + msg += get_TableName(); + if (m_trxName == null) + log.log(Level.WARNING, msg); + else + log.log(Level.WARNING, "[" + m_trxName + "]" + msg); + } + return ok; + } + + /** + * Export data as insert SQL statement + */ + public String toInsertSQL() + { + StringBuilder sqlInsert = new StringBuilder(); + buildInsertSQL(sqlInsert, true, null, null, 0, true); + return sqlInsert.toString(); + } + + /** + * Build insert SQL statement and capture change log + * @param sqlInsert + * @param withValues true to create statement with column values, false to use parameter binding (i.e with ?) + * @param params statement parameters when withValues is false + * @param session to capture change log. null when call from toInsertSQL (i.e to build sql only, not for real insert to DB) + * @param AD_ChangeLog_ID initial change log id + * @param generateScriptOnly true if it is to generate sql script only, false for real DB insert + * @return last AD_ChangeLog_ID + */ + protected int buildInsertSQL(StringBuilder sqlInsert, boolean withValues, List params, MSession session, + int AD_ChangeLog_ID, boolean generateScriptOnly) { + sqlInsert.append("INSERT INTO "); sqlInsert.append(p_info.getTableName()).append(" ("); StringBuilder sqlValues = new StringBuilder(") VALUES ("); int size = get_ColumnCount(); @@ -3212,6 +3296,13 @@ public abstract class PO continue; } + //do not export secure column + if (generateScriptOnly) + { + if (p_info.isEncrypted(i) || p_info.isSecure(i) || "Password".equalsIgnoreCase(p_info.getColumnName(i))) + continue; + } + // ** add column ** if (doComma) { @@ -3229,10 +3320,17 @@ public abstract class PO try { if (m_IDs.length == 1 && p_info.hasKeyColumn() - && m_KeyColumns[0].endsWith("_ID") && m_KeyColumns[0].equals(p_info.getColumnName(i)) && !Env.isUseCentralizedId(p_info.getTableName())) + && m_KeyColumns[0].endsWith("_ID") && m_KeyColumns[0].equals(p_info.getColumnName(i)) && (generateScriptOnly || !Env.isUseCentralizedId(p_info.getTableName()))) { - MSequence sequence = MSequence.get(Env.getCtx(), p_info.getTableName(), get_TrxName(), true); - sqlValues.append("nextidfunc("+sequence.getAD_Sequence_ID()+",'N')"); + if (generateScriptOnly && get_ID() > 0 && get_ID() <= MTable.MAX_OFFICIAL_ID) + { + sqlValues.append(value); + } + else + { + MSequence sequence = MSequence.get(Env.getCtx(), p_info.getTableName(), get_TrxName(), true); + sqlValues.append("nextidfunc("+sequence.getAD_Sequence_ID()+",'N')"); + } } else if (c == Object.class) // may have need to deal with null values differently sqlValues.append (saveNewSpecial (value, i)); @@ -3379,11 +3477,11 @@ public abstract class PO } } - if (!withValues || Env.isUseCentralizedId(p_info.getTableName())) + if (session != null && (!withValues || Env.isUseCentralizedId(p_info.getTableName()))) { // Change Log - Only String insertLog = MSysConfig.getValue(MSysConfig.SYSTEM_INSERT_CHANGELOG, "Y", getAD_Client_ID()); - if ( session != null + if (!generateScriptOnly && session != null && m_IDs.length == 1 && p_info.isAllowLogging(i) // logging allowed && !p_info.isEncrypted(i) // not encrypted @@ -3410,7 +3508,7 @@ public abstract class PO while (it.hasNext()) { String column = (String)it.next(); - index = p_info.getColumnIndex(column); + int index = p_info.getColumnIndex(column); String value = (String)m_custom.get(column); if (value == null) continue; @@ -3443,65 +3541,7 @@ public abstract class PO } sqlInsert.append(sqlValues) .append(")"); - // - int no = withValues ? DB.executeUpdate(sqlInsert.toString(), m_trxName) - : DB.executeUpdate(sqlInsert.toString(), params.toArray(), false, m_trxName); - boolean ok = no == 1; - if (ok) - { - if (withValues && m_IDs.length == 1 && p_info.hasKeyColumn() - && m_KeyColumns[0].endsWith("_ID") && !Env.isUseCentralizedId(p_info.getTableName())) - { - int id = DB.getSQLValueEx(get_TrxName(), "SELECT " + m_KeyColumns[0] + " FROM " - + p_info.getTableName() + " WHERE " + getUUIDColumnName() + "=?", get_ValueAsString(getUUIDColumnName())); - m_IDs[0] = Integer.valueOf(id); - set_ValueNoCheck(m_KeyColumns[0], m_IDs[0]); - - int ki = p_info.getColumnIndex(m_KeyColumns[0]); - // Change Log - Only - String insertLog = MSysConfig.getValue(MSysConfig.SYSTEM_INSERT_CHANGELOG, "Y", getAD_Client_ID()); - if ( session != null - && m_IDs.length == 1 - && p_info.isAllowLogging(ki) // logging allowed - && !p_info.isEncrypted(ki) // not encrypted - && !p_info.isVirtualColumn(ki) // no virtual column - && !"Password".equals(p_info.getColumnName(ki)) - && (insertLog.equalsIgnoreCase("Y") - || (insertLog.equalsIgnoreCase("K") && p_info.getColumn(ki).IsKey)) - ) - { - // change log on new - MChangeLog cLog = session.changeLog ( - m_trxName, AD_ChangeLog_ID, - p_info.getAD_Table_ID(), p_info.getColumn(ki).AD_Column_ID, - get_ID(), getAD_Client_ID(), getAD_Org_ID(), null, id, MChangeLog.EVENTCHANGELOG_Insert); - if (cLog != null) - AD_ChangeLog_ID = cLog.getAD_ChangeLog_ID(); - } - } - ok = lobSave(); - if (!load(m_trxName)) // re-read Info - { - if (m_trxName == null) - log.log(Level.SEVERE, "reloading"); - else - log.log(Level.SEVERE, "[" + m_trxName + "] - reloading"); - ok = false;; - } - } - else - { - String msg = "Not inserted - "; - if (CLogMgt.isLevelFiner()) - msg += sqlInsert.toString(); - else - msg += get_TableName(); - if (m_trxName == null) - log.log(Level.WARNING, msg); - else - log.log(Level.WARNING, "[" + m_trxName + "]" + msg); - } - return ok; + return AD_ChangeLog_ID; } /** diff --git a/org.adempiere.base/src/org/compiere/model/POInfo.java b/org.adempiere.base/src/org/compiere/model/POInfo.java index c38661269d..c3a260f50a 100644 --- a/org.adempiere.base/src/org/compiere/model/POInfo.java +++ b/org.adempiere.base/src/org/compiere/model/POInfo.java @@ -657,6 +657,17 @@ public class POInfo implements Serializable return m_columns[index].IsEncrypted; } // isEncrypted + /** + * @param index + * @return true if column is secure + */ + public boolean isSecure(int index) + { + if (index < 0 || index >= m_columns.length) + return false; + return MColumn.get(m_columns[index].AD_Column_ID).isSecure(); + } + /** * Is allowed logging on this column * diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/panel/action/ExportAction.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/panel/action/ExportAction.java index 308fe00cbd..f7bb6f73f3 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/panel/action/ExportAction.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/panel/action/ExportAction.java @@ -15,6 +15,7 @@ package org.adempiere.webui.panel.action; import java.io.File; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -44,6 +45,7 @@ import org.adempiere.webui.event.DialogEvents; import org.adempiere.webui.util.ZKUpdateUtil; import org.adempiere.webui.window.Dialog; import org.compiere.model.GridTab; +import org.compiere.model.MRole; import org.compiere.util.Env; import org.compiere.util.Msg; import org.zkoss.util.media.AMedia; @@ -56,26 +58,30 @@ import org.zkoss.zul.Vbox; import org.zkoss.zul.Vlayout; /** - * + * Action to export data from {@link GridTab} * @author hengsin - * */ public class ExportAction implements EventListener { private AbstractADWindowContent panel; + /** Exporter Label:IGridTabExporter */ private Map exporterMap = null; + /** Exporter Label:Exporter File Extension */ private Map extensionMap = null; - + + /** Export file dialog */ private Window winExportFile = null; private ConfirmPanel confirmPanel = new ConfirmPanel(true); + /** List of exporters */ private Listbox cboType = new Listbox(); private Checkbox chkCurrentRow = new Checkbox(); - private int indxDetailSelected = 0; - private List childs; - private Row selectionTabRow = null; - private List chkSelectionTabForExport = null; + private int selectedChildTabIndex = 0; + private List childTabs; + private Row childTabSelectionRow = null; + private List chkSelectChildTabs = null; private IGridTabExporter exporter; + /** * @param panel */ @@ -92,13 +98,17 @@ public class ExportAction implements EventListener exporterMap = new HashMap(); extensionMap = new HashMap(); List exporterList = EquinoxExtensionLocator.instance().list(IGridTabExporter.class).getExtensions(); + MRole role = MRole.getDefault(); for(IGridTabExporter exporter : exporterList) { + if (exporter.isAdvanced() && !role.isAccessAdvanced()) + continue; + String extension = exporter.getFileExtension(); - if (!extensionMap.containsKey(extension)) + if (!extensionMap.containsKey(exporter.getFileExtensionLabel())) { - extensionMap.put(extension, exporter.getFileExtensionLabel()); - exporterMap.put(extension, exporter); + extensionMap.put(exporter.getFileExtensionLabel(), extension); + exporterMap.put(exporter.getFileExtensionLabel(), exporter); } } @@ -116,9 +126,11 @@ public class ExportAction implements EventListener cboType.setMold("select"); cboType.getItems().clear(); - for(Map.Entry entry : extensionMap.entrySet()) + List keys = new ArrayList<>(extensionMap.keySet()); + Collections.sort(keys); + for(String key : keys) { - cboType.appendItem(entry.getKey() + " - " + entry.getValue(), entry.getKey()); + cboType.appendItem(key, key); } cboType.setSelectedIndex(0); @@ -159,9 +171,10 @@ public class ExportAction implements EventListener chkCurrentRow.setLabel(Msg.getMsg(Env.getCtx(), "ExportCurrentRowOnly")); chkCurrentRow.setSelected(true); row.appendChild(chkCurrentRow); + chkCurrentRow.addActionListener(this); - selectionTabRow = new Row(); - rows.appendChild(selectionTabRow); + childTabSelectionRow = new Row(); + rows.appendChild(childTabSelectionRow); LayoutUtils.addSclass("dialog-footer", confirmPanel); vb.appendChild(confirmPanel); @@ -179,51 +192,61 @@ public class ExportAction implements EventListener } /** - * Show list tab can export for user selection + * Show child tabs for user selection */ protected void displayExportTabSelection() { initTabInfo (); + int tabLevel = panel.getActiveGridTab().getTabLevel(); + exporter = getExporter (); if (exporter == null){ Events.echoEvent("onExporterException", winExportFile, null); } // clear list checkbox selection to recreate with new reporter - selectionTabRow.getChildren().clear(); + childTabSelectionRow.getChildren().clear(); + if (exporter.isExportChildTabsForCurrentRowOnly() && !chkCurrentRow.isChecked()) + return; + Vlayout vlayout = new Vlayout(); - selectionTabRow.appendChild(new Space()); - selectionTabRow.appendChild(vlayout); + childTabSelectionRow.appendChild(new Space()); + childTabSelectionRow.appendChild(vlayout); vlayout.appendChild(new Label(Msg.getMsg(Env.getCtx(), "SelectTabToExport"))); - chkSelectionTabForExport = new ArrayList (); + chkSelectChildTabs = new ArrayList (); boolean isHasSelectionTab = false; - boolean isSelectTabDefault = false; + boolean selectAllChildTabs = false; // with 2Pack, default is export all child tab if (exporter.getClass().getName().equals("org.adempiere.pipo2.GridTab2PackExporter")){ - isSelectTabDefault = true; + selectAllChildTabs = true; } // for to make each export tab with one checkbox - for (GridTab child : childs){ + for (GridTab child : childTabs){ Checkbox chkSelectionTab = new Checkbox(); chkSelectionTab.setLabel(child.getName()); - // just allow selection tab can export + // check with exporter if (!exporter.isExportableTab(child)){ continue; } - if (child.getTabNo() == indxDetailSelected || isSelectTabDefault){ + if (exporter.maxDeepOfChildTab() > 0) { + int deep = child.getTabLevel() - tabLevel; + if (deep > exporter.maxDeepOfChildTab()) + continue; + } + if (child.getTabNo() == selectedChildTabIndex || selectAllChildTabs){ chkSelectionTab.setSelected(true); } chkSelectionTab.setAttribute("tabBinding", child); vlayout.appendChild(chkSelectionTab); - chkSelectionTabForExport.add(chkSelectionTab); + chkSelectChildTabs.add(chkSelectionTab); chkSelectionTab.addEventListener(Events.ON_CHECK, this); isHasSelectionTab = true; } // in case no child tab can export. clear selection area if (isHasSelectionTab == false){ - selectionTabRow.getChildren().clear(); + childTabSelectionRow.getChildren().clear(); } } @@ -237,13 +260,18 @@ public class ExportAction implements EventListener panel.hideBusyMask(); panel.focusToLastFocusEditor(); } else if (event.getTarget().equals(cboType) && event.getName().equals(Events.ON_SELECT)) { - displayExportTabSelection(); - }else if (event.getTarget() instanceof Checkbox) { + displayExportTabSelection(); + } else if (event.getTarget() == chkCurrentRow) { + exporter = getExporter(); + if (exporter != null && exporter.isExportChildTabsForCurrentRowOnly()) { + displayExportTabSelection(); + } + } else if (event.getTarget() instanceof Checkbox) { // A child is not exportable without its parent Checkbox cbSel = (Checkbox) event.getTarget(); GridTab gtSel = (GridTab)cbSel.getAttribute("tabBinding"); boolean found = false; - for (Checkbox cb : chkSelectionTabForExport) { + for (Checkbox cb : chkSelectChildTabs) { if (cb == cbSel) { found = true; continue; @@ -258,26 +286,29 @@ public class ExportAction implements EventListener } } } - }else if (event.getName().equals("onExporterException")){ + } else if (event.getName().equals("onExporterException")){ Dialog.error(0, "FileInvalidExtension"); winExportFile.onClose(); } } + /** + * Close export file dialog + */ private void onCancel() { winExportFile.onClose(); } /** - * get info of window export, - * index of active tab, list child tab + * Get GridTabs info from calling AD window, + * index of active detail tab, child tabs */ protected void initTabInfo() { IADTabbox adTab = panel.getADTab(); int selected = adTab.getSelectedIndex(); int tabLevel = panel.getActiveGridTab().getTabLevel(); Set tables = new HashSet(); - childs = new ArrayList(); + childTabs = new ArrayList(); List includedList = panel.getActiveGridTab().getIncludedTabs(); for(GridTab included : includedList) { @@ -285,7 +316,7 @@ public class ExportAction implements EventListener if (tables.contains(tableName)) continue; tables.add(tableName); - childs.add(included); + childTabs.add(included); } for(int i = selected+1; i < adTab.getTabCount(); i++) { @@ -298,18 +329,18 @@ public class ExportAction implements EventListener if (tables.contains(tableName)) continue; tables.add(tableName); - childs.add(adTabPanel.getGridTab()); + childTabs.add(adTabPanel.getGridTab()); } - indxDetailSelected = 0; + selectedChildTabIndex = 0; if( adTab.getSelectedDetailADTabpanel()!=null ) - indxDetailSelected = adTab.getSelectedDetailADTabpanel().getGridTab().getTabNo(); + selectedChildTabIndex = adTab.getSelectedDetailADTabpanel().getGridTab().getTabNo(); } /** * Get selected exporter - * @return + * @return IGridTabExporter */ protected IGridTabExporter getExporter() { ListItem li = cboType.getSelectedItem(); @@ -323,18 +354,21 @@ public class ExportAction implements EventListener return exporter; } + /** + * Invoke exporter and prompt user to download exported data file + */ private void exportFile() { try { boolean currentRowOnly = chkCurrentRow.isSelected(); File file = File.createTempFile("Export", "."+cboType.getSelectedItem().getValue().toString()); - childs.clear(); - for (Checkbox chkSeletionTab : chkSelectionTabForExport){ + List selectedChildTabs = new ArrayList<>(); + for (Checkbox chkSeletionTab : chkSelectChildTabs){ if (chkSeletionTab.isChecked()){ - childs.add((GridTab)chkSeletionTab.getAttribute("tabBinding")); + selectedChildTabs.add((GridTab)chkSeletionTab.getAttribute("tabBinding")); } } - exporter.export(panel.getActiveGridTab(), childs, currentRowOnly,file,indxDetailSelected); + exporter.export(panel.getActiveGridTab(), selectedChildTabs, currentRowOnly, file, selectedChildTabIndex); winExportFile.onClose(); winExportFile = null;