From 975a46ae4599f725f66f52c5d4c4095c3f67ad0b Mon Sep 17 00:00:00 2001 From: Carlos Ruiz Date: Fri, 19 Jan 2018 18:22:16 +0100 Subject: [PATCH 1/7] IDEMPIERE-3580 Advanced Zoom Across --- .../oracle/201801191815_IDEMPIERE-3580.sql | 43 ++++ .../201801191815_IDEMPIERE-3580.sql | 40 +++ .../adempiere/model/GenericZoomProvider.java | 228 ++++++++++++++---- .../compiere/model/I_AD_UserPreference.java | 9 + .../src/org/compiere/model/MTab.java | 24 +- .../org/compiere/model/MZoomCondition.java | 4 +- .../compiere/model/X_AD_UserPreference.java | 23 +- .../adempiere/webui/adwindow/ADSortTab.java | 1 + 8 files changed, 317 insertions(+), 55 deletions(-) create mode 100644 migration/i5.1/oracle/201801191815_IDEMPIERE-3580.sql create mode 100644 migration/i5.1/postgresql/201801191815_IDEMPIERE-3580.sql diff --git a/migration/i5.1/oracle/201801191815_IDEMPIERE-3580.sql b/migration/i5.1/oracle/201801191815_IDEMPIERE-3580.sql new file mode 100644 index 0000000000..0a62ab41ff --- /dev/null +++ b/migration/i5.1/oracle/201801191815_IDEMPIERE-3580.sql @@ -0,0 +1,43 @@ +SET SQLBLANKLINES ON +SET DEFINE OFF + +-- IDEMPIERE-3580 Advanced Zoom Across +-- Jan 19, 2018 6:08:19 PM CET +INSERT INTO AD_SysConfig (AD_SysConfig_ID,AD_Client_ID,AD_Org_ID,Created,Updated,CreatedBy,UpdatedBy,IsActive,Name,Value,Description,EntityType,ConfigurationLevel,AD_SysConfig_UU) VALUES (200113,0,0,TO_DATE('2018-01-19 18:08:19','YYYY-MM-DD HH24:MI:SS'),TO_DATE('2018-01-19 18:08:19','YYYY-MM-DD HH24:MI:SS'),100,100,'Y','ZOOM_ACROSS_QUERY_TIMEOUT','5','Timeout in seconds for the count queries ran when pushing the button Zoom Across','D','C','a1f85478-e2d8-4c78-98ab-283f74e090ec') +; + +-- Jan 19, 2018 6:10:52 PM CET +INSERT INTO AD_Element (AD_Element_ID,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,ColumnName,Name,Description,Help,PrintName,EntityType,AD_Element_UU) VALUES (203157,0,0,'Y',TO_DATE('2018-01-19 18:09:06','YYYY-MM-DD HH24:MI:SS'),100,TO_DATE('2018-01-19 18:09:06','YYYY-MM-DD HH24:MI:SS'),100,'IsDetailedZoomAcross','Detailed Zoom Across',NULL,'The toolbar button zoom across discover where the record on screen is used on first tabs of windows. With detailed zoom across it goes deeper in the discovery of relationships within detailed tabs.','Detailed Zoom Across','D','58745104-ed8b-459c-aed8-6a9d9c4e33a1') +; + +-- Jan 19, 2018 6:11:25 PM CET +INSERT INTO AD_Column (AD_Column_ID,Version,Name,Help,AD_Table_ID,ColumnName,DefaultValue,FieldLength,IsKey,IsParent,IsMandatory,IsTranslated,IsIdentifier,SeqNo,IsEncrypted,AD_Reference_ID,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,AD_Element_ID,IsUpdateable,IsSelectionColumn,EntityType,IsSyncDatabase,IsAlwaysUpdateable,IsAutocomplete,IsAllowLogging,AD_Column_UU,IsAllowCopy,SeqNoSelection,IsToolbarButton,IsSecure) VALUES (213339,0,'Detailed Zoom Across','The toolbar button zoom across discover where the record on screen is used on first tabs of windows. With detailed zoom across it goes deeper in the discovery of relationships within detailed tabs.',200174,'IsDetailedZoomAcross','N',1,'N','N','N','N','N',0,'N',20,0,0,'Y',TO_DATE('2018-01-19 18:11:24','YYYY-MM-DD HH24:MI:SS'),100,TO_DATE('2018-01-19 18:11:24','YYYY-MM-DD HH24:MI:SS'),100,203157,'Y','N','D','N','N','N','Y','c2e13243-17fa-45f4-9135-4fb4d1572664','Y',0,'N','N') +; + +-- Jan 19, 2018 6:11:30 PM CET +ALTER TABLE AD_UserPreference ADD IsDetailedZoomAcross CHAR(1) DEFAULT 'N' CHECK (IsDetailedZoomAcross IN ('Y','N')) +; + +-- Jan 19, 2018 6:11:39 PM CET +INSERT INTO AD_Field (AD_Field_ID,Name,Help,AD_Tab_ID,AD_Column_ID,IsDisplayed,DisplayLength,SeqNo,IsSameLine,IsHeading,IsFieldOnly,IsEncrypted,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,IsReadOnly,IsCentrallyMaintained,EntityType,AD_Field_UU,IsDisplayedGrid,SeqNoGrid,XPosition,ColumnSpan) VALUES (205303,'Detailed Zoom Across','The toolbar button zoom across discover where the record on screen is used on first tabs of windows. With detailed zoom across it goes deeper in the discovery of relationships within detailed tabs.',200189,213339,'Y',1,90,'N','N','N','N',0,0,'Y',TO_DATE('2018-01-19 18:11:39','YYYY-MM-DD HH24:MI:SS'),100,TO_DATE('2018-01-19 18:11:39','YYYY-MM-DD HH24:MI:SS'),100,'N','Y','D','6c0b384b-f05e-4843-8b0e-eb15e2ee81d2','Y',90,2,2) +; + +-- Jan 19, 2018 6:13:19 PM CET +UPDATE AD_Field SET IsDisplayed='Y', SeqNo=50, AD_Reference_Value_ID=NULL, AD_Val_Rule_ID=NULL, XPosition=5, ColumnSpan=1, IsToolbarButton=NULL,Updated=TO_DATE('2018-01-19 18:13:19','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=203797 +; + +-- Jan 19, 2018 6:13:19 PM CET +UPDATE AD_Field SET IsDisplayed='Y', SeqNo=70, AD_Reference_Value_ID=NULL, AD_Val_Rule_ID=NULL, XPosition=5, ColumnSpan=1, IsToolbarButton=NULL,Updated=TO_DATE('2018-01-19 18:13:19','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=205303 +; + +-- Jan 19, 2018 6:13:19 PM CET +UPDATE AD_Field SET IsDisplayed='Y', SeqNo=80, AD_Reference_Value_ID=NULL, AD_Val_Rule_ID=NULL, XPosition=1, ColumnSpan=1, IsToolbarButton=NULL,Updated=TO_DATE('2018-01-19 18:13:19','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=203800 +; + +-- Jan 19, 2018 6:13:19 PM CET +UPDATE AD_Field SET IsDisplayed='Y', SeqNo=90, AD_Reference_Value_ID=NULL, AD_Val_Rule_ID=NULL, XPosition=2, ColumnSpan=1, IsToolbarButton=NULL,Updated=TO_DATE('2018-01-19 18:13:19','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=203798 +; + +SELECT register_migration_script('201801191815_IDEMPIERE-3580.sql') FROM dual +; + diff --git a/migration/i5.1/postgresql/201801191815_IDEMPIERE-3580.sql b/migration/i5.1/postgresql/201801191815_IDEMPIERE-3580.sql new file mode 100644 index 0000000000..9818fbf0a6 --- /dev/null +++ b/migration/i5.1/postgresql/201801191815_IDEMPIERE-3580.sql @@ -0,0 +1,40 @@ +-- IDEMPIERE-3580 Advanced Zoom Across +-- Jan 19, 2018 6:08:19 PM CET +INSERT INTO AD_SysConfig (AD_SysConfig_ID,AD_Client_ID,AD_Org_ID,Created,Updated,CreatedBy,UpdatedBy,IsActive,Name,Value,Description,EntityType,ConfigurationLevel,AD_SysConfig_UU) VALUES (200113,0,0,TO_TIMESTAMP('2018-01-19 18:08:19','YYYY-MM-DD HH24:MI:SS'),TO_TIMESTAMP('2018-01-19 18:08:19','YYYY-MM-DD HH24:MI:SS'),100,100,'Y','ZOOM_ACROSS_QUERY_TIMEOUT','5','Timeout in seconds for the count queries ran when pushing the button Zoom Across','D','C','a1f85478-e2d8-4c78-98ab-283f74e090ec') +; + +-- Jan 19, 2018 6:10:52 PM CET +INSERT INTO AD_Element (AD_Element_ID,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,ColumnName,Name,Description,Help,PrintName,EntityType,AD_Element_UU) VALUES (203157,0,0,'Y',TO_TIMESTAMP('2018-01-19 18:09:06','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2018-01-19 18:09:06','YYYY-MM-DD HH24:MI:SS'),100,'IsDetailedZoomAcross','Detailed Zoom Across',NULL,'The toolbar button zoom across discover where the record on screen is used on first tabs of windows. With detailed zoom across it goes deeper in the discovery of relationships within detailed tabs.','Detailed Zoom Across','D','58745104-ed8b-459c-aed8-6a9d9c4e33a1') +; + +-- Jan 19, 2018 6:11:25 PM CET +INSERT INTO AD_Column (AD_Column_ID,Version,Name,Help,AD_Table_ID,ColumnName,DefaultValue,FieldLength,IsKey,IsParent,IsMandatory,IsTranslated,IsIdentifier,SeqNo,IsEncrypted,AD_Reference_ID,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,AD_Element_ID,IsUpdateable,IsSelectionColumn,EntityType,IsSyncDatabase,IsAlwaysUpdateable,IsAutocomplete,IsAllowLogging,AD_Column_UU,IsAllowCopy,SeqNoSelection,IsToolbarButton,IsSecure) VALUES (213339,0,'Detailed Zoom Across','The toolbar button zoom across discover where the record on screen is used on first tabs of windows. With detailed zoom across it goes deeper in the discovery of relationships within detailed tabs.',200174,'IsDetailedZoomAcross','N',1,'N','N','N','N','N',0,'N',20,0,0,'Y',TO_TIMESTAMP('2018-01-19 18:11:24','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2018-01-19 18:11:24','YYYY-MM-DD HH24:MI:SS'),100,203157,'Y','N','D','N','N','N','Y','c2e13243-17fa-45f4-9135-4fb4d1572664','Y',0,'N','N') +; + +-- Jan 19, 2018 6:11:30 PM CET +ALTER TABLE AD_UserPreference ADD COLUMN IsDetailedZoomAcross CHAR(1) DEFAULT 'N' CHECK (IsDetailedZoomAcross IN ('Y','N')) +; + +-- Jan 19, 2018 6:11:39 PM CET +INSERT INTO AD_Field (AD_Field_ID,Name,Help,AD_Tab_ID,AD_Column_ID,IsDisplayed,DisplayLength,SeqNo,IsSameLine,IsHeading,IsFieldOnly,IsEncrypted,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,IsReadOnly,IsCentrallyMaintained,EntityType,AD_Field_UU,IsDisplayedGrid,SeqNoGrid,XPosition,ColumnSpan) VALUES (205303,'Detailed Zoom Across','The toolbar button zoom across discover where the record on screen is used on first tabs of windows. With detailed zoom across it goes deeper in the discovery of relationships within detailed tabs.',200189,213339,'Y',1,90,'N','N','N','N',0,0,'Y',TO_TIMESTAMP('2018-01-19 18:11:39','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2018-01-19 18:11:39','YYYY-MM-DD HH24:MI:SS'),100,'N','Y','D','6c0b384b-f05e-4843-8b0e-eb15e2ee81d2','Y',90,2,2) +; + +-- Jan 19, 2018 6:13:19 PM CET +UPDATE AD_Field SET IsDisplayed='Y', SeqNo=50, AD_Reference_Value_ID=NULL, AD_Val_Rule_ID=NULL, XPosition=5, ColumnSpan=1, IsToolbarButton=NULL,Updated=TO_TIMESTAMP('2018-01-19 18:13:19','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=203797 +; + +-- Jan 19, 2018 6:13:19 PM CET +UPDATE AD_Field SET IsDisplayed='Y', SeqNo=70, AD_Reference_Value_ID=NULL, AD_Val_Rule_ID=NULL, XPosition=5, ColumnSpan=1, IsToolbarButton=NULL,Updated=TO_TIMESTAMP('2018-01-19 18:13:19','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=205303 +; + +-- Jan 19, 2018 6:13:19 PM CET +UPDATE AD_Field SET IsDisplayed='Y', SeqNo=80, AD_Reference_Value_ID=NULL, AD_Val_Rule_ID=NULL, XPosition=1, ColumnSpan=1, IsToolbarButton=NULL,Updated=TO_TIMESTAMP('2018-01-19 18:13:19','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=203800 +; + +-- Jan 19, 2018 6:13:19 PM CET +UPDATE AD_Field SET IsDisplayed='Y', SeqNo=90, AD_Reference_Value_ID=NULL, AD_Val_Rule_ID=NULL, XPosition=2, ColumnSpan=1, IsToolbarButton=NULL,Updated=TO_TIMESTAMP('2018-01-19 18:13:19','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=203798 +; + +SELECT register_migration_script('201801191815_IDEMPIERE-3580.sql') FROM dual +; + diff --git a/org.adempiere.base/src/org/adempiere/model/GenericZoomProvider.java b/org.adempiere.base/src/org/adempiere/model/GenericZoomProvider.java index 464380bc3f..8f5c1fb486 100644 --- a/org.adempiere.base/src/org/adempiere/model/GenericZoomProvider.java +++ b/org.adempiere.base/src/org/adempiere/model/GenericZoomProvider.java @@ -17,16 +17,23 @@ import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.Properties; import java.util.logging.Level; import org.adempiere.exceptions.AdempiereException; import org.compiere.model.MQuery; +import org.compiere.model.MRole; +import org.compiere.model.MSysConfig; import org.compiere.model.MTab; +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.Util; /** * Generic provider of zoom targets. Contains pieces of {@link org.compiere.apps.AZoomAcross} @@ -37,32 +44,69 @@ import org.compiere.util.Env; */ public class GenericZoomProvider implements IZoomProvider { - private static final CLogger logger = CLogger - .getCLogger(GenericZoomProvider.class); + private static final CLogger logger = CLogger.getCLogger(GenericZoomProvider.class); + + private Map queries; public List retrieveZoomInfos(PO po) { + // User preference + boolean detailedZoom = "Y".equals(Env.getContext(Env.getCtx(), "P|IsDetailedZoomAcross")); - String sql = "SELECT DISTINCT ws.AD_Window_ID, ws.Name, wp.AD_Window_ID, wp.Name, t.TableName, tts.AD_Tab_ID, ttp.AD_Tab_ID " - + "FROM AD_Table t "; + StringBuilder sqlb = new StringBuilder().append( + "SELECT w.AD_Window_ID, w.Name, tt.Name, f.Name, t.TableName, c.ColumnName, tt.AD_Tab_ID, "); boolean baseLanguage = Env.isBaseLanguage(Env.getCtx(), "AD_Window"); - if (baseLanguage) - sql += "JOIN AD_Window ws ON (t.AD_Window_ID=ws.AD_Window_ID AND ws.IsActive='Y')" - + " LEFT OUTER JOIN AD_Window wp ON (t.PO_Window_ID=wp.AD_Window_ID AND wp.IsActive='Y') "; - else - sql += "JOIN AD_Window_Trl ws ON (t.AD_Window_ID=ws.AD_Window_ID AND ws.AD_Language=?)" - + " LEFT OUTER JOIN AD_Window_Trl wp ON (t.PO_Window_ID=wp.AD_Window_ID AND wp.AD_Language=?) " - + " JOIN AD_Window wso ON (t.AD_Window_ID=wso.AD_Window_ID AND wso.IsActive='Y')" - + " LEFT OUTER JOIN AD_Window wpo ON (t.PO_Window_ID=wpo.AD_Window_ID AND wpo.IsActive='Y') "; - // WARNING - HardCoded: first tab must have SeqNo = 10 - sql += "JOIN AD_Tab tts ON (tts.AD_Window_ID=ws.AD_Window_ID AND tts.AD_Table_ID=t.AD_Table_ID AND tts.SeqNo=10)" // first tab so - +" LEFT OUTER JOIN AD_Tab ttp ON (ttp.AD_Window_ID=wp.AD_Window_ID AND ttp.AD_Table_ID=t.AD_Table_ID AND ttp.SeqNo=10)" // first tab po - +" WHERE t.TableName NOT LIKE 'I%'" // No Import - + " AND t.AD_Table_ID IN " - + "(SELECT AD_Table_ID FROM AD_Column " - + "WHERE ColumnName=? AND IsKey='N' AND IsParent='N' AND IsActive='Y' AND ColumnSQL IS NULL) " // #x - + "ORDER BY 2"; + String tabAlias; + if (baseLanguage) { + tabAlias = "tt"; + } else { + tabAlias = "tt0"; + } + String justFirstTab = ""; + if (! detailedZoom) { + justFirstTab = " AND " + tabAlias + ".SeqNo=10 "; + } + if (baseLanguage) { + sqlb.append( + "tt.SeqNo " + + "FROM AD_Table t " + + "JOIN AD_Tab tt ON (tt.AD_Table_ID=t.AD_Table_ID AND tt.IsActive='Y' AND tt.Name NOT LIKE 'Used in%' AND tt.IsReadOnly='N' AND tt.IsSortTab='N'") + .append(justFirstTab) + .append(") " + + "JOIN AD_Window w ON (tt.AD_Window_ID=w.AD_Window_ID AND w.IsActive='Y') " + + "JOIN AD_Column c ON (t.AD_Table_ID=c.AD_Table_ID AND c.IsActive='Y' AND c.IsKey='N' AND c.IsParent='N' AND c.ColumnSQL IS NULL) " + + "JOIN AD_Field f ON (f.AD_Column_ID=c.AD_Column_ID AND f.AD_Tab_ID=tt.AD_Tab_ID AND f.IsActive='Y' AND f.IsDisplayed='Y') "); + } else { + sqlb.append( + "tt0.SeqNo " + + "FROM AD_Table t " + + "JOIN AD_Tab tt0 ON (tt0.AD_Table_ID=t.AD_Table_ID AND tt0.IsActive='Y' AND tt0.Name NOT LIKE 'Used in%' AND tt0.IsReadOnly='N' AND tt0.IsSortTab='N'") + .append(justFirstTab) + .append(") " + + "JOIN AD_Tab_Trl tt ON (tt.AD_Tab_ID=tt0.AD_Tab_ID AND tt.AD_Language=?) " + + "JOIN AD_Window w0 ON (tt0.AD_Window_ID=w0.AD_Window_ID AND w0.IsActive='Y') " + + "JOIN AD_Window_Trl w ON (w.AD_Window_ID=w0.AD_Window_ID AND w.AD_Language=?) " + + "JOIN AD_Column c ON (t.AD_Table_ID=c.AD_Table_ID AND c.IsActive='Y' AND c.IsKey='N' AND c.IsParent='N' AND c.ColumnSQL IS NULL) " + + "JOIN AD_Field f0 ON (f0.AD_Column_ID=c.AD_Column_ID AND f0.AD_Tab_ID=tt0.AD_Tab_ID AND f0.IsActive='Y' AND f0.IsDisplayed='Y') " + + "JOIN AD_Field_Trl f ON (f.AD_Field_ID=f0.AD_Field_ID) "); + } + sqlb.append( + "LEFT JOIN AD_Ref_Table r ON (c.AD_Reference_Value_ID=r.AD_Reference_ID) " + + "LEFT JOIN AD_Table tr ON (r.AD_Table_ID=tr.AD_Table_ID) " + + "WHERE t.IsActive='Y' " + + " AND t.TableName NOT LIKE 'I|_%' ESCAPE '|' " // not import tables + + " AND t.TableName NOT LIKE 'T|_%' ESCAPE '|' " // not temp tables + + " AND t.IsView='N' "); // not views + if (detailedZoom) { + sqlb.append( + " AND ( ( c.ColumnName=? AND c.AD_Reference_ID=19) " + + " OR ( c.ColumnName=? AND c.AD_Reference_ID=30 AND c.AD_Reference_Value_ID IS NULL ) " + + " OR ( c.AD_Reference_ID IN (18, 30) AND c.AD_Reference_Value_ID=r.AD_Reference_ID AND tr.TableName=? ) ) "); + } else { + sqlb.append(" AND c.ColumnName=? "); + } + sqlb.append(" ORDER BY 2, 8"); - final PreparedStatement pstmt = DB.prepareStatement(sql, null); + final PreparedStatement pstmt = DB.prepareStatement(sqlb.toString(), null); ResultSet rs = null; try { @@ -71,63 +115,147 @@ public class GenericZoomProvider implements IZoomProvider { pstmt.setString(index++, Env.getAD_Language(Env.getCtx())); pstmt.setString(index++, Env.getAD_Language(Env.getCtx())); } - pstmt.setString(index++, po.get_TableName() + "_ID"); + pstmt.setString(index++, po.get_KeyColumns()[0]); + if (detailedZoom) { + pstmt.setString(index++, po.get_KeyColumns()[0]); + pstmt.setString(index++, po.get_TableName()); + } rs = pstmt.executeQuery(); final List result = new ArrayList(); + queries = new HashMap(); while (rs.next()) { int AD_Window_ID = rs.getInt(1); - String Name = rs.getString(2); - int PO_Window_ID = rs.getInt(3); - int AD_Tab_ID = rs.getInt(6); - int PO_Tab_ID = rs.getInt(7); + String winName = rs.getString(2); + String tabName = rs.getString(3); + String fldName = rs.getString(4); String targetTableName = rs.getString(5); + String targetColumnName = rs.getString(6); + int AD_Tab_ID = rs.getInt(7); - final MQuery query = evaluateQuery(targetTableName, - AD_Tab_ID, Name, po); - result.add(new ZoomInfoFactory.ZoomInfo(AD_Window_ID, - query, Name)); - if (PO_Window_ID != 0 && PO_Tab_ID != 0) { - Name = rs.getString(4); - final MQuery querypo = evaluateQuery(targetTableName, - PO_Tab_ID, Name, po); - result.add(new ZoomInfoFactory.ZoomInfo(PO_Window_ID, - querypo, Name)); + Boolean access = MRole.getDefault().getWindowAccess(AD_Window_ID); + if (access == null) + continue; + + final MQuery query = evaluateQuery(targetTableName, targetColumnName, AD_Tab_ID, po); + if (query != null && query.getRecordCount() > 0) { + if (detailedZoom) { + result.add(new ZoomInfoFactory.ZoomInfo(AD_Window_ID, query, winName + " / " + tabName + " / " + fldName)); + } else { + result.add(new ZoomInfoFactory.ZoomInfo(AD_Window_ID, query, winName)); + } } } + queries = null; return result; } catch (SQLException e) { - logger.log(Level.SEVERE, sql, e); + logger.log(Level.SEVERE, sqlb.toString(), e); throw new AdempiereException(e); } finally { DB.close(rs, pstmt); } } - private static MQuery evaluateQuery(String targetTableName, - int AD_Tab_ID, String Name, final PO po) { + private MQuery evaluateQuery(String targetTableName, String targetColumnName, int AD_Tab_ID, final PO po) { + Properties ctx = Env.getCtx(); + int clientID = Env.getAD_Client_ID(ctx); - MTab tab = new MTab(Env.getCtx(), AD_Tab_ID, null); final MQuery query = new MQuery(); + MTable table = MTable.get(ctx, targetTableName); + if (table.getColumnIndex("AD_Client_ID") < 0) // table doesn't have AD_Client_ID + return null; - query.addRestriction(po.get_TableName() + "_ID=" + po.get_ID()); - if (tab.getWhereClause() != null && tab.getWhereClause().length() > 0) - query.addRestriction("(" + tab.getWhereClause() + ")"); + int tabIDLoop = AD_Tab_ID; + int levelUp = 0; + while (true) { + MTab tab = new MTab(ctx, tabIDLoop, null); + String whereCtx = tab.getWhereClause(); + if (!Util.isEmpty(whereCtx, true)) { + if (whereCtx.indexOf("@") != -1) + whereCtx = Env.parseVariable(whereCtx, po, null, true); + if (whereCtx.indexOf("@") != -1) // could not parse - probably window context variable in where tab + return null; + if (levelUp == 0) { + query.addRestriction("(" + whereCtx + ")"); + } else if (levelUp == 1) { + MTable parentTable = MTable.get(ctx, tab.getAD_Table_ID()); + String parentTableName = parentTable.getTableName(); + StringBuilder subquery = new StringBuilder() + .append(parentTableName) + .append("_ID IN (SELECT ") + .append(parentTableName) + .append("_ID FROM ") + .append(parentTableName) + .append(" WHERE ") + .append(whereCtx) + .append(")"); + query.addRestriction("(" + subquery + ")"); + } else { + // Cannot add where beyond the first parent - need to implement recursion + return null; + } + } + levelUp++; + tabIDLoop = tab.getParentTabID(); + if (tabIDLoop < 0) + break; + } + + query.addRestriction(targetColumnName + "=" + po.get_ID()); query.setZoomTableName(targetTableName); - query.setZoomColumnName(po.get_KeyColumns()[0]); + query.setZoomColumnName(targetColumnName); query.setZoomValue(po.get_ID()); - query.addRestriction("AD_Client_ID", MQuery.EQUAL, Env.getAD_Client_ID(Env.getCtx())); - - String sql = "SELECT COUNT(*) FROM " + targetTableName + " WHERE " - + Env.parseVariable(query.getWhereClause(false), po, null, false); - int count = DB.getSQLValue(null, sql); + String accessLevel = table.getAccessLevel(); + if ( clientID != 0 + && MTable.ACCESSLEVEL_SystemOnly.equals(accessLevel)) { + return null; + } + if ( clientID != 0 + && ( MTable.ACCESSLEVEL_All.equals(accessLevel) + || MTable.ACCESSLEVEL_SystemPlusClient.equals(accessLevel))) { + query.addRestriction("AD_Client_ID IN (0, " + clientID + ")"); + } else { + query.addRestriction("AD_Client_ID=" + clientID); + } + StringBuilder sqlb = new StringBuilder("SELECT COUNT(*) FROM ") + .append(targetTableName) + .append(" WHERE ") + .append(query.getWhereClause(true)); + String sql = sqlb.toString(); + int count = -1; + if (queries.containsKey(sql)) { + count = queries.get(sql); + } else { + int timeout = MSysConfig.getIntValue("ZOOM_ACROSS_QUERY_TIMEOUT", 5, Env.getAD_Client_ID(Env.getCtx())); // default 5 seconds + count = getSQLValueTimeout(null, sql, timeout); + queries.put(sql, count); + } query.setRecordCount(count); - return query; } + private int getSQLValueTimeout(Object object, String sql, int timeOut) { + int retValue = -1; + PreparedStatement pstmt = null; + ResultSet rs = null; + try { + pstmt = DB.prepareStatement(sql, null); + if (timeOut > 0) + pstmt.setQueryTimeout(timeOut); + rs = pstmt.executeQuery(); + if (rs.next()) + retValue = rs.getInt(1); + } catch (SQLException e) { + logger.warning(e.getMessage() + " -> " + sql); + } finally { + DB.close(rs, pstmt); + rs = null; pstmt = null; + } + return retValue; + } + } diff --git a/org.adempiere.base/src/org/compiere/model/I_AD_UserPreference.java b/org.adempiere.base/src/org/compiere/model/I_AD_UserPreference.java index 5bb19a377c..482759b96b 100644 --- a/org.adempiere.base/src/org/compiere/model/I_AD_UserPreference.java +++ b/org.adempiere.base/src/org/compiere/model/I_AD_UserPreference.java @@ -155,6 +155,15 @@ public interface I_AD_UserPreference */ public boolean isActive(); + /** Column name IsDetailedZoomAcross */ + public static final String COLUMNNAME_IsDetailedZoomAcross = "IsDetailedZoomAcross"; + + /** Set Detailed Zoom Across */ + public void setIsDetailedZoomAcross (boolean IsDetailedZoomAcross); + + /** Get Detailed Zoom Across */ + public boolean isDetailedZoomAcross(); + /** Column name ToggleOnDoubleClick */ public static final String COLUMNNAME_ToggleOnDoubleClick = "ToggleOnDoubleClick"; diff --git a/org.adempiere.base/src/org/compiere/model/MTab.java b/org.adempiere.base/src/org/compiere/model/MTab.java index ca83b48527..af504f96d0 100644 --- a/org.adempiere.base/src/org/compiere/model/MTab.java +++ b/org.adempiere.base/src/org/compiere/model/MTab.java @@ -38,11 +38,10 @@ import org.compiere.util.DB; */ public class MTab extends X_AD_Tab { - /** * */ - private static final long serialVersionUID = 4946144044358216142L; + private static final long serialVersionUID = -2964171360368660043L; /** * Standard Constructor @@ -208,5 +207,24 @@ public class MTab extends X_AD_Tab return retValue; } //end vpj-cd e-evolution - + + public int getParentTabID() { + int parentTabID = -1; + + if (getTabLevel() == 0) + return parentTabID; // tab level 0 doesn't have parent + + final String sql = "" + + "SELECT AD_Tab_ID " + + "FROM AD_Tab " + + "WHERE AD_Window_ID = ? " + + " AND SeqNo < ? " + + " AND TabLevel = ? " + + " AND IsActive = 'Y' " + + "ORDER BY SeqNo DESC"; + parentTabID = DB.getSQLValue(get_TrxName(), sql, getAD_Window_ID(), getSeqNo(), getTabLevel()-1); + return parentTabID; + } + + } // M_Tab diff --git a/org.adempiere.base/src/org/compiere/model/MZoomCondition.java b/org.adempiere.base/src/org/compiere/model/MZoomCondition.java index 1867c04ebe..2984a57958 100644 --- a/org.adempiere.base/src/org/compiere/model/MZoomCondition.java +++ b/org.adempiere.base/src/org/compiere/model/MZoomCondition.java @@ -189,7 +189,9 @@ public class MZoomCondition extends X_AD_ZoomCondition { window.initTab(gTab.getTabNo()); GridTab parentTab = gTab.getParentTab(); - int parentId = DB.getSQLValue(null, "SELECT " + gTab.getLinkColumnName() + " FROM " + gTab.getTableName() + " WHERE " + query.getWhereClause()); + int parentId = -1; + if (!Util.isEmpty(gTab.getLinkColumnName())) + parentId = DB.getSQLValue(null, "SELECT " + gTab.getLinkColumnName() + " FROM " + gTab.getTableName() + " WHERE " + query.getWhereClause()); if (parentId <= 0) { if (Util.isEmpty(parentTab.getKeyColumnName())) parentTab.initTab(false); diff --git a/org.adempiere.base/src/org/compiere/model/X_AD_UserPreference.java b/org.adempiere.base/src/org/compiere/model/X_AD_UserPreference.java index 40fa62f6ac..654f4d55c1 100644 --- a/org.adempiere.base/src/org/compiere/model/X_AD_UserPreference.java +++ b/org.adempiere.base/src/org/compiere/model/X_AD_UserPreference.java @@ -29,7 +29,7 @@ public class X_AD_UserPreference extends PO implements I_AD_UserPreference, I_Pe /** * */ - private static final long serialVersionUID = 20171031L; + private static final long serialVersionUID = 20180119L; /** Standard Constructor */ public X_AD_UserPreference (Properties ctx, int AD_UserPreference_ID, String trxName) @@ -194,6 +194,27 @@ public class X_AD_UserPreference extends PO implements I_AD_UserPreference, I_Pe return false; } + /** Set Detailed Zoom Across. + @param IsDetailedZoomAcross Detailed Zoom Across */ + public void setIsDetailedZoomAcross (boolean IsDetailedZoomAcross) + { + set_Value (COLUMNNAME_IsDetailedZoomAcross, Boolean.valueOf(IsDetailedZoomAcross)); + } + + /** Get Detailed Zoom Across. + @return Detailed Zoom Across */ + public boolean isDetailedZoomAcross () + { + Object oo = get_Value(COLUMNNAME_IsDetailedZoomAcross); + if (oo != null) + { + if (oo instanceof Boolean) + return ((Boolean)oo).booleanValue(); + return "Y".equals(oo); + } + return false; + } + /** Set Toggle on Double Click. @param ToggleOnDoubleClick Defines if double click in a field on grid mode switch to form view 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 4a3e153da8..986f1ad9ed 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 @@ -937,6 +937,7 @@ public class ADSortTab extends Panel implements IADTabpanel } public void refresh() { + createUI(); loadData(); } From 7c0bffa908937e41d29957e1836fc5838a2cef81 Mon Sep 17 00:00:00 2001 From: Carlos Ruiz Date: Fri, 19 Jan 2018 23:02:46 +0100 Subject: [PATCH 2/7] IDEMPIERE-1906 Use postgresql SIMILAR TO instead of LIKE --- .../oracle/201801192257_IDEMPIERE-1906.sql | 35 +++++++++++++++++++ .../201801192257_IDEMPIERE-1906.sql | 32 +++++++++++++++++ .../org/adempiere/webui/AdempiereWebUI.java | 2 ++ .../compiere/dbPort/Convert_PostgreSQL.java | 23 +++++++++++- 4 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 migration/i5.1/oracle/201801192257_IDEMPIERE-1906.sql create mode 100644 migration/i5.1/postgresql/201801192257_IDEMPIERE-1906.sql diff --git a/migration/i5.1/oracle/201801192257_IDEMPIERE-1906.sql b/migration/i5.1/oracle/201801192257_IDEMPIERE-1906.sql new file mode 100644 index 0000000000..927a489f93 --- /dev/null +++ b/migration/i5.1/oracle/201801192257_IDEMPIERE-1906.sql @@ -0,0 +1,35 @@ +SET SQLBLANKLINES ON +SET DEFINE OFF + +-- IDEMPIERE-1906 Use postgresql SIMILAR TO instead of LIKE +-- Jan 19, 2018 10:55:51 PM CET +INSERT INTO AD_Element (AD_Element_ID,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,ColumnName,Name,Description,Help,PrintName,EntityType,AD_Element_UU) VALUES (203158,0,0,'Y',TO_DATE('2018-01-19 22:54:41','YYYY-MM-DD HH24:MI:SS'),100,TO_DATE('2018-01-19 22:54:41','YYYY-MM-DD HH24:MI:SS'),100,'IsUseSimilarTo','Use Similar To',NULL,'In PostgreSQL database enable using the more powerful SIMILAR TO instead of LIKE for matching queries.','Use Similar To','D','6917772a-f465-411c-95ed-8a834249a578') +; + +-- Jan 19, 2018 10:56:06 PM CET +INSERT INTO AD_Column (AD_Column_ID,Version,Name,Help,AD_Table_ID,ColumnName,DefaultValue,FieldLength,IsKey,IsParent,IsMandatory,IsTranslated,IsIdentifier,SeqNo,IsEncrypted,AD_Reference_ID,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,AD_Element_ID,IsUpdateable,IsSelectionColumn,EntityType,IsSyncDatabase,IsAlwaysUpdateable,IsAutocomplete,IsAllowLogging,AD_Column_UU,IsAllowCopy,SeqNoSelection,IsToolbarButton,IsSecure) VALUES (213340,0,'Use Similar To','In PostgreSQL database enable using the more powerful SIMILAR TO instead of LIKE for matching queries.',200174,'IsUseSimilarTo','N',1,'N','N','N','N','N',0,'N',20,0,0,'Y',TO_DATE('2018-01-19 22:56:06','YYYY-MM-DD HH24:MI:SS'),100,TO_DATE('2018-01-19 22:56:06','YYYY-MM-DD HH24:MI:SS'),100,203158,'Y','N','D','N','N','N','Y','81564050-9da4-492f-b298-cf8aac897583','Y',0,'N','N') +; + +-- Jan 19, 2018 10:56:07 PM CET +ALTER TABLE AD_UserPreference ADD IsUseSimilarTo CHAR(1) DEFAULT 'N' CHECK (IsUseSimilarTo IN ('Y','N')) +; + +-- Jan 19, 2018 10:56:20 PM CET +INSERT INTO AD_Field (AD_Field_ID,Name,Help,AD_Tab_ID,AD_Column_ID,IsDisplayed,DisplayLength,SeqNo,IsSameLine,IsHeading,IsFieldOnly,IsEncrypted,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,IsReadOnly,IsCentrallyMaintained,EntityType,AD_Field_UU,IsDisplayedGrid,SeqNoGrid,XPosition,ColumnSpan) VALUES (205304,'Use Similar To','In PostgreSQL database enable using the more powerful SIMILAR TO instead of LIKE for matching queries.',200189,213340,'Y',1,100,'N','N','N','N',0,0,'Y',TO_DATE('2018-01-19 22:56:20','YYYY-MM-DD HH24:MI:SS'),100,TO_DATE('2018-01-19 22:56:20','YYYY-MM-DD HH24:MI:SS'),100,'N','Y','D','92dd69a0-5640-41d9-b26f-2546a71eb75b','Y',100,2,2) +; + +-- Jan 19, 2018 10:56:41 PM CET +UPDATE AD_Field SET IsDisplayed='Y', SeqNo=90, AD_Reference_Value_ID=NULL, AD_Val_Rule_ID=NULL, XPosition=5, IsToolbarButton=NULL,Updated=TO_DATE('2018-01-19 22:56:41','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=205304 +; + +-- Jan 19, 2018 10:56:41 PM CET +UPDATE AD_Field SET SeqNo=100, AD_Reference_Value_ID=NULL, AD_Val_Rule_ID=NULL, IsToolbarButton=NULL,Updated=TO_DATE('2018-01-19 22:56:41','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=203798 +; + +-- Jan 19, 2018 10:59:21 PM CET +UPDATE AD_Field SET DisplayLogic='@#DBType@=PostgreSQL', AD_Reference_Value_ID=NULL, AD_Val_Rule_ID=NULL, IsToolbarButton=NULL,Updated=TO_DATE('2018-01-19 22:59:21','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=205304 +; + +SELECT register_migration_script('201801192257_IDEMPIERE-1906.sql') FROM dual +; + diff --git a/migration/i5.1/postgresql/201801192257_IDEMPIERE-1906.sql b/migration/i5.1/postgresql/201801192257_IDEMPIERE-1906.sql new file mode 100644 index 0000000000..8575ad86e4 --- /dev/null +++ b/migration/i5.1/postgresql/201801192257_IDEMPIERE-1906.sql @@ -0,0 +1,32 @@ +-- IDEMPIERE-1906 Use postgresql SIMILAR TO instead of LIKE +-- Jan 19, 2018 10:55:51 PM CET +INSERT INTO AD_Element (AD_Element_ID,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,ColumnName,Name,Description,Help,PrintName,EntityType,AD_Element_UU) VALUES (203158,0,0,'Y',TO_TIMESTAMP('2018-01-19 22:54:41','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2018-01-19 22:54:41','YYYY-MM-DD HH24:MI:SS'),100,'IsUseSimilarTo','Use Similar To',NULL,'In PostgreSQL database enable using the more powerful SIMILAR TO instead of LIKE for matching queries.','Use Similar To','D','6917772a-f465-411c-95ed-8a834249a578') +; + +-- Jan 19, 2018 10:56:06 PM CET +INSERT INTO AD_Column (AD_Column_ID,Version,Name,Help,AD_Table_ID,ColumnName,DefaultValue,FieldLength,IsKey,IsParent,IsMandatory,IsTranslated,IsIdentifier,SeqNo,IsEncrypted,AD_Reference_ID,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,AD_Element_ID,IsUpdateable,IsSelectionColumn,EntityType,IsSyncDatabase,IsAlwaysUpdateable,IsAutocomplete,IsAllowLogging,AD_Column_UU,IsAllowCopy,SeqNoSelection,IsToolbarButton,IsSecure) VALUES (213340,0,'Use Similar To','In PostgreSQL database enable using the more powerful SIMILAR TO instead of LIKE for matching queries.',200174,'IsUseSimilarTo','N',1,'N','N','N','N','N',0,'N',20,0,0,'Y',TO_TIMESTAMP('2018-01-19 22:56:06','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2018-01-19 22:56:06','YYYY-MM-DD HH24:MI:SS'),100,203158,'Y','N','D','N','N','N','Y','81564050-9da4-492f-b298-cf8aac897583','Y',0,'N','N') +; + +-- Jan 19, 2018 10:56:07 PM CET +ALTER TABLE AD_UserPreference ADD COLUMN IsUseSimilarTo CHAR(1) DEFAULT 'N' CHECK (IsUseSimilarTo IN ('Y','N')) +; + +-- Jan 19, 2018 10:56:20 PM CET +INSERT INTO AD_Field (AD_Field_ID,Name,Help,AD_Tab_ID,AD_Column_ID,IsDisplayed,DisplayLength,SeqNo,IsSameLine,IsHeading,IsFieldOnly,IsEncrypted,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,IsReadOnly,IsCentrallyMaintained,EntityType,AD_Field_UU,IsDisplayedGrid,SeqNoGrid,XPosition,ColumnSpan) VALUES (205304,'Use Similar To','In PostgreSQL database enable using the more powerful SIMILAR TO instead of LIKE for matching queries.',200189,213340,'Y',1,100,'N','N','N','N',0,0,'Y',TO_TIMESTAMP('2018-01-19 22:56:20','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2018-01-19 22:56:20','YYYY-MM-DD HH24:MI:SS'),100,'N','Y','D','92dd69a0-5640-41d9-b26f-2546a71eb75b','Y',100,2,2) +; + +-- Jan 19, 2018 10:56:41 PM CET +UPDATE AD_Field SET IsDisplayed='Y', SeqNo=90, AD_Reference_Value_ID=NULL, AD_Val_Rule_ID=NULL, XPosition=5, IsToolbarButton=NULL,Updated=TO_TIMESTAMP('2018-01-19 22:56:41','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=205304 +; + +-- Jan 19, 2018 10:56:41 PM CET +UPDATE AD_Field SET SeqNo=100, AD_Reference_Value_ID=NULL, AD_Val_Rule_ID=NULL, IsToolbarButton=NULL,Updated=TO_TIMESTAMP('2018-01-19 22:56:41','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=203798 +; + +-- Jan 19, 2018 10:59:21 PM CET +UPDATE AD_Field SET DisplayLogic='@#DBType@=PostgreSQL', AD_Reference_Value_ID=NULL, AD_Val_Rule_ID=NULL, IsToolbarButton=NULL,Updated=TO_TIMESTAMP('2018-01-19 22:59:21','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=205304 +; + +SELECT register_migration_script('201801192257_IDEMPIERE-1906.sql') FROM dual +; + diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/AdempiereWebUI.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/AdempiereWebUI.java index 725ba183b4..1d135c15f3 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/AdempiereWebUI.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/AdempiereWebUI.java @@ -50,6 +50,7 @@ import org.compiere.model.MTable; import org.compiere.model.MUser; import org.compiere.model.MUserPreference; import org.compiere.util.CLogger; +import org.compiere.util.DB; import org.compiere.util.Env; import org.compiere.util.Language; import org.compiere.util.Msg; @@ -273,6 +274,7 @@ public class AdempiereWebUI extends Window implements EventListener, IWeb BrowserToken.save(mSession, user); Env.setContext(ctx, "#UIClient", "zk"); + Env.setContext(ctx, "#DBType", DB.getDatabase().getName()); StringBuilder localHttpAddr = new StringBuilder(Executions.getCurrent().getScheme()); localHttpAddr.append("://").append(Executions.getCurrent().getLocalAddr()); int port = Executions.getCurrent().getLocalPort(); diff --git a/org.compiere.db.postgresql.provider/src/org/compiere/dbPort/Convert_PostgreSQL.java b/org.compiere.db.postgresql.provider/src/org/compiere/dbPort/Convert_PostgreSQL.java index 1615d19644..aa6546b81b 100644 --- a/org.compiere.db.postgresql.provider/src/org/compiere/dbPort/Convert_PostgreSQL.java +++ b/org.compiere.db.postgresql.provider/src/org/compiere/dbPort/Convert_PostgreSQL.java @@ -24,6 +24,7 @@ import java.util.regex.Pattern; import org.compiere.db.DB_PostgreSQL; import org.compiere.util.CLogger; +import org.compiere.util.Env; import org.compiere.util.Util; /** @@ -35,7 +36,7 @@ import org.compiere.util.Util; */ public class Convert_PostgreSQL extends Convert_SQL92 { /** - * Cosntructor + * Constructor */ public Convert_PostgreSQL() { m_map = ConvertMap_PostgreSQL.getConvertMap(); @@ -79,6 +80,7 @@ public class Convert_PostgreSQL extends Convert_SQL92 { String statement = replaceQuotedStrings(sqlStatement, retVars); statement = convertWithConvertMap(statement); + statement = convertSimilarTo(statement); statement = statement.replace(DB_PostgreSQL.NATIVE_MARKER, ""); String cmpString = statement.toUpperCase(); @@ -122,6 +124,25 @@ public class Convert_PostgreSQL extends Convert_SQL92 { return result; } // convertStatement + private String convertSimilarTo(String statement) { + String retValue = statement; + boolean useSimilarTo = "Y".equals(Env.getContext(Env.getCtx(), "P|IsUseSimilarTo")); + if (useSimilarTo) { + String regex = "\\bLIKE\\b"; + String replacement = "SIMILAR TO"; + try { + Pattern p = Pattern.compile(regex, REGEX_FLAGS); + Matcher m = p.matcher(retValue); + retValue = m.replaceAll(replacement); + } catch (Exception e) { + String error = "Error expression: " + regex + " - " + e; + log.info(error); + m_conversionError = error; + } + } + return retValue; + } + @Override protected String escapeQuotedString(String in) { From 470b3d45cc38fa33225c6a0aae1d1c41701a8f57 Mon Sep 17 00:00:00 2001 From: Carlos Ruiz Date: Sat, 20 Jan 2018 10:03:37 +0100 Subject: [PATCH 3/7] IDEMPIERE-3580 Advanced Zoom Across --- .../src/org/adempiere/model/GenericZoomProvider.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/org.adempiere.base/src/org/adempiere/model/GenericZoomProvider.java b/org.adempiere.base/src/org/adempiere/model/GenericZoomProvider.java index 8f5c1fb486..80d8c808b3 100644 --- a/org.adempiere.base/src/org/adempiere/model/GenericZoomProvider.java +++ b/org.adempiere.base/src/org/adempiere/model/GenericZoomProvider.java @@ -87,7 +87,7 @@ public class GenericZoomProvider implements IZoomProvider { + "JOIN AD_Window_Trl w ON (w.AD_Window_ID=w0.AD_Window_ID AND w.AD_Language=?) " + "JOIN AD_Column c ON (t.AD_Table_ID=c.AD_Table_ID AND c.IsActive='Y' AND c.IsKey='N' AND c.IsParent='N' AND c.ColumnSQL IS NULL) " + "JOIN AD_Field f0 ON (f0.AD_Column_ID=c.AD_Column_ID AND f0.AD_Tab_ID=tt0.AD_Tab_ID AND f0.IsActive='Y' AND f0.IsDisplayed='Y') " - + "JOIN AD_Field_Trl f ON (f.AD_Field_ID=f0.AD_Field_ID) "); + + "JOIN AD_Field_Trl f ON (f.AD_Field_ID=f0.AD_Field_ID AND f.AD_Language=?) "); } sqlb.append( "LEFT JOIN AD_Ref_Table r ON (c.AD_Reference_Value_ID=r.AD_Reference_ID) " @@ -114,6 +114,7 @@ public class GenericZoomProvider implements IZoomProvider { if (!baseLanguage) { pstmt.setString(index++, Env.getAD_Language(Env.getCtx())); pstmt.setString(index++, Env.getAD_Language(Env.getCtx())); + pstmt.setString(index++, Env.getAD_Language(Env.getCtx())); } pstmt.setString(index++, po.get_KeyColumns()[0]); if (detailedZoom) { From 339f171f654cd14e6e34ad32805ed706a7097aa3 Mon Sep 17 00:00:00 2001 From: Carlos Ruiz Date: Mon, 22 Jan 2018 19:28:28 +0100 Subject: [PATCH 4/7] IDEMPIERE-3099 AP Credit Memo, Rule: Cash Generate Payment with wrong amount --- org.adempiere.base/src/org/compiere/model/MInvoice.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/org.adempiere.base/src/org/compiere/model/MInvoice.java b/org.adempiere.base/src/org/compiere/model/MInvoice.java index 5f4f3d34c5..86b65aa3e9 100644 --- a/org.adempiere.base/src/org/compiere/model/MInvoice.java +++ b/org.adempiere.base/src/org/compiere/model/MInvoice.java @@ -1778,7 +1778,10 @@ public class MInvoice extends X_C_Invoice implements DocAction payment.setC_Invoice_ID(getC_Invoice_ID()); payment.setC_Currency_ID(getC_Currency_ID()); payment.setC_DocType_ID(doctype.getC_DocType_ID()); - payment.setPayAmt(getGrandTotal()); + if (isCreditMemo()) + payment.setPayAmt(getGrandTotal().negate()); + else + payment.setPayAmt(getGrandTotal()); payment.setIsPrepayment(false); payment.setDateAcct(getDateAcct()); payment.setDateTrx(getDateInvoiced()); From 0636192a5e54422c2febb3df93e29aada54d2093 Mon Sep 17 00:00:00 2001 From: Carlos Ruiz Date: Tue, 23 Jan 2018 00:59:28 +0100 Subject: [PATCH 5/7] IDEMPIERE-3628 Wrong invoice reversal for a price changed from zero to non-zero --- .../src/org/compiere/model/MInvoice.java | 22 ++++++++++++------- .../src/org/compiere/model/MInvoiceLine.java | 7 +++++- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/org.adempiere.base/src/org/compiere/model/MInvoice.java b/org.adempiere.base/src/org/compiere/model/MInvoice.java index 86b65aa3e9..8fe61c279f 100644 --- a/org.adempiere.base/src/org/compiere/model/MInvoice.java +++ b/org.adempiere.base/src/org/compiere/model/MInvoice.java @@ -679,7 +679,7 @@ public class MInvoice extends X_C_Invoice implements DocAction whereClauseFinal += whereClause; List list = new Query(getCtx(), I_C_InvoiceLine.Table_Name, whereClauseFinal, get_TrxName()) .setParameters(getC_Invoice_ID()) - .setOrderBy(I_C_InvoiceLine.COLUMNNAME_Line) + .setOrderBy("Line, C_InvoiceLine_ID") .list(); return list.toArray(new MInvoiceLine[list.size()]); } // getLines @@ -2450,17 +2450,23 @@ public class MInvoice extends X_C_Invoice implements DocAction reversal.setReversal(true); // Reverse Line Qty + MInvoiceLine[] oLines = getLines(false); MInvoiceLine[] rLines = reversal.getLines(true); for (int i = 0; i < rLines.length; i++) { MInvoiceLine rLine = rLines[i]; - rLine.setQtyEntered(rLine.getQtyEntered().negate()); - rLine.setQtyInvoiced(rLine.getQtyInvoiced().negate()); - rLine.setLineNetAmt(rLine.getLineNetAmt().negate()); - if (rLine.getTaxAmt() != null && rLine.getTaxAmt().compareTo(Env.ZERO) != 0) - rLine.setTaxAmt(rLine.getTaxAmt().negate()); - if (rLine.getLineTotalAmt() != null && rLine.getLineTotalAmt().compareTo(Env.ZERO) != 0) - rLine.setLineTotalAmt(rLine.getLineTotalAmt().negate()); + rLine.getParent().setReversal(true); + MInvoiceLine oLine = oLines[i]; + rLine.setQtyEntered(oLine.getQtyEntered().negate()); + rLine.setQtyInvoiced(oLine.getQtyInvoiced().negate()); + rLine.setLineNetAmt(oLine.getLineNetAmt().negate()); + rLine.setTaxAmt(oLine.getTaxAmt().negate()); + rLine.setLineTotalAmt(oLine.getLineTotalAmt().negate()); + rLine.setPriceActual(oLine.getPriceActual()); + rLine.setPriceList(oLine.getPriceList()); + rLine.setPriceLimit(oLine.getPriceLimit()); + rLine.setPriceEntered(oLine.getPriceEntered()); + rLine.setC_UOM_ID(oLine.getC_UOM_ID()); if (!rLine.save(get_TrxName())) { m_processMsg = "Could not correct Invoice Reversal Line"; diff --git a/org.adempiere.base/src/org/compiere/model/MInvoiceLine.java b/org.adempiere.base/src/org/compiere/model/MInvoiceLine.java index 272995eb03..3582e9d1e5 100644 --- a/org.adempiere.base/src/org/compiere/model/MInvoiceLine.java +++ b/org.adempiere.base/src/org/compiere/model/MInvoiceLine.java @@ -842,12 +842,16 @@ public class MInvoiceLine extends X_C_InvoiceLine protected boolean beforeSave (boolean newRecord) { if (log.isLoggable(Level.FINE)) log.fine("New=" + newRecord); - if (newRecord && getParent().isComplete()) { + boolean parentComplete = getParent().isComplete(); + boolean isReversal = getParent().isReversal(); + if (newRecord && parentComplete) { log.saveError("ParentComplete", Msg.translate(getCtx(), "C_InvoiceLine")); return false; } // Re-set invoice header (need to update m_IsSOTrx flag) - phib [ 1686773 ] setInvoice(getParent()); + + if (!parentComplete && !isReversal) { // do not change things when parent is complete // Charge if (getC_Charge_ID() != 0) { @@ -916,6 +920,7 @@ public class MInvoiceLine extends X_C_InvoiceLine return false; } } + } return true; } // beforeSave From 0e6975f59c5b6fc69bb29fc4e39d0cabefffdf0f Mon Sep 17 00:00:00 2001 From: Heng Sin Low Date: Mon, 22 Jan 2018 22:36:33 +0800 Subject: [PATCH 6/7] IDEMPIERE-3627 Field and Process Parameter: @SQL support for Readonly logic --- .../src/org/compiere/model/GridField.java | 81 +++++++++++++++---- 1 file changed, 66 insertions(+), 15 deletions(-) diff --git a/org.adempiere.base/src/org/compiere/model/GridField.java b/org.adempiere.base/src/org/compiere/model/GridField.java index 1e9eb87d40..0d9aa0defb 100644 --- a/org.adempiere.base/src/org/compiere/model/GridField.java +++ b/org.adempiere.base/src/org/compiere/model/GridField.java @@ -30,7 +30,9 @@ import java.util.ArrayList; import java.util.Calendar; import java.util.Collections; import java.util.Date; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Properties; import java.util.StringTokenizer; import java.util.logging.Level; @@ -353,7 +355,7 @@ public class GridField { boolean retValue = false; if (m_vo.MandatoryLogic != null && m_vo.MandatoryLogic.startsWith("@SQL=")) { - retValue = mandatorySqlStatement(); + retValue = parseSQLLogic(m_vo.MandatoryLogic); } else{ retValue= Evaluator.evaluateLogic(this, m_vo.MandatoryLogic); @@ -385,21 +387,46 @@ public class GridField return isDisplayed (checkContext); } // isMandatory - private boolean mandatorySqlStatement() { - String sql = m_vo.MandatoryLogic.substring(5); // w/o tag - sql = Env.parseContext(m_vo.ctx, m_vo.WindowNo, sql, false, false); // replace + private boolean parseSQLLogic(String sqlLogic) { + String sql = sqlLogic.substring(5); // remove @SQL= + boolean reverse = false; + if (sql.startsWith("!")) { + reverse = true; + sql = sql.substring(1); //remove ! + } + sql = Env.parseContext(m_vo.ctx, m_vo.WindowNo, m_vo.TabNo, sql, false, false); // replace // variables if (sql.equals("")) { - log.log(Level.WARNING,"(" + m_vo.ColumnName + ") - Mandatory SQL variable parse failed: " + m_vo.MandatoryLogic); + log.log(Level.WARNING,"(" + m_vo.ColumnName + ") - SQL variable parse failed: " + sqlLogic); } else { + SQLLogicResult cache = sqlLogicCache.get(sql); + if (cache != null) { + long since = System.currentTimeMillis() - cache.timestamp; + if (since <= 500) { + cache.timestamp = System.currentTimeMillis(); + if (cache.value) + return reverse ? false : true; + else + return reverse ? true : false; + } + } PreparedStatement stmt = null; ResultSet rs = null; try { stmt = DB.prepareStatement(sql, null); rs = stmt.executeQuery(); - if (rs.next()) - return true; + boolean hasNext = rs.next(); + if (cache == null) { + cache = new SQLLogicResult(); + sqlLogicCache.put(sql, cache); + } + cache.value = hasNext; + cache.timestamp = System.currentTimeMillis(); + if (hasNext) + return reverse ? false : true; + else + return reverse ? true : false; } catch (SQLException e) { log.log(Level.WARNING, "(" + m_vo.ColumnName + ") " + sql, e); } finally { @@ -419,10 +446,19 @@ public class GridField public boolean isEditablePara(boolean checkContext) { if (checkContext && m_vo.ReadOnlyLogic.length() > 0) { - boolean retValue = !Evaluator.evaluateLogic(this, m_vo.ReadOnlyLogic); - if (log.isLoggable(Level.FINEST)) log.finest(m_vo.ColumnName + " R/O(" + m_vo.ReadOnlyLogic + ") => R/W-" + retValue); - if (!retValue) - return false; + if (m_vo.ReadOnlyLogic.startsWith("@SQL=")) + { + boolean retValue = !parseSQLLogic(m_vo.ReadOnlyLogic); + if (!retValue) + return false; + } + else + { + boolean retValue = !Evaluator.evaluateLogic(this, m_vo.ReadOnlyLogic); + if (log.isLoggable(Level.FINEST)) log.finest(m_vo.ColumnName + " R/O(" + m_vo.ReadOnlyLogic + ") => R/W-" + retValue); + if (!retValue) + return false; + } } // ultimately visibility decides @@ -522,10 +558,19 @@ public class GridField // Do we have a readonly rule if (checkContext && m_vo.ReadOnlyLogic.length() > 0) { - boolean retValue = !Evaluator.evaluateLogic(this, m_vo.ReadOnlyLogic); - if (log.isLoggable(Level.FINEST)) log.finest(m_vo.ColumnName + " R/O(" + m_vo.ReadOnlyLogic + ") => R/W-" + retValue); - if (!retValue) - return false; + if (m_vo.ReadOnlyLogic.startsWith("@SQL=")) + { + boolean retValue = !parseSQLLogic(m_vo.ReadOnlyLogic); + if (!retValue) + return false; + } + else + { + boolean retValue = !Evaluator.evaluateLogic(this, m_vo.ReadOnlyLogic); + if (log.isLoggable(Level.FINEST)) log.finest(m_vo.ColumnName + " R/O(" + m_vo.ReadOnlyLogic + ") => R/W-" + retValue); + if (!retValue) + return false; + } } //BF [ 2910368 ] @@ -2516,4 +2561,10 @@ public class GridField return m_lookupEditorSettingValue; } + private final Map sqlLogicCache = new HashMap<>(); + + private class SQLLogicResult { + long timestamp; + boolean value; + } } // GridField From 44ef536af6bfa5865a569a278ddeea5e9df215a3 Mon Sep 17 00:00:00 2001 From: Carlos Ruiz Date: Tue, 23 Jan 2018 11:55:52 +0100 Subject: [PATCH 7/7] IDEMPIERE-3627 Field and Process Parameter: @SQL support for Readonly logic --- org.adempiere.base/src/org/compiere/model/GridField.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/org.adempiere.base/src/org/compiere/model/GridField.java b/org.adempiere.base/src/org/compiere/model/GridField.java index 0d9aa0defb..7c9d484c70 100644 --- a/org.adempiere.base/src/org/compiere/model/GridField.java +++ b/org.adempiere.base/src/org/compiere/model/GridField.java @@ -30,11 +30,11 @@ import java.util.ArrayList; import java.util.Calendar; import java.util.Collections; import java.util.Date; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.StringTokenizer; +import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Level; import org.adempiere.base.ILookupFactory; @@ -2561,7 +2561,7 @@ public class GridField return m_lookupEditorSettingValue; } - private final Map sqlLogicCache = new HashMap<>(); + private static final Map sqlLogicCache = new ConcurrentHashMap<>(); private class SQLLogicResult { long timestamp;