From 6a66903ec687601a118dd3fbf8895e62c8439ff4 Mon Sep 17 00:00:00 2001 From: hengsin Date: Fri, 3 Mar 2023 01:24:41 +0800 Subject: [PATCH] IDEMPIERE-5586 Implement ID independent migration script (#1685) * IDEMPIERE-5586 Implement ID independent migration script * IDEMPIERE-5586 Implement ID independent migration script - add Oracle script (99% converted by ChatGPT from PostgreSQL :)). * IDEMPIERE-5586 Implement ID independent migration script - handle official id value for foreign key reference . - add back centralized id support. * IDEMPIERE-5586 Implement ID independent migration script - minor refactoring - add support for AD_Table_ID+Record_ID usage * IDEMPIERE-5586 Implement ID independent migration script - add support for Delete - add unit test * IDEMPIERE-5586 Implement ID independent migration script - add insert_accounting and insert_tree support. * IDEMPIERE-5586 Implement ID independent migration script - add ADSortTab support. * IDEMPIERE-5586 Implement ID independent migration script - move migration script from iD11 to iD10. --- db/oracle/functions/torecordid.sql | 41 +++ db/postgresql/functions/torecordid.sql | 42 ++++ .../oracle/202302220654_IDEMPIERE-5586.sql | 47 ++++ .../202302220654_IDEMPIERE-5586.sql | 48 ++++ .../src/org/compiere/dbPort/Convert.java | 97 +++++-- .../src/org/compiere/model/MBPartner.java | 5 +- .../src/org/compiere/model/MProduct.java | 5 +- .../src/org/compiere/model/MSequence.java | 22 +- .../src/org/compiere/model/PO.java | 238 +++++++++++++++--- .../src/org/compiere/util/Env.java | 50 +++- .../adempiere/webui/adwindow/ADSortTab.java | 151 +++++++---- .../adempiere/webui/window/WPreference.java | 29 +-- .../src/org/idempiere/test/base/POTest.java | 61 +++++ 13 files changed, 700 insertions(+), 136 deletions(-) create mode 100644 db/oracle/functions/torecordid.sql create mode 100644 db/postgresql/functions/torecordid.sql create mode 100644 migration/iD10/oracle/202302220654_IDEMPIERE-5586.sql create mode 100644 migration/iD10/postgresql/202302220654_IDEMPIERE-5586.sql diff --git a/db/oracle/functions/torecordid.sql b/db/oracle/functions/torecordid.sql new file mode 100644 index 0000000000..ef5633e151 --- /dev/null +++ b/db/oracle/functions/torecordid.sql @@ -0,0 +1,41 @@ +CREATE OR REPLACE FUNCTION torecordid ( + p_tablename IN VARCHAR2, + p_uu_value IN VARCHAR2 +) +RETURN NUMBER AS + id_column VARCHAR2(200); + uu_column VARCHAR2(200); + o_id NUMBER; +BEGIN + SELECT a.ColumnName + INTO id_column + FROM AD_Column a + JOIN AD_Table b ON (a.AD_Table_ID=b.AD_Table_ID) + WHERE a.IsActive='Y' AND a.IsKey='Y' AND lower(b.TableName) = lower(p_tablename); + + IF (id_column IS NULL) THEN + raise_application_error(-20001, 'ID column not found for table ' || p_tablename); + END IF; + + IF (length(p_tablename) <= 27) THEN + uu_column := p_tablename || '_UU'; + ELSE + SELECT a.ColumnName + INTO uu_column + FROM AD_Column a + JOIN AD_Table b ON (a.AD_Table_ID=b.AD_Table_ID) + WHERE a.IsActive='Y' AND a.FieldLength=36 AND lower(a.ColumnName) LIKE (lower(SUBSTR(p_tablename, 1, 27)) || '%UU') + AND lower(b.TableName) = lower(p_tablename); + END IF; + + IF (uu_column IS NULL) THEN + raise_application_error(-20002, 'UUID column not found for table ' || p_tablename); + END IF; + + EXECUTE IMMEDIATE 'SELECT ' || id_column || ' FROM ' || p_tablename || ' WHERE ' || uu_column || '=:1' + INTO o_id + USING p_uu_value; + + RETURN o_id; +END; +/ diff --git a/db/postgresql/functions/torecordid.sql b/db/postgresql/functions/torecordid.sql new file mode 100644 index 0000000000..4810585545 --- /dev/null +++ b/db/postgresql/functions/torecordid.sql @@ -0,0 +1,42 @@ +CREATE OR REPLACE FUNCTION torecordid( + p_tablename IN VARCHAR, + p_uu_value IN VARCHAR +) + RETURNS INTEGER AS $body$ +DECLARE + id_column VARCHAR; + uu_column VARCHAR; + o_id INTEGER; +BEGIN + SELECT a.ColumnName + INTO id_column + FROM AD_Column a + JOIN AD_Table b ON (a.AD_Table_ID=b.AD_Table_ID) + WHERE a.IsActive='Y' AND a.IsKey='Y' AND lower(b.TableName) = lower(p_tablename); + + IF (id_column IS NULL) THEN + RAISE EXCEPTION 'ID column not found for table %', p_tablename; + END IF; + + IF (length(p_tablename) <= 27) THEN + uu_column = p_tablename || '_UU'; + ELSE + SELECT a.ColumnName + INTO uu_column + FROM AD_Column a + JOIN AD_Table b ON (a.AD_Table_ID=b.AD_Table_ID) + WHERE a.IsActive='Y' AND a.FieldLength=36 AND lower(ColumnName) like (lower(substring(p_tablename from 0 for 27)) || '%UU') + AND lower(b.TableName) = lower(p_tablename); + END IF; + + IF (uu_column IS NULL) THEN + RAISE EXCEPTION 'UUID column not found for table %', p_tablename; + END IF; + + EXECUTE 'SELECT ' || quote_ident(lower(id_column)) || ' FROM ' || quote_ident(lower(p_tablename)) || ' WHERE ' || uu_column || '=$1' + INTO STRICT o_id + USING p_uu_value; + + RETURN o_id; +END; +$body$ LANGUAGE plpgsql; diff --git a/migration/iD10/oracle/202302220654_IDEMPIERE-5586.sql b/migration/iD10/oracle/202302220654_IDEMPIERE-5586.sql new file mode 100644 index 0000000000..d12860703e --- /dev/null +++ b/migration/iD10/oracle/202302220654_IDEMPIERE-5586.sql @@ -0,0 +1,47 @@ +-- IDEMPIERE-5586 +SELECT register_migration_script('202302220654_IDEMPIERE-5586.sql') FROM dual; + +SET SQLBLANKLINES ON +SET DEFINE OFF + +CREATE OR REPLACE FUNCTION torecordid ( + p_tablename IN VARCHAR2, + p_uu_value IN VARCHAR2 +) +RETURN NUMBER AS + id_column VARCHAR2(200); + uu_column VARCHAR2(200); + o_id NUMBER; +BEGIN + SELECT a.ColumnName + INTO id_column + FROM AD_Column a + JOIN AD_Table b ON (a.AD_Table_ID=b.AD_Table_ID) + WHERE a.IsActive='Y' AND a.IsKey='Y' AND lower(b.TableName) = lower(p_tablename); + + IF (id_column IS NULL) THEN + raise_application_error(-20001, 'ID column not found for table ' || p_tablename); + END IF; + + IF (length(p_tablename) <= 27) THEN + uu_column := p_tablename || '_UU'; + ELSE + SELECT a.ColumnName + INTO uu_column + FROM AD_Column a + JOIN AD_Table b ON (a.AD_Table_ID=b.AD_Table_ID) + WHERE a.IsActive='Y' AND a.FieldLength=36 AND lower(a.ColumnName) LIKE (lower(SUBSTR(p_tablename, 1, 27)) || '%UU') + AND lower(b.TableName) = lower(p_tablename); + END IF; + + IF (uu_column IS NULL) THEN + raise_application_error(-20002, 'UUID column not found for table ' || p_tablename); + END IF; + + EXECUTE IMMEDIATE 'SELECT ' || id_column || ' FROM ' || p_tablename || ' WHERE ' || uu_column || '=:1' + INTO o_id + USING p_uu_value; + + RETURN o_id; +END; +/ diff --git a/migration/iD10/postgresql/202302220654_IDEMPIERE-5586.sql b/migration/iD10/postgresql/202302220654_IDEMPIERE-5586.sql new file mode 100644 index 0000000000..635516a803 --- /dev/null +++ b/migration/iD10/postgresql/202302220654_IDEMPIERE-5586.sql @@ -0,0 +1,48 @@ +-- IDEMPIERE-5586 +SELECT register_migration_script('202302220654_IDEMPIERE-5586.sql') FROM dual; + +CREATE OR REPLACE FUNCTION torecordid( + p_tablename IN VARCHAR, + p_uu_value IN VARCHAR +) + RETURNS INTEGER AS $body$ +DECLARE + id_column VARCHAR; + uu_column VARCHAR; + o_id INTEGER; +BEGIN + SELECT a.ColumnName + INTO id_column + FROM AD_Column a + JOIN AD_Table b ON (a.AD_Table_ID=b.AD_Table_ID) + WHERE a.IsActive='Y' AND a.IsKey='Y' AND lower(b.TableName) = lower(p_tablename); + + IF (id_column IS NULL) THEN + RAISE EXCEPTION 'ID column not found for table %', p_tablename; + END IF; + + IF (length(p_tablename) <= 27) THEN + uu_column = p_tablename || '_UU'; + ELSE + SELECT a.ColumnName + INTO uu_column + FROM AD_Column a + JOIN AD_Table b ON (a.AD_Table_ID=b.AD_Table_ID) + WHERE a.IsActive='Y' AND a.FieldLength=36 AND lower(ColumnName) like (lower(substring(p_tablename from 0 for 27)) || '%UU') + AND lower(b.TableName) = lower(p_tablename); + END IF; + + IF (uu_column IS NULL) THEN + RAISE EXCEPTION 'UUID column not found for table %', p_tablename; + END IF; + + EXECUTE 'SELECT ' || quote_ident(lower(id_column)) || ' FROM ' || quote_ident(lower(p_tablename)) || ' WHERE ' || uu_column || '=$1' + INTO STRICT o_id + USING p_uu_value; + + RETURN o_id; +END; +$body$ LANGUAGE plpgsql +; + + diff --git a/org.adempiere.base/src/org/compiere/dbPort/Convert.java b/org.adempiere.base/src/org/compiere/dbPort/Convert.java index 1b61a777c4..6330eb1e7c 100644 --- a/org.adempiere.base/src/org/compiere/dbPort/Convert.java +++ b/org.adempiere.base/src/org/compiere/dbPort/Convert.java @@ -42,10 +42,12 @@ import java.util.regex.Pattern; import org.adempiere.exceptions.AdempiereException; import org.compiere.Adempiere; import org.compiere.db.Database; +import org.compiere.model.I_AD_UserPreference; import org.compiere.util.CLogger; import org.compiere.util.DisplayType; import org.compiere.util.Env; import org.compiere.util.Ini; +import org.compiere.util.Util; /** * Convert SQL to Target DB @@ -441,6 +443,11 @@ public abstract class Convert */ public abstract boolean isOracle(); + /** + * Log oraStatement and pgStatement to SQL migration script file. + * @param oraStatement + * @param pgStatement + */ public synchronized static void logMigrationScript(String oraStatement, String pgStatement) { // Check AdempiereSys // check property Log migration script @@ -456,26 +463,10 @@ public abstract class Convert String prm_COMMENT = null; try { if (fosScriptOr == null || fosScriptPg == null) { - String now = new SimpleDateFormat("yyyyMMddHHmm").format(new Date()); - prm_COMMENT = Env.getContext(Env.getCtx(), "MigrationScriptComment"); - String pattern = "(IDEMPIERE-[0-9]*)"; - Pattern p = Pattern.compile(pattern); - Matcher m = p.matcher(prm_COMMENT); - String ticket = null; - if (m.find()) - ticket = m.group(1); - if (ticket == null) - ticket = "PlaceholderForTicket"; - fileName = now + "_" + ticket + ".sql"; - String version = Adempiere.MAIN_VERSION.substring(8); - boolean isIDE = Files.isDirectory(Paths.get(Adempiere.getAdempiereHome() + File.separator + "org.adempiere.base")); - String homeScript; - if (isIDE) - homeScript = Adempiere.getAdempiereHome() + File.separator; - else - homeScript = System.getProperty("java.io.tmpdir") + File.separator; - folderOr = homeScript + "migration" + File.separator + "iD" + version + File.separator + "oracle" + File.separator; - folderPg = homeScript + "migration" + File.separator + "iD" + version + File.separator + "postgresql" + File.separator; + prm_COMMENT = Env.getContext(Env.getCtx(), I_AD_UserPreference.COLUMNNAME_MigrationScriptComment); + fileName = getMigrationScriptFileName(prm_COMMENT); + folderOr = getMigrationScriptFolder("oracle"); + folderPg = getMigrationScriptFolder("postgresql"); Files.createDirectories(Paths.get(folderOr)); Files.createDirectories(Paths.get(folderPg)); } @@ -511,6 +502,42 @@ public abstract class Convert } } + /** + * @param ticketComment + * @return migration script file name + */ + public static String getMigrationScriptFileName(String ticketComment) { + // [timestamp]_[ticket].sql + String fileName; + String now = new SimpleDateFormat("yyyyMMddHHmm").format(new Date()); + String pattern = "(IDEMPIERE-[0-9]*)"; + Pattern p = Pattern.compile(pattern); + Matcher m = p.matcher(ticketComment); + String ticket = null; + if (m.find()) + ticket = m.group(1); + if (ticket == null) + ticket = "PlaceholderForTicket"; + fileName = now + "_" + ticket + ".sql"; + return fileName; + } + + /** + * @param dbtype oracle or postgresql + * @return absolute migration script folder path for dbtype + */ + public static String getMigrationScriptFolder(String dbtype) { + // migration/iD[version]/[oracle|postgresql] directory + String version = Adempiere.MAIN_VERSION.substring(8); + boolean isIDE = Files.isDirectory(Paths.get(Adempiere.getAdempiereHome() + File.separator + "org.adempiere.base")); + String homeScript; + if (isIDE) + homeScript = Adempiere.getAdempiereHome() + File.separator; + else + homeScript = System.getProperty("java.io.tmpdir") + File.separator; + return homeScript + "migration" + File.separator + "iD" + version + File.separator + dbtype + File.separator; + } + /** * @return true if it is in log migration script mode */ @@ -519,13 +546,12 @@ public abstract class Convert if (Ini.isClient()) { logMigrationScript = Ini.isPropertyBool(Ini.P_LOGMIGRATIONSCRIPT); } else { - String sysProperty = Env.getCtx().getProperty("LogMigrationScript", "N"); + String sysProperty = Env.getCtx().getProperty(Ini.P_LOGMIGRATIONSCRIPT, "N"); logMigrationScript = "y".equalsIgnoreCase(sysProperty) || "true".equalsIgnoreCase(sysProperty); } return logMigrationScript; } - private static String [] dontLogTables = new String[] { "AD_ACCESSLOG", "AD_ALERTPROCESSORLOG", @@ -573,6 +599,25 @@ public abstract class Convert "T_TRIALBALANCE" }; + /** + * @param tableName + * @return true if log migration script should ignore tableName + */ + public static boolean isDontLogTable(String tableName) { + if (Util.isEmpty(tableName)) + return false; + + // Don't log trl - those will be created/maintained using synchronize terminology + if (tableName.endsWith("_TRL")) + return true; + + for (String t : dontLogTables) { + if (t.equalsIgnoreCase(tableName)) + return true; + } + return false; + } + private static boolean dontLog(String statement) { // Do not log *Access records - teo_Sarca BF [ 2782095 ] // IDEMPIERE-323 Migration script log AD_Document_Action_Access (nmicoud / CarlosRuiz_globalqss) @@ -599,6 +644,8 @@ public abstract class Convert return true; if (uppStmt.matches("INSERT INTO .*_TRL .*")) return true; + if (uppStmt.matches("DELETE FROM .*_TRL .*")) + return true; // Don't log tree custom table statements (not present in core) if (uppStmt.startsWith("INSERT INTO AD_TREENODE ") && uppStmt.contains(" AND T.TREETYPE='TL' AND T.AD_TABLE_ID=")) return true; @@ -619,6 +666,12 @@ public abstract class Convert return false; } + /** + * Use writer to append SQL statement to an output media (usually file). + * @param w {@link Writer} + * @param statement SQL statement + * @throws IOException + */ private static void writeLogMigrationScript(Writer w, String statement) throws IOException { // log time and date diff --git a/org.adempiere.base/src/org/compiere/model/MBPartner.java b/org.adempiere.base/src/org/compiere/model/MBPartner.java index 0c32503acd..1d3fcf4ccb 100644 --- a/org.adempiere.base/src/org/compiere/model/MBPartner.java +++ b/org.adempiere.base/src/org/compiere/model/MBPartner.java @@ -989,7 +989,10 @@ public class MBPartner extends X_C_BPartner implements ImmutablePOSupport // Trees insert_Tree(MTree_Base.TREETYPE_BPartner); // Accounting - StringBuilder msgacc = new StringBuilder("p.C_BP_Group_ID=").append(getC_BP_Group_ID()); + StringBuilder msgacc = new StringBuilder("p.C_BP_Group_ID=") + .append(getC_BP_Group_ID() > MTable.MAX_OFFICIAL_ID && Env.isLogMigrationScript(get_TableName()) + ? "toRecordId('C_BP_Group',"+DB.TO_STRING(MBPGroup.get(getC_BP_Group_ID()).getC_BP_Group_UU())+")" + : getC_BP_Group_ID()); insert_Accounting("C_BP_Customer_Acct", "C_BP_Group_Acct", msgacc.toString()); insert_Accounting("C_BP_Vendor_Acct", "C_BP_Group_Acct",msgacc.toString()); } diff --git a/org.adempiere.base/src/org/compiere/model/MProduct.java b/org.adempiere.base/src/org/compiere/model/MProduct.java index 0be63ed139..b8ce423764 100644 --- a/org.adempiere.base/src/org/compiere/model/MProduct.java +++ b/org.adempiere.base/src/org/compiere/model/MProduct.java @@ -854,7 +854,10 @@ public class MProduct extends X_M_Product implements ImmutablePOSupport if (newRecord) { insert_Accounting("M_Product_Acct", "M_Product_Category_Acct", - "p.M_Product_Category_ID=" + getM_Product_Category_ID()); + "p.M_Product_Category_ID=" + + (getM_Product_Category_ID() > MTable.MAX_OFFICIAL_ID && Env.isLogMigrationScript(get_TableName()) + ? "toRecordId('M_Product_Category',"+DB.TO_STRING(MProductCategory.get(getM_Product_Category_ID()).getM_Product_Category_UU())+")" + : getM_Product_Category_ID())); insert_Tree(X_AD_Tree.TREETYPE_Product); } if (newRecord || is_ValueChanged(COLUMNNAME_Value)) diff --git a/org.adempiere.base/src/org/compiere/model/MSequence.java b/org.adempiere.base/src/org/compiere/model/MSequence.java index 33d0e6d1cf..59573aedbe 100644 --- a/org.adempiere.base/src/org/compiere/model/MSequence.java +++ b/org.adempiere.base/src/org/compiere/model/MSequence.java @@ -86,7 +86,7 @@ public class MSequence extends X_AD_Sequence } else { - String sysProperty = Env.getCtx().getProperty("AdempiereSys", "N"); + String sysProperty = Env.getCtx().getProperty(Ini.P_ADEMPIERESYS, "N"); adempiereSys = "y".equalsIgnoreCase(sysProperty) || "true".equalsIgnoreCase(sysProperty); } if (adempiereSys && AD_Client_ID > 11) @@ -315,7 +315,7 @@ public class MSequence extends X_AD_Sequence } else { - String sysProperty = Env.getCtx().getProperty("AdempiereSys", "N"); + String sysProperty = Env.getCtx().getProperty(Ini.P_ADEMPIERESYS, "N"); adempiereSys = "y".equalsIgnoreCase(sysProperty) || "true".equalsIgnoreCase(sysProperty); } if (adempiereSys && Env.getAD_Client_ID(Env.getCtx()) > 11) @@ -1103,7 +1103,7 @@ public class MSequence extends X_AD_Sequence String prm_PASSWORD = MSysConfig.getValue(MSysConfig.DICTIONARY_ID_PASSWORD); // "password_inseguro"; String prm_TABLE = TableName; String prm_ALTKEY = ""; // TODO: generate alt-key based on key of table - String prm_COMMENT = Env.getContext(Env.getCtx(), "MigrationScriptComment"); + String prm_COMMENT = Env.getContext(Env.getCtx(), I_AD_UserPreference.COLUMNNAME_MigrationScriptComment); String prm_PROJECT = new String("Adempiere"); return getNextID_HTTP(TableName, website, prm_USER, @@ -1122,7 +1122,7 @@ public class MSequence extends X_AD_Sequence String prm_PASSWORD = MSysConfig.getValue(MSysConfig.PROJECT_ID_PASSWORD); // "password_inseguro"; String prm_TABLE = TableName; String prm_ALTKEY = ""; // TODO: generate alt-key based on key of table - String prm_COMMENT = Env.getContext(Env.getCtx(), "MigrationScriptComment"); + String prm_COMMENT = Env.getContext(Env.getCtx(), I_AD_UserPreference.COLUMNNAME_MigrationScriptComment); String prm_PROJECT = MSysConfig.getValue(MSysConfig.PROJECT_ID_PROJECT); return getNextID_HTTP(TableName, website, prm_USER, @@ -1219,7 +1219,11 @@ public class MSequence extends X_AD_Sequence "T_TRIALBALANCE" }; - private static boolean isExceptionCentralized(String tableName) { + /** + * @param tableName + * @return true if centralized id shouldn't be used for tableName + */ + public static boolean isExceptionCentralized(String tableName) { for (String exceptionTable : dontUseCentralized) { if (tableName.equalsIgnoreCase(exceptionTable)) @@ -1232,8 +1236,12 @@ public class MSequence extends X_AD_Sequence private static CCache tablesWithEntityType = new CCache(Table_Name, "TablesWithEntityType", 60, 0, false, 0); - private synchronized static boolean isTableWithEntityType(String tableName) { - if (tablesWithEntityType == null || tablesWithEntityType.size() == 0) { + /** + * @param tableName + * @return true if tableName has entity type column name + */ + public static synchronized boolean isTableWithEntityType(String tableName) { + if (tablesWithEntityType.size() == 0) { final String sql = "SELECT TableName FROM AD_Table WHERE AD_Table_ID IN (SELECT AD_Table_ID FROM AD_Column WHERE ColumnName='EntityType') ORDER BY TableName"; List> list = DB.getSQLArrayObjectsEx(null, sql); for (List row : list) { diff --git a/org.adempiere.base/src/org/compiere/model/PO.java b/org.adempiere.base/src/org/compiere/model/PO.java index 690093ad0d..75cc2a4f31 100644 --- a/org.adempiere.base/src/org/compiere/model/PO.java +++ b/org.adempiere.base/src/org/compiere/model/PO.java @@ -66,7 +66,6 @@ import org.compiere.util.DB; import org.compiere.util.DisplayType; import org.compiere.util.Env; import org.compiere.util.Evaluatee; -import org.compiere.util.Ini; import org.compiere.util.Language; import org.compiere.util.Msg; import org.compiere.util.SecureEngine; @@ -468,6 +467,17 @@ public abstract class PO return m_idOld; } // getID + /** + * @return UUID value + */ + public String get_UUID() { + String uidColumn = getUUIDColumnName(); + if (p_info.getColumnIndex(uidColumn) >=0) + return get_ValueAsString(uidColumn); + else + return null; + } + /** * Get Context * @return context @@ -2630,22 +2640,18 @@ public abstract class PO return saveFinish (false, ok); } // saveUpdate + /** + * @return true if sql migration script should be logged for changes to this PO instance + */ private boolean isLogSQLScript() { - boolean logMigrationScript = false; - if (Ini.isClient()) { - logMigrationScript = Ini.isPropertyBool(Ini.P_LOGMIGRATIONSCRIPT); - } else { - String sysProperty = Env.getCtx().getProperty("LogMigrationScript", "N"); - logMigrationScript = "y".equalsIgnoreCase(sysProperty) || "true".equalsIgnoreCase(sysProperty); - } - return logMigrationScript; + return Env.isLogMigrationScript(p_info.getTableName()); } private boolean doUpdate(boolean withValues) { //params for insert statement List params = new ArrayList(); - - String where = get_WhereClause(true); + + String where = withValues ? get_WhereClause(true, get_ValueAsString(getUUIDColumnName())) : get_WhereClause(true); List optimisticLockingParams = new ArrayList(); if (is_UseOptimisticLocking() && m_optimisticLockingColumns != null && m_optimisticLockingColumns.length > 0) @@ -2737,6 +2743,55 @@ public abstract class PO // values if (value == Null.NULL) sql.append("NULL"); + else if (value instanceof Integer && "Record_ID".equalsIgnoreCase(columnName)) + { + Integer idValue = (Integer) value; + if (idValue <= MTable.MAX_OFFICIAL_ID) + { + sql.append(value); + } + else if (p_info.getColumnIndex("AD_Table_ID") >= 0) + { + int tableId = get_ValueAsInt("AD_Table_ID"); + if (tableId > 0) + { + MTable refTable = MTable.get(Env.getCtx(), tableId); + String refTableName = refTable.getTableName(); + String refKeyColumnName = refTable.getKeyColumns()[0]; + String refUUColumnName = MTable.getUUIDColumnName(refTableName); + String refUUValue = DB.getSQLValueString(get_TrxName(), "SELECT " + refUUColumnName + " FROM " + + refTableName + " WHERE " + refKeyColumnName + "=?", (Integer)value); + sql.append("toRecordId('"+ refTableName + "','" + refUUValue + "')"); + } + else + { + sql.append(value); + } + } + else + { + sql.append(value); + } + } + else if (value instanceof Integer && p_info.isColumnLookup(i)) + { + Integer idValue = (Integer) value; + if (idValue <= MTable.MAX_OFFICIAL_ID) + { + sql.append(value); + } + else + { + MColumn col = MColumn.get(p_info.getAD_Column_ID(columnName)); + String refTableName = col.getReferenceTableName(); + MTable refTable = MTable.get(Env.getCtx(), refTableName); + String refKeyColumnName = refTable.getKeyColumns()[0]; + String refUUColumnName = MTable.getUUIDColumnName(refTableName); + String refUUValue = DB.getSQLValueString(get_TrxName(), "SELECT " + refUUColumnName + " FROM " + + refTableName + " WHERE " + refKeyColumnName + "=?", (Integer)value); + sql.append("toRecordId('"+ refTableName + "','" + refUUValue + "')"); + } + } else if (value instanceof Integer || value instanceof BigDecimal) sql.append(value); else if (c == Boolean.class) @@ -3038,7 +3093,7 @@ public abstract class PO { // Set ID for single key - Multi-Key values need explicitly be set previously if (m_IDs.length == 1 && p_info.hasKeyColumn() - && m_KeyColumns[0].endsWith("_ID")) // AD_Language, EntityType + && m_KeyColumns[0].endsWith("_ID") && (Env.isUseCentralizedId(p_info.getTableName()) || !isLogSQLScript())) // AD_Language, EntityType { int no = saveNew_getID(); if (no <= 0) @@ -3172,10 +3227,65 @@ public abstract class PO { try { - if (c == Object.class) // may have need to deal with null values differently + 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())) + { + 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)); else if (value == null || value.equals (Null.NULL)) sqlValues.append ("NULL"); + else if (value instanceof Integer && "Record_ID".equalsIgnoreCase(p_info.getColumnName(i))) + { + Integer idValue = (Integer) value; + if (idValue <= MTable.MAX_OFFICIAL_ID) + { + sqlValues.append(value); + } + else if (p_info.getColumnIndex("AD_Table_ID") >= 0) + { + int tableId = get_ValueAsInt("AD_Table_ID"); + if (tableId > 0) + { + MTable refTable = MTable.get(Env.getCtx(), tableId); + String refTableName = refTable.getTableName(); + String refKeyColumnName = refTable.getKeyColumns()[0]; + String refUUColumnName = MTable.getUUIDColumnName(refTableName); + String refUUValue = DB.getSQLValueString(get_TrxName(), "SELECT " + refUUColumnName + " FROM " + + refTableName + " WHERE " + refKeyColumnName + "=?", (Integer)value); + sqlValues.append("toRecordId('"+ refTableName + "','" + refUUValue + "')"); + } + else + { + sqlValues.append(value); + } + } + else + { + sqlValues.append(value); + } + } + else if (value instanceof Integer && p_info.isColumnLookup(i)) + { + Integer idValue = (Integer) value; + if (idValue <= MTable.MAX_OFFICIAL_ID) + { + sqlValues.append(value); + } + else + { + MColumn col = MColumn.get(p_info.getAD_Column_ID(p_info.getColumnName(i))); + String refTableName = col.getReferenceTableName(); + MTable refTable = MTable.get(Env.getCtx(), refTableName); + String refKeyColumnName = refTable.getKeyColumns()[0]; + String refUUColumnName = MTable.getUUIDColumnName(refTable.getTableName()); + String refUUValue = DB.getSQLValueString(get_TrxName(), "SELECT " + refUUColumnName + " FROM " + + refTableName + " WHERE " + refKeyColumnName + "=?", (Integer)value); + sqlValues.append("toRecordId('"+ refTableName + "','" + refUUValue + "')"); + } + } else if (value instanceof Integer || value instanceof BigDecimal) sqlValues.append (value); else if (c == Boolean.class) @@ -3268,17 +3378,19 @@ public abstract class PO } } - // 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(i) // logging allowed - && !p_info.isEncrypted(i) // not encrypted - && !p_info.isVirtualColumn(i) // no virtual column - && !"Password".equals(p_info.getColumnName(i)) - && (insertLog.equalsIgnoreCase("Y") - || (insertLog.equalsIgnoreCase("K") && p_info.getColumn(i).IsKey)) - ) + if (!withValues || Env.isUseCentralizedId(p_info.getTableName())) + { + // 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(i) // logging allowed + && !p_info.isEncrypted(i) // not encrypted + && !p_info.isVirtualColumn(i) // no virtual column + && !"Password".equals(p_info.getColumnName(i)) + && (insertLog.equalsIgnoreCase("Y") + || (insertLog.equalsIgnoreCase("K") && p_info.getColumn(i).IsKey)) + ) { // change log on new MChangeLog cLog = session.changeLog ( @@ -3288,7 +3400,7 @@ public abstract class PO if (cLog != null) AD_ChangeLog_ID = cLog.getAD_ChangeLog_ID(); } - + } } // Custom Columns if (m_custom != null) @@ -3336,6 +3448,36 @@ public abstract class PO 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 { @@ -3666,7 +3808,7 @@ public abstract class PO } // The Delete Statement - String where = get_WhereClause(true); + String where = isLogSQLScript() ? get_WhereClause(true, get_ValueAsString(getUUIDColumnName())) : get_WhereClause(true); List optimisticLockingParams = new ArrayList(); if (is_UseOptimisticLocking() && m_optimisticLockingColumns != null && m_optimisticLockingColumns.length > 0) { @@ -4153,7 +4295,7 @@ public abstract class PO // String tableName = p_info.getTableName(); String keyColumn = m_KeyColumns[0]; - StringBuilder sql = new StringBuilder ("DELETE FROM ") + StringBuilder sql = new StringBuilder ("DELETE FROM ") .append(tableName).append("_Trl WHERE ") .append(keyColumn).append("=").append(get_ID()); int no = DB.executeUpdate(sql.toString(), trxName); @@ -4222,7 +4364,9 @@ public abstract class PO if (uuidColumn != null && uuidFunction) sb.append(",").append(PO.getUUIDColumnName(acctTableName)); // .. SELECT - sb.append(") SELECT ").append(get_ID()) + sb.append(") SELECT ").append(get_ID() > MTable.MAX_OFFICIAL_ID && Env.isLogMigrationScript(get_TableName()) + ? "toRecordId("+DB.TO_STRING(get_TableName())+","+DB.TO_STRING(get_UUID())+")" + : get_ID()) .append(", p.C_AcctSchema_ID, p.AD_Client_ID,0,'Y', getDate(),") .append(getUpdatedBy()).append(",getDate(),").append(getUpdatedBy()); for (int i = 0; i < s_acctColumns.size(); i++) @@ -4231,12 +4375,19 @@ public abstract class PO sb.append(",generate_uuid()"); // .. FROM sb.append(" FROM ").append(acctBaseTable) - .append(" p WHERE p.AD_Client_ID=").append(getAD_Client_ID()); + .append(" p WHERE p.AD_Client_ID=") + .append(getAD_Client_ID() > MTable.MAX_OFFICIAL_ID && Env.isLogMigrationScript(get_TableName()) + ? "toRecordId('AD_Client',"+DB.TO_STRING(MClient.get(getAD_Client_ID()).getAD_Client_UU())+")" + : getAD_Client_ID()); if (whereClause != null && whereClause.length() > 0) sb.append (" AND ").append(whereClause); sb.append(" AND NOT EXISTS (SELECT * FROM ").append(acctTableName) .append(" e WHERE e.C_AcctSchema_ID=p.C_AcctSchema_ID AND e.") - .append(get_TableName()).append("_ID=").append(get_ID()).append(")"); + .append(get_TableName()).append("_ID="); + if (get_ID() > MTable.MAX_OFFICIAL_ID && Env.isLogMigrationScript(get_TableName())) + sb.append("toRecordId(").append(DB.TO_STRING(get_TableName())).append(",").append(DB.TO_STRING(get_UUID())).append("))"); + else + sb.append(get_ID()).append(")"); // int no = DB.executeUpdate(sb.toString(), get_TrxName()); if (no > 0) { @@ -4304,24 +4455,41 @@ public abstract class PO else sb.append(") "); sb.append("SELECT t.AD_Client_ID, 0, 'Y', getDate(), "+getUpdatedBy()+", getDate(), "+getUpdatedBy()+"," - + "t.AD_Tree_ID, ").append(get_ID()).append(", 0, 999"); + + "t.AD_Tree_ID, ") + .append(get_ID() > MTable.MAX_OFFICIAL_ID && Env.isLogMigrationScript(get_TableName()) + ? "toRecordId("+DB.TO_STRING(get_TableName())+","+DB.TO_STRING(get_UUID())+")" + : get_ID()) + .append(", 0, 999"); if (uuidColumn != null && uuidFunction) sb.append(", Generate_UUID() "); else sb.append(" "); sb.append("FROM AD_Tree t " - + "WHERE t.AD_Client_ID=").append(getAD_Client_ID()).append(" AND t.IsActive='Y'"); + + "WHERE t.AD_Client_ID=") + .append(getAD_Client_ID() > MTable.MAX_OFFICIAL_ID && Env.isLogMigrationScript(get_TableName()) + ? "toRecordId('AD_Client',"+DB.TO_STRING(MClient.get(getAD_Client_ID()).getAD_Client_UU())+")" + : getAD_Client_ID()) + .append(" AND t.IsActive='Y'"); // Account Element Value handling if (C_Element_ID != 0) sb.append(" AND EXISTS (SELECT * FROM C_Element ae WHERE ae.C_Element_ID=") - .append(C_Element_ID).append(" AND t.AD_Tree_ID=ae.AD_Tree_ID)"); + .append(C_Element_ID > MTable.MAX_OFFICIAL_ID && Env.isLogMigrationScript(get_TableName()) + ? "toRecordId('C_Element',"+DB.TO_STRING(new MElement(getCtx(), C_Element_ID, get_TrxName()).getC_Element_UU())+")" + : C_Element_ID) + .append(" AND t.AD_Tree_ID=ae.AD_Tree_ID)"); else // std trees sb.append(" AND t.IsAllNodes='Y' AND t.TreeType='").append(treeType).append("'"); if (MTree_Base.TREETYPE_CustomTable.equals(treeType)) - sb.append(" AND t.AD_Table_ID=").append(get_Table_ID()); + sb.append(" AND t.AD_Table_ID=") + .append(get_Table_ID() > MTable.MAX_OFFICIAL_ID && Env.isLogMigrationScript(get_TableName()) + ? "toRecordId('AD_Table',"+DB.TO_STRING(MTable.get(get_Table_ID()).getAD_Table_UU())+")" + : get_Table_ID()); // Duplicate Check sb.append(" AND NOT EXISTS (SELECT * FROM " + MTree_Base.getNodeTableName(treeType) + " e " - + "WHERE e.AD_Tree_ID=t.AD_Tree_ID AND Node_ID=").append(get_ID()).append(")"); + + "WHERE e.AD_Tree_ID=t.AD_Tree_ID AND Node_ID=") + .append(get_ID() > MTable.MAX_OFFICIAL_ID && Env.isLogMigrationScript(get_TableName()) + ? "toRecordId("+DB.TO_STRING(get_TableName())+","+DB.TO_STRING(get_UUID())+")" + : get_ID()).append(")"); int no = DB.executeUpdate(sb.toString(), get_TrxName()); if (no > 0) { if (log.isLoggable(Level.FINE)) log.fine("#" + no + " - TreeType=" + treeType); diff --git a/org.adempiere.base/src/org/compiere/util/Env.java b/org.adempiere.base/src/org/compiere/util/Env.java index e46ae02d36..b455d84bfc 100644 --- a/org.adempiere.base/src/org/compiere/util/Env.java +++ b/org.adempiere.base/src/org/compiere/util/Env.java @@ -47,6 +47,7 @@ import org.adempiere.util.ServerContext; import org.adempiere.util.ServerContextProvider; import org.compiere.Adempiere; import org.compiere.db.CConnection; +import org.compiere.dbPort.Convert; import org.compiere.model.GridTab; import org.compiere.model.GridWindowVO; import org.compiere.model.MClient; @@ -55,6 +56,7 @@ import org.compiere.model.MLookupCache; import org.compiere.model.MQuery; import org.compiere.model.MRefList; import org.compiere.model.MRole; +import org.compiere.model.MSequence; import org.compiere.model.MSession; import org.compiere.model.MSysConfig; import org.compiere.model.MTable; @@ -142,7 +144,6 @@ public final class Env private static final String PREFIX_SYSTEM_VARIABLE = "$env."; private final static ContextProvider clientContextProvider = new DefaultContextProvider(); - private static List eventListeners = new ArrayList(); @@ -2201,4 +2202,51 @@ public final class Env } } + /** + * @param tableName + * @return true if log migration script is turn on and should be used for tableName + */ + public static boolean isLogMigrationScript(String tableName) { + boolean logMigrationScript = false; + if (Ini.isClient()) { + logMigrationScript = Ini.isPropertyBool(Ini.P_LOGMIGRATIONSCRIPT); + } else { + String sysProperty = Env.getCtx().getProperty(Ini.P_LOGMIGRATIONSCRIPT, "N"); + logMigrationScript = "y".equalsIgnoreCase(sysProperty) || "true".equalsIgnoreCase(sysProperty); + } + + return logMigrationScript ? !Convert.isDontLogTable(tableName) : false; + } + + /** + * @return true if centralized id is turn on and should be used for tableName + */ + public static boolean isUseCentralizedId(String tableName) + { + String sysProperty = Env.getCtx().getProperty(Ini.P_ADEMPIERESYS, "N"); + boolean adempiereSys = "y".equalsIgnoreCase(sysProperty) || "true".equalsIgnoreCase(sysProperty); + if (adempiereSys && Env.getAD_Client_ID(Env.getCtx()) > 11) + adempiereSys = false; + + if (adempiereSys) + { + boolean b = MSysConfig.getBooleanValue(MSysConfig.DICTIONARY_ID_USE_CENTRALIZED_ID, true); + if (b) + return !MSequence.isExceptionCentralized(tableName); + else + return b; + } + else + { + boolean queryProjectServer = false; + if (MSequence.isTableWithEntityType(tableName)) + queryProjectServer = true; + if (!queryProjectServer && MSequence.Table_Name.equalsIgnoreCase(tableName)) + queryProjectServer = true; + if (queryProjectServer && !MSequence.isExceptionCentralized(tableName)) { + return MSysConfig.getBooleanValue(MSysConfig.PROJECT_ID_USE_CENTRALIZED_ID, false); + } + } + return false; + } } // Env \ No newline at end of file diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/ADSortTab.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/ADSortTab.java index 773204781c..eefd7685c4 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/ADSortTab.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/ADSortTab.java @@ -21,7 +21,9 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.logging.Level; @@ -42,12 +44,15 @@ import org.adempiere.webui.window.Dialog; import org.compiere.model.GridTab; import org.compiere.model.MRole; import org.compiere.model.MSysConfig; +import org.compiere.model.MTable; +import org.compiere.model.PO; import org.compiere.util.CLogger; import org.compiere.util.DB; import org.compiere.util.Env; import org.compiere.util.KeyNamePair; import org.compiere.util.Msg; import org.compiere.util.NamePair; +import org.compiere.util.Trx; import org.compiere.util.Util; import org.zkoss.zk.au.out.AuFocus; import org.zkoss.zk.ui.event.DropEvent; @@ -701,66 +706,102 @@ public class ADSortTab extends Panel implements IADTabpanel if (!adWindowPanel.getToolbar().isSaveEnable()) return; boolean ok = true; - //TODO: should use model instead to enable change log and event handling StringBuilder info = new StringBuilder(); - StringBuffer sql = null; - // noList - Set SortColumn to null and optional YesNo Column to 'N' - for (int i = 0; i < noModel.getSize(); i++) - { - ListElement pp = (ListElement)noModel.getElementAt(i); - if (!pp.isUpdateable()) - continue; - if(pp.getSortNo() == 0 && (m_ColumnYesNoName == null || !pp.isYes())) - continue; // no changes - // - sql = new StringBuffer(); - sql.append("UPDATE ").append(m_TableName) - .append(" SET ").append(m_ColumnSortName).append("=0"); - if (m_ColumnYesNoName != null) - sql.append(",").append(m_ColumnYesNoName).append("='N'"); - sql.append(", Updated=getDate(), UpdatedBy=").append(Env.getAD_User_ID(Env.getCtx())); - sql.append(" WHERE ").append(m_KeyColumnName).append("=").append(pp.getKey()); - if (DB.executeUpdate(sql.toString(), null) == 1) { - pp.setSortNo(0); - pp.setIsYes(false); + MTable table = MTable.get(Env.getCtx(), m_TableName); + Map noModelBackup = new HashMap<>(); + Map yesModelBackup = new HashMap<>(); + + Trx trx = Trx.get(Trx.createTrxName("ADSortTab_save"), true); + try { + trx.start(); + // noList - Set SortColumn to null and optional YesNo Column to 'N' + for (int i = 0; i < noModel.getSize(); i++) + { + ListElement pp = (ListElement)noModel.getElementAt(i); + if (!pp.isUpdateable()) + continue; + if(pp.getSortNo() == 0 && (m_ColumnYesNoName == null || !pp.isYes())) + continue; // no changes + // + PO po = table.getPO(pp.getKey(), trx.getTrxName()); + po.set_ValueOfColumn(m_ColumnSortName, 0); + if (m_ColumnYesNoName != null) + po.set_ValueOfColumn(m_ColumnYesNoName, "N"); + try { + po.saveEx(); + ListElement backup = new ListElement(pp.getKey(), pp.getName(), pp.getSortNo(), pp.isYes(), pp.getAD_Client_ID(), pp.getAD_Org_ID()); + noModelBackup.put(i, backup); + pp.setSortNo(0); + pp.setIsYes(false); + } catch (Exception e) { + ok = false; + trx.rollback(); + if (info.length() > 0) + info.append(", "); + info.append(pp.getName()); + log.log(Level.SEVERE, "NoModel - Not updated: " + m_KeyColumnName + "=" + pp.getKey(), e); + break; + } } - else { - ok = false; - if (info.length() > 0) - info.append(", "); - info.append(pp.getName()); - log.log(Level.SEVERE, "NoModel - Not updated: " + m_KeyColumnName + "=" + pp.getKey()); + + if (ok) { + // yesList - Set SortColumn to value and optional YesNo Column to 'Y' + int index = 0; + for (int i = 0; i < yesModel.getSize(); i++) + { + ListElement pp = (ListElement)yesModel.getElementAt(i); + if (!pp.isUpdateable()) + continue; + index += 10; + if(pp.getSortNo() == index && (m_ColumnYesNoName == null || pp.isYes())) + continue; // no changes + // + PO po = table.getPO(pp.getKey(), trx.getTrxName()); + po.set_ValueOfColumn(m_ColumnSortName, index); + if (m_ColumnYesNoName != null) + po.set_ValueOfColumn(m_ColumnYesNoName, "Y"); + try { + po.saveEx(); + ListElement backup = new ListElement(pp.getKey(), pp.getName(), pp.getSortNo(), pp.isYes(), pp.getAD_Client_ID(), pp.getAD_Org_ID()); + yesModelBackup.put(i, backup); + pp.setSortNo(index); + pp.setIsYes(true); + } catch (Exception e) { + ok = false; + trx.rollback(); + if (info.length() > 0) + info.append(", "); + info.append(pp.getName()); + log.log(Level.SEVERE, "YesModel - Not updated: " + m_KeyColumnName + "=" + pp.getKey(), e); + break; + } + } } - } - // yesList - Set SortColumn to value and optional YesNo Column to 'Y' - int index = 0; - for (int i = 0; i < yesModel.getSize(); i++) - { - ListElement pp = (ListElement)yesModel.getElementAt(i); - if (!pp.isUpdateable()) - continue; - index += 10; - if(pp.getSortNo() == index && (m_ColumnYesNoName == null || pp.isYes())) - continue; // no changes - // - sql = new StringBuffer(); - sql.append("UPDATE ").append(m_TableName) - .append(" SET ").append(m_ColumnSortName).append("=").append(index); - if (m_ColumnYesNoName != null) - sql.append(",").append(m_ColumnYesNoName).append("='Y'"); - sql.append(", Updated=getDate(), UpdatedBy=").append(Env.getAD_User_ID(Env.getCtx())); - sql.append(" WHERE ").append(m_KeyColumnName).append("=").append(pp.getKey()); - if (DB.executeUpdate(sql.toString(), null) == 1) { - pp.setSortNo(index); - pp.setIsYes(true); + + if (ok) { + try { + trx.commit(true); + } catch (Exception e) { + ok = false; + trx.rollback(); + info.append("Failed to commit database transaction"); + log.log(Level.SEVERE, "Failed to commit database transaction", e); + } } - else { - ok = false; - if (info.length() > 0) - info.append(", "); - info.append(pp.getName()); - log.log(Level.SEVERE, "YesModel - Not updated: " + m_KeyColumnName + "=" + pp.getKey()); + + if (!ok) { + //rollback changes to yes and no model + for(Integer index : noModelBackup.keySet()) { + ListElement e = noModelBackup.get(index); + noModel.setElementAt(e, index); + } + for(Integer index : yesModelBackup.keySet()) { + ListElement e = yesModelBackup.get(index); + yesModel.setElementAt(e, index); + } } + } finally { + trx.close(); } // if (ok) { diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/window/WPreference.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/window/WPreference.java index 332b49b77b..6f1b9d2a95 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/window/WPreference.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/window/WPreference.java @@ -36,6 +36,7 @@ import org.compiere.model.MUserPreference; import org.compiere.model.SystemIDs; import org.compiere.util.CLogger; import org.compiere.util.Env; +import org.compiere.util.Ini; import org.compiere.util.Msg; import org.zkoss.zk.ui.Component; import org.zkoss.zk.ui.event.Event; @@ -136,24 +137,24 @@ public class WPreference extends WQuickEntry implements EventListener, Va if (Env.getAD_Client_ID(Env.getCtx()) <= 20 && Env.getAD_User_ID(Env.getCtx()) <= 102) { this.appendChild(new Space()); - adempiereSys = new WYesNoEditor("AdempiereSys", Msg.getMsg(Env.getCtx(), "AdempiereSys", true), + adempiereSys = new WYesNoEditor(Ini.P_ADEMPIERESYS, Msg.getMsg(Env.getCtx(), Ini.P_ADEMPIERESYS, true), null, false, false, true); - adempiereSys.getComponent().setTooltiptext(Msg.getMsg(Env.getCtx(), "AdempiereSys", false)); + adempiereSys.getComponent().setTooltiptext(Msg.getMsg(Env.getCtx(), Ini.P_ADEMPIERESYS, false)); div = new Div(); div.setStyle(LINE_DIV_STYLE); div.appendChild(adempiereSys.getComponent()); this.appendChild(div); - adempiereSys.setValue(Env.getCtx().getProperty("AdempiereSys")); + adempiereSys.setValue(Env.getCtx().getProperty(Ini.P_ADEMPIERESYS)); adempiereSys.addValueChangeListener(this); - logMigrationScript = new WYesNoEditor("LogMigrationScript", Msg.getMsg(Env.getCtx(), "LogMigrationScript", true), + logMigrationScript = new WYesNoEditor(Ini.P_LOGMIGRATIONSCRIPT, Msg.getMsg(Env.getCtx(), Ini.P_LOGMIGRATIONSCRIPT, true), null, false, false, true); - logMigrationScript.getComponent().setTooltiptext(Msg.getMsg(Env.getCtx(), "LogMigrationScript", false)); + logMigrationScript.getComponent().setTooltiptext(Msg.getMsg(Env.getCtx(), Ini.P_LOGMIGRATIONSCRIPT, false)); div = new Div(); div.setStyle(LINE_DIV_STYLE); div.appendChild(logMigrationScript.getComponent()); this.appendChild(div); - logMigrationScript.setValue(Env.getCtx().getProperty("LogMigrationScript")); + logMigrationScript.setValue(Env.getCtx().getProperty(Ini.P_LOGMIGRATIONSCRIPT)); logMigrationScript.addValueChangeListener(this); } @@ -250,12 +251,12 @@ public class WPreference extends WQuickEntry implements EventListener, Va // Log Migration Script and AdempiereSys are just in-memory preferences, must not be saved if (logMigrationScript != null) { - Env.getCtx().setProperty("LogMigrationScript", (Boolean)logMigrationScript.getValue() ? "Y" : "N"); - Env.getCtx().setProperty("P|LogMigrationScript", (Boolean)logMigrationScript.getValue() ? "Y" : "N"); + Env.getCtx().setProperty(Ini.P_LOGMIGRATIONSCRIPT, (Boolean)logMigrationScript.getValue() ? "Y" : "N"); + Env.getCtx().setProperty("P|"+Ini.P_LOGMIGRATIONSCRIPT, (Boolean)logMigrationScript.getValue() ? "Y" : "N"); } if (adempiereSys != null) { - Env.getCtx().setProperty("AdempiereSys", (Boolean)adempiereSys.getValue() ? "Y" : "N"); - Env.getCtx().setProperty("P|AdempiereSys", (Boolean)adempiereSys.getValue() ? "Y" : "N"); + Env.getCtx().setProperty(Ini.P_ADEMPIERESYS, (Boolean)adempiereSys.getValue() ? "Y" : "N"); + Env.getCtx().setProperty("P|"+Ini.P_ADEMPIERESYS, (Boolean)adempiereSys.getValue() ? "Y" : "N"); } this.detach(); @@ -265,12 +266,12 @@ public class WPreference extends WQuickEntry implements EventListener, Va if (evt.getSource() instanceof WYesNoEditor) { // Log Migration Script and AdempiereSys are just in-memory preferences, set them without need to save if (evt.getSource() == logMigrationScript) { - Env.getCtx().setProperty("LogMigrationScript", (Boolean)logMigrationScript.getValue() ? "Y" : "N"); - Env.getCtx().setProperty("P|LogMigrationScript", (Boolean)logMigrationScript.getValue() ? "Y" : "N"); + Env.getCtx().setProperty(Ini.P_LOGMIGRATIONSCRIPT, (Boolean)logMigrationScript.getValue() ? "Y" : "N"); + Env.getCtx().setProperty("P|"+Ini.P_LOGMIGRATIONSCRIPT, (Boolean)logMigrationScript.getValue() ? "Y" : "N"); dynamicDisplay(); } else if (evt.getSource() == adempiereSys) { - Env.getCtx().setProperty("AdempiereSys", (Boolean)adempiereSys.getValue() ? "Y" : "N"); - Env.getCtx().setProperty("P|AdempiereSys", (Boolean)adempiereSys.getValue() ? "Y" : "N"); + Env.getCtx().setProperty(Ini.P_ADEMPIERESYS, (Boolean)adempiereSys.getValue() ? "Y" : "N"); + Env.getCtx().setProperty("P|"+Ini.P_ADEMPIERESYS, (Boolean)adempiereSys.getValue() ? "Y" : "N"); } } super.valueChange(evt); diff --git a/org.idempiere.test/src/org/idempiere/test/base/POTest.java b/org.idempiere.test/src/org/idempiere/test/base/POTest.java index f5045d3cb2..75483b3905 100644 --- a/org.idempiere.test/src/org/idempiere/test/base/POTest.java +++ b/org.idempiere.test/src/org/idempiere/test/base/POTest.java @@ -32,20 +32,29 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; +import java.io.File; import java.math.BigDecimal; import java.math.RoundingMode; import java.util.Properties; import org.adempiere.exceptions.DBException; +import org.compiere.dbPort.Convert; +import org.compiere.model.I_AD_UserPreference; +import org.compiere.model.MAcctSchema; import org.compiere.model.MBPartner; import org.compiere.model.MClient; import org.compiere.model.MMessage; +import org.compiere.model.MProduct; +import org.compiere.model.MProductCategory; +import org.compiere.model.MProductCategoryAcct; import org.compiere.model.MTest; import org.compiere.model.POInfo; import org.compiere.util.DB; import org.compiere.util.Env; +import org.compiere.util.Ini; import org.compiere.util.Trx; import org.idempiere.test.AbstractTestCase; +import org.idempiere.test.DictionaryIDs; import org.junit.jupiter.api.Test; /** @@ -480,4 +489,56 @@ public class POTest extends AbstractTestCase assertEquals(expected, testPo.getTestVirtualQty().setScale(2, RoundingMode.HALF_UP), "Wrong value returned"); } + @Test + public void testLogMigrationScript() { + MClient client = MClient.get(Env.getCtx()); + MAcctSchema as = client.getAcctSchema(); + + assertFalse(Env.isLogMigrationScript(MProduct.Table_Name), "Unexpected Log Migration Script default for MProduct"); + Env.getCtx().setProperty(Ini.P_LOGMIGRATIONSCRIPT, "Y"); + Env.setContext(Env.getCtx(), I_AD_UserPreference.COLUMNNAME_MigrationScriptComment, "testLogMigrationScript"); + assertTrue(Env.isLogMigrationScript(MProduct.Table_Name), "Unexpected Log Migration Script Y/N value for MProduct"); + String fileName = Convert.getMigrationScriptFileName("testLogMigrationScript"); + String folderPg = Convert.getMigrationScriptFolder("postgresql"); + String folderOr = Convert.getMigrationScriptFolder("oracle"); + + MProductCategory lotLevel = new MProductCategory(Env.getCtx(), 0, null); + lotLevel.setName("testLogMigrationScript"); + lotLevel.saveEx(); + MProduct product = null; + try { + MProductCategoryAcct lotLevelAcct = MProductCategoryAcct.get(lotLevel.get_ID(), as.get_ID()); + lotLevelAcct = new MProductCategoryAcct(Env.getCtx(), lotLevelAcct); + lotLevelAcct.setCostingLevel(MAcctSchema.COSTINGLEVEL_BatchLot); + lotLevelAcct.saveEx(); + + product = new MProduct(Env.getCtx(), 0, null); + product.setM_Product_Category_ID(lotLevel.get_ID()); + product.setName("testLogMigrationScript"); + product.setProductType(MProduct.PRODUCTTYPE_Item); + product.setIsStocked(true); + product.setIsSold(true); + product.setIsPurchased(true); + product.setC_UOM_ID(DictionaryIDs.C_UOM.EACH.id); + product.setC_TaxCategory_ID(DictionaryIDs.C_TaxCategory.STANDARD.id); + product.setM_AttributeSet_ID(DictionaryIDs.M_AttributeSet.FERTILIZER_LOT.id); + product.saveEx(); + } finally { + rollback(); + + if (product != null) { + product.set_TrxName(null); + product.deleteEx(true); + } + + lotLevel.deleteEx(true); + } + + File file = new File(folderPg + fileName); + assertTrue(file.exists(), "Not found: " + folderPg + fileName); + file.delete(); + file = new File(folderOr + fileName); + assertTrue(file.exists(), "Not found: " + folderOr + fileName); + file.delete(); + } }