From 184e8191e65deefbdcc8b535280e1801da54b028 Mon Sep 17 00:00:00 2001 From: hengsin Date: Thu, 12 Aug 2021 21:02:01 +0800 Subject: [PATCH] IDEMPIERE-4904 Add custom query clause support to report parameter (#821) --- .../oracle/202108091122_IDEMPIERE-4904.sql | 18 ++ .../202108091122_IDEMPIERE-4904.sql | 15 ++ .../org/compiere/model/I_AD_Process_Para.java | 13 ++ .../src/org/compiere/model/MQuery.java | 167 +++++++++++++++++- .../org/compiere/model/X_AD_Process_Para.java | 21 ++- 5 files changed, 228 insertions(+), 6 deletions(-) create mode 100644 migration/i8.2z/oracle/202108091122_IDEMPIERE-4904.sql create mode 100644 migration/i8.2z/postgresql/202108091122_IDEMPIERE-4904.sql diff --git a/migration/i8.2z/oracle/202108091122_IDEMPIERE-4904.sql b/migration/i8.2z/oracle/202108091122_IDEMPIERE-4904.sql new file mode 100644 index 0000000000..5a136154a9 --- /dev/null +++ b/migration/i8.2z/oracle/202108091122_IDEMPIERE-4904.sql @@ -0,0 +1,18 @@ +SET SQLBLANKLINES ON +SET DEFINE OFF + +-- Aug 9, 2021 12:54:11 PM GMT+08:00 +-- IDEMPIERE-4904 Add custom query clause support to report parameter +INSERT INTO AD_Column (AD_Column_ID,SeqNoSelection,IsSyncDatabase,Version,IsMandatory,IsTranslated,IsIdentifier,SeqNo,IsParent,FieldLength,IsSelectionColumn,IsKey,IsAutocomplete,IsAllowLogging,MandatoryLogic,AD_Column_UU,Updated,IsUpdateable,ColumnName,Description,Name,IsAllowCopy,IsActive,CreatedBy,UpdatedBy,IsAlwaysUpdateable,AD_Client_ID,AD_Org_ID,Created,EntityType,IsEncrypted,IsSecure,AD_Element_ID,AD_Reference_ID,AD_Table_ID,IsToolbarButton) VALUES (213185,0,'N',0,'N','N','N',0,'N',2000,'N','N','N','Y',NULL,'04f9c29a-7806-444f-a313-45204f9efa84',TO_DATE('2021-08-09 12:54:09','YYYY-MM-DD HH24:MI:SS'),'Y','Query','SQL','Query','N','Y',100,100,'N',0,0,TO_DATE('2021-08-09 12:54:09','YYYY-MM-DD HH24:MI:SS'),'D','N','N',53775,14,285,'N') +; + +-- Aug 9, 2021 12:54:23 PM GMT+08:00 +ALTER TABLE AD_Process_Para ADD Query VARCHAR2(2000 CHAR) DEFAULT NULL +; + +-- Aug 9, 2021 12:57:18 PM GMT+08:00 +INSERT INTO AD_Field (SortNo,AD_Field_ID,IsEncrypted,DisplayLength,IsSameLine,IsHeading,SeqNo,IsCentrallyMaintained,IsReadOnly,DisplayLogic,AD_Org_ID,Updated,Description,Name,AD_Field_UU,IsDisplayed,IsFieldOnly,CreatedBy,UpdatedBy,IsActive,IsDisplayedGrid,SeqNoGrid,XPosition,IsQuickEntry,AD_Client_ID,Created,ColumnSpan,NumLines,IsAdvancedField,IsDefaultFocus,AD_Column_ID,EntityType,AD_Tab_ID) VALUES (0,205193,'N',0,'N','N',270,'Y','N','@IsReport@=Y',0,TO_DATE('2021-08-09 12:57:17','YYYY-MM-DD HH24:MI:SS'),'SQL','Query','ed208c1d-a58a-43d5-912f-af8294e5d276','Y','N',100,100,'Y','Y',270,1,'N',0,TO_DATE('2021-08-09 12:57:17','YYYY-MM-DD HH24:MI:SS'),5,3,'N','N',213185,'D',246) +; + +SELECT register_migration_script('202108091122_IDEMPIERE-4904.sql') FROM dual +; diff --git a/migration/i8.2z/postgresql/202108091122_IDEMPIERE-4904.sql b/migration/i8.2z/postgresql/202108091122_IDEMPIERE-4904.sql new file mode 100644 index 0000000000..4483a865b4 --- /dev/null +++ b/migration/i8.2z/postgresql/202108091122_IDEMPIERE-4904.sql @@ -0,0 +1,15 @@ +-- Aug 9, 2021 12:54:11 PM GMT+08:00 +-- IDEMPIERE-4904 Add custom query clause support to report parameter +INSERT INTO AD_Column (AD_Column_ID,SeqNoSelection,IsSyncDatabase,Version,IsMandatory,IsTranslated,IsIdentifier,SeqNo,IsParent,FieldLength,IsSelectionColumn,IsKey,IsAutocomplete,IsAllowLogging,MandatoryLogic,AD_Column_UU,Updated,IsUpdateable,ColumnName,Description,Name,IsAllowCopy,IsActive,CreatedBy,UpdatedBy,IsAlwaysUpdateable,AD_Client_ID,AD_Org_ID,Created,EntityType,IsEncrypted,IsSecure,AD_Element_ID,AD_Reference_ID,AD_Table_ID,IsToolbarButton) VALUES (213185,0,'N',0,'N','N','N',0,'N',2000,'N','N','N','Y',NULL,'04f9c29a-7806-444f-a313-45204f9efa84',TO_TIMESTAMP('2021-08-09 12:54:09','YYYY-MM-DD HH24:MI:SS'),'Y','Query','SQL','Query','N','Y',100,100,'N',0,0,TO_TIMESTAMP('2021-08-09 12:54:09','YYYY-MM-DD HH24:MI:SS'),'D','N','N',53775,14,285,'N') +; + +-- Aug 9, 2021 12:54:23 PM GMT+08:00 +ALTER TABLE AD_Process_Para ADD COLUMN Query VARCHAR(2000) DEFAULT NULL +; + +-- Aug 9, 2021 12:57:18 PM GMT+08:00 +INSERT INTO AD_Field (SortNo,AD_Field_ID,IsEncrypted,DisplayLength,IsSameLine,IsHeading,SeqNo,IsCentrallyMaintained,IsReadOnly,DisplayLogic,AD_Org_ID,Updated,Description,Name,AD_Field_UU,IsDisplayed,IsFieldOnly,CreatedBy,UpdatedBy,IsActive,IsDisplayedGrid,SeqNoGrid,XPosition,IsQuickEntry,AD_Client_ID,Created,ColumnSpan,NumLines,IsAdvancedField,IsDefaultFocus,AD_Column_ID,EntityType,AD_Tab_ID) VALUES (0,205193,'N',0,'N','N',270,'Y','N','@IsReport@=Y',0,TO_TIMESTAMP('2021-08-09 12:57:17','YYYY-MM-DD HH24:MI:SS'),'SQL','Query','ed208c1d-a58a-43d5-912f-af8294e5d276','Y','N',100,100,'Y','Y',270,1,'N',0,TO_TIMESTAMP('2021-08-09 12:57:17','YYYY-MM-DD HH24:MI:SS'),5,3,'N','N',213185,'D',246) +; + +SELECT register_migration_script('202108091122_IDEMPIERE-4904.sql') FROM dual +; diff --git a/org.adempiere.base/src/org/compiere/model/I_AD_Process_Para.java b/org.adempiere.base/src/org/compiere/model/I_AD_Process_Para.java index 3dae66be02..b0e9b7ab10 100644 --- a/org.adempiere.base/src/org/compiere/model/I_AD_Process_Para.java +++ b/org.adempiere.base/src/org/compiere/model/I_AD_Process_Para.java @@ -414,6 +414,19 @@ public interface I_AD_Process_Para /** Get Placeholder2 */ public String getPlaceholder2(); + /** Column name Query */ + public static final String COLUMNNAME_Query = "Query"; + + /** Set Query. + * SQL + */ + public void setQuery (String Query); + + /** Get Query. + * SQL + */ + public String getQuery(); + /** Column name ReadOnlyLogic */ public static final String COLUMNNAME_ReadOnlyLogic = "ReadOnlyLogic"; diff --git a/org.adempiere.base/src/org/compiere/model/MQuery.java b/org.adempiere.base/src/org/compiere/model/MQuery.java index f11c51cda9..f4098f2fe0 100644 --- a/org.adempiere.base/src/org/compiere/model/MQuery.java +++ b/org.adempiere.base/src/org/compiere/model/MQuery.java @@ -21,7 +21,11 @@ import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Timestamp; +import java.text.MessageFormat; 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; @@ -29,8 +33,10 @@ import org.compiere.util.CLogger; import org.compiere.util.DB; import org.compiere.util.DisplayType; import org.compiere.util.Env; +import org.compiere.util.Evaluatee; import org.compiere.util.KeyNamePair; import org.compiere.util.Msg; +import org.compiere.util.Util; import org.compiere.util.ValueNamePair; /** @@ -79,12 +85,14 @@ public class MQuery implements Serializable, Cloneable if (rows < 1) return reportQuery; + Map parameterMap = new HashMap<>(); + List queryList = new ArrayList<>(); boolean trl = !Env.isBaseLanguage(ctx, "AD_Process_Para"); if (!trl) SQL = "SELECT ip.ParameterName,ip.P_String,ip.P_String_To," // 1..3 + "ip.P_Number,ip.P_Number_To," // 4..5 + "ip.P_Date,ip.P_Date_To, ip.Info,ip.Info_To, " // 6..9 - + "pp.Name, pp.IsRange, pp.AD_Reference_ID " // 10..12 + + "pp.Name, pp.IsRange, pp.AD_Reference_ID, pp.Query " // 10..13 + "FROM AD_PInstance_Para ip, AD_PInstance i, AD_Process_Para pp " + "WHERE i.AD_PInstance_ID=ip.AD_PInstance_ID" + " AND pp.AD_Process_ID=i.AD_Process_ID" @@ -95,7 +103,7 @@ public class MQuery implements Serializable, Cloneable else SQL = "SELECT ip.ParameterName,ip.P_String,ip.P_String_To, ip.P_Number,ip.P_Number_To," + "ip.P_Date,ip.P_Date_To, ip.Info,ip.Info_To, " - + "ppt.Name, pp.IsRange, pp.AD_Reference_ID " + + "ppt.Name, pp.IsRange, pp.AD_Reference_ID, pp.Query " + "FROM AD_PInstance_Para ip, AD_PInstance i, AD_Process_Para pp, AD_Process_Para_Trl ppt " + "WHERE i.AD_PInstance_ID=ip.AD_PInstance_ID" + " AND pp.AD_Process_ID=i.AD_Process_ID" @@ -147,13 +155,14 @@ public class MQuery implements Serializable, Cloneable boolean isRange = "Y".equals(rs.getString(11)); // int Reference_ID = rs.getInt(12); + String P_Query = rs.getString(13); // if (s_log.isLoggable(Level.FINE)) s_log.fine(ParameterName + " S=" + P_String + "-" + P_String_To + ", N=" + P_Number + "-" + P_Number_To + ", D=" + P_Date + "-" + P_Date_To + "; Name=" + Name + ", Info=" + Info + "-" + Info_To + ", Range=" + isRange); // //custom query or column not exists - render as report parameters - if (table != null && table.getColumn(ParameterName) == null) + if (!Util.isEmpty(P_Query) || (table != null && table.getColumn(ParameterName) == null)) { query = reportQuery.getReportProcessQuery(); } @@ -161,6 +170,7 @@ public class MQuery implements Serializable, Cloneable //------------------------------------------------------------- if (P_String != null) { + parameterMap.put(ParameterName, P_String); if (P_String_To == null) { if (Reference_ID == DisplayType.ChosenMultipleSelectionList) @@ -191,14 +201,18 @@ public class MQuery implements Serializable, Cloneable } } else + { query.addRangeRestriction(ParameterName, P_String, P_String_To, Name, Info, Info_To); + parameterMap.put("To_"+ParameterName, P_String_To); + } } // Number else if (P_Number != null || P_Number_To != null) { if (P_Number_To == null) { + parameterMap.put(ParameterName, P_Number.toString()); if (isRange) query.addRestriction(ParameterName, MQuery.GREATER_EQUAL, P_Number, Name, Info); @@ -208,12 +222,16 @@ public class MQuery implements Serializable, Cloneable } else // P_Number_To != null { - if (P_Number == null) + parameterMap.put("To_"+ParameterName, P_Number_To.toString()); + if (P_Number == null) query.addRestriction(ParameterName, MQuery.LESS_EQUAL, P_Number_To, Name, Info); else + { query.addRangeRestriction(ParameterName, P_Number, P_Number_To, Name, Info, Info_To); + parameterMap.put(ParameterName, P_Number.toString()); + } } } // Date @@ -224,6 +242,7 @@ public class MQuery implements Serializable, Cloneable if (P_Date_To == null) { + parameterMap.put(ParameterName, DisplayType.getDateFormat().format(P_Date)); if (isRange) query.addRestriction(paramName, MQuery.GREATER_EQUAL, P_Date, Name, Info); else @@ -231,12 +250,21 @@ public class MQuery implements Serializable, Cloneable } else // P_Date_To != null { + parameterMap.put("To_"+ParameterName, DisplayType.getDateFormat().format(P_Date_To)); if (P_Date == null) query.addRestriction(paramName, MQuery.LESS_EQUAL, P_Date_To, Name, Info); else + { query.addRangeRestriction(paramName, P_Date, P_Date_To, Name, Info, Info_To); + parameterMap.put(ParameterName, DisplayType.getDateFormat().format(P_Date)); + } } } + + //keep custom query for later context parsing + if (!Util.isEmpty(P_Query) && (parameterMap.containsKey(ParameterName) || parameterMap.containsKey("To_"+ParameterName))) + queryList.add(P_Query); + //add to reportprocessquery if new restriction added to reportquery if (query == reportQuery && reportQuery.getReportProcessQuery() != null && reportQuery.getRestrictionCount() > restrictionCount) @@ -254,10 +282,76 @@ public class MQuery implements Serializable, Cloneable DB.close(rs, pstmt); rs = null; pstmt = null; } + + //add custom query + if (queryList.size() > 0) + { + QueryEvaluatee evaluatee= new QueryEvaluatee(parameterMap); + for(String query : queryList) + { + if (query.indexOf("@") >= 0) + { + query = parseVariable(evaluatee, query, false); + reportQuery.addRestriction(query); + } + } + } + if (s_log.isLoggable(Level.INFO)) s_log.info(reportQuery.toString()); return reportQuery; } // get + private static String parseVariable(Evaluatee evaluatee, String expression, boolean ignoreUnparseable) { + if (expression == null || expression.length() == 0) + return ""; + + String token; + String inStr = new String(expression); + StringBuilder outStr = new StringBuilder(); + + int i = inStr.indexOf('@'); + while (i != -1) + { + outStr.append(inStr.substring(0, i)); // up to @ + inStr = inStr.substring(i+1, inStr.length()); // from first @ + + int j = inStr.indexOf('@'); // next @ + if (j < 0) + { + s_log.log(Level.SEVERE, "No second tag: " + inStr); + return ""; // no second tag + } + + token = inStr.substring(0, j); + + //format string + String format = ""; + int f = token.indexOf('<'); + if (f > 0 && token.endsWith(">")) { + format = token.substring(f+1, token.length()-1); + token = token.substring(0, f); + } + + String v = evaluatee.get_ValueAsString(token); + if (!Util.isEmpty(v)) { + if (format != null && format.length() > 0) { + MessageFormat mf = new MessageFormat(format); + outStr.append(mf.format(v)); + } else { + outStr.append(v.toString()); + } + } else if (!ignoreUnparseable) { + return ""; + } + + inStr = inStr.substring(j+1, inStr.length()); // from second @ + i = inStr.indexOf('@'); + } + outStr.append(inStr); // add the rest of the string + + return outStr.toString(); + } + /** * Get Zoom Column Name. * Converts Synonyms like SalesRep_ID to AD_User_ID @@ -1350,3 +1444,68 @@ class Restriction implements Serializable } // getInfoDisplay } // Restriction + +class QueryEvaluatee implements Evaluatee { + private Map parameterMap; + + public QueryEvaluatee(Map parameterMap) { + this.parameterMap = parameterMap; + } + + /** + * Get Variable Value (Evaluatee) + * @param variableName name + * @return value + */ + public String get_ValueAsString (Properties ctx, String variableName) + { + //ref column + String foreignColumn = ""; + int f = variableName.indexOf('.'); + if (f > 0) { + foreignColumn = variableName.substring(f+1, variableName.length()); + variableName = variableName.substring(0, f); + } + + String value = null; + if (variableName.startsWith("#") || variableName.startsWith("$")) { + value = Env.getContext(ctx, variableName); + } else { + value = parameterMap.get(variableName); + } + if (!Util.isEmpty(value) && !Util.isEmpty(foreignColumn) && variableName.endsWith("_ID")) { + String refValue = ""; + int id = 0; + try { + id = Integer.parseInt(value); + } catch (Exception e){} + if (id > 0) { + if (variableName.startsWith("#") || variableName.startsWith("$")) { + variableName = variableName.substring(1); + } else if (variableName.indexOf("|") > 0) { + variableName = variableName.substring(variableName.lastIndexOf("|")+1); + } + String foreignTable = null; + if (foreignColumn.indexOf(".") > 0) { + foreignTable = foreignColumn.substring(0, foreignColumn.indexOf(".")); + } else { + foreignTable = variableName.substring(0, variableName.length()-3); + } + MTable t = MTable.get(Env.getCtx(), foreignTable); + if (t != null) { + refValue = DB.getSQLValueString(null, + "SELECT " + foreignColumn + " FROM " + foreignTable + " WHERE " + + foreignTable + "_ID = ?", id); + } + } + return refValue; + } + return value; + } // get_ValueAsString + + @Override + public String get_ValueAsString(String variableName) { + return get_ValueAsString(Env.getCtx(), variableName); + } + +} diff --git a/org.adempiere.base/src/org/compiere/model/X_AD_Process_Para.java b/org.adempiere.base/src/org/compiere/model/X_AD_Process_Para.java index 749866a61a..b05af647c5 100644 --- a/org.adempiere.base/src/org/compiere/model/X_AD_Process_Para.java +++ b/org.adempiere.base/src/org/compiere/model/X_AD_Process_Para.java @@ -30,7 +30,7 @@ public class X_AD_Process_Para extends PO implements I_AD_Process_Para, I_Persis /** * */ - private static final long serialVersionUID = 20210723L; + private static final long serialVersionUID = 20210809L; /** Standard Constructor */ public X_AD_Process_Para (Properties ctx, int AD_Process_Para_ID, String trxName) @@ -43,7 +43,7 @@ public class X_AD_Process_Para extends PO implements I_AD_Process_Para, I_Persis setAD_Reference_ID (0); setColumnName (null); setEntityType (null); -// @SQL=select get_sysconfig('DEFAULT_ENTITYTYPE','U',0,0) from dual +// @SQL=SELECT CASE WHEN '@P|AdempiereSys:N@'='Y' THEN 'D' ELSE get_sysconfig('DEFAULT_ENTITYTYPE','U',0,0) END FROM Dual setFieldLength (0); setIsAutocomplete (false); // N @@ -618,6 +618,23 @@ public class X_AD_Process_Para extends PO implements I_AD_Process_Para, I_Persis return (String)get_Value(COLUMNNAME_Placeholder2); } + /** Set Query. + @param Query + SQL + */ + public void setQuery (String Query) + { + set_Value (COLUMNNAME_Query, Query); + } + + /** Get Query. + @return SQL + */ + public String getQuery () + { + return (String)get_Value(COLUMNNAME_Query); + } + /** Set Read Only Logic. @param ReadOnlyLogic Logic to determine if field is read only (applies only when field is read-write)