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