diff --git a/base/src/org/compiere/model/Query.java b/base/src/org/compiere/model/Query.java
index 6ac2d34099..563ab2da43 100644
--- a/base/src/org/compiere/model/Query.java
+++ b/base/src/org/compiere/model/Query.java
@@ -19,6 +19,7 @@
******************************************************************************/
package org.compiere.model;
+import java.math.BigDecimal;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
@@ -39,19 +40,26 @@ import org.compiere.util.Util;
/**
*
* @author Low Heng Sin
- * @author Teo Sarca, SC ARHIPAC SERVICE SRL
+ * @author Teo Sarca, www.arhipac.ro
*
FR [ 1981760 ] Improve Query class
* BF [ 2030280 ] org.compiere.model.Query apply access filter issue
* FR [ 2041894 ] Add Query.match() method
* FR [ 2107068 ] Query.setOrderBy should be more error tolerant
* FR [ 2107109 ] Add method Query.setOnlyActiveRecords
* FR [ 2421313 ] Introduce Query.firstOnly convenient method
+ * FR [ 2546052 ] Introduce Query aggregate methods
* @author Redhuan D. Oon
* FR: [ 2214883 ] Remove SQL code and Replace for Query // introducing SQL String prompt in log.info
- * >li>FR: [ 2214883 ] - to introduce .setClient_ID
+ * FR: [ 2214883 ] - to introduce .setClient_ID
*/
public class Query
{
+ public static final String AGGREGATE_COUNT = "COUNT";
+ public static final String AGGREGATE_SUM = "SUM";
+ public static final String AGGREGATE_AVG = "AVG";
+ public static final String AGGREGATE_MIN = "MIN";
+ public static final String AGGREGATE_MAX = "MAX";
+
private static CLogger log = CLogger.getCLogger (Query.class);
private Properties ctx = null;
@@ -294,6 +302,68 @@ public class Query
{
return buildSQL(null, true);
}
+
+ /**
+ * Aggregate given expression on this criteria
+ * @param sqlExpression
+ * @param sqlFunction
+ * @return aggregated value
+ */
+ public BigDecimal aggregate(String sqlExpression, String sqlFunction) throws DBException
+ {
+ if (Util.isEmpty(sqlFunction, true))
+ {
+ throw new DBException("No Aggregate Function defined");
+ }
+ if (Util.isEmpty(sqlExpression, true))
+ {
+ if (AGGREGATE_COUNT == sqlFunction)
+ {
+ sqlExpression = "*";
+ }
+ else
+ {
+ throw new DBException("No Expression defined");
+ }
+ }
+
+ StringBuffer sqlSelect = new StringBuffer("SELECT ").append(sqlFunction).append("(")
+ .append(sqlExpression).append(")")
+ .append(" FROM ").append(table.getTableName());
+
+ BigDecimal value = null;
+ String sql = buildSQL(sqlSelect, false);
+ PreparedStatement pstmt = null;
+ ResultSet rs = null;
+ try
+ {
+ pstmt = DB.prepareStatement(sql, this.trxName);
+ rs = createResultSet(pstmt);
+ if (rs.next())
+ {
+ value = rs.getBigDecimal(1);
+ }
+ if (rs.next())
+ {
+ throw new DBException("QueryMoreThanOneRecordsFound"); // TODO : translate
+ }
+ }
+ catch (SQLException e)
+ {
+ throw new DBException(e);
+ }
+ finally
+ {
+ DB.close(rs, pstmt);
+ rs = null; pstmt = null;
+ }
+ //
+ if (value == null)
+ {
+ value = Env.ZERO;
+ }
+ return value;
+ }
/**
* Count items that match query criteria
@@ -302,25 +372,18 @@ public class Query
*/
public int count() throws DBException
{
- int count = -1;
- String sql = buildSQL(new StringBuffer("SELECT COUNT(*) FROM ").append(table.getTableName()), false);
- PreparedStatement pstmt = null;
- ResultSet rs = null;
- try {
- pstmt = DB.prepareStatement(sql, this.trxName);
- rs = createResultSet(pstmt);
- rs.next();
- count = rs.getInt(1);
- }
- catch (SQLException e) {
- throw new DBException(e);
- }
- finally {
- DB.close(rs, pstmt);
- }
- return count;
+ return aggregate("*", AGGREGATE_COUNT).intValue();
}
+ /**
+ * SUM sqlExpression for items that match query criteria
+ * @param sqlExpression
+ * @return sum
+ */
+ public BigDecimal sum(String sqlExpression)
+ {
+ return aggregate(sqlExpression, AGGREGATE_SUM);
+ }
/**
* Check if there items for query criteria
* @return true if exists, false otherwise
@@ -548,5 +611,4 @@ public class Query
}
return retValue;
} // get_IDs
-
}
diff --git a/extend/src/test/functional/QueryTest.java b/extend/src/test/functional/QueryTest.java
index c204ad7b76..485658c0cc 100644
--- a/extend/src/test/functional/QueryTest.java
+++ b/extend/src/test/functional/QueryTest.java
@@ -183,5 +183,47 @@ public class QueryTest extends AdempiereTestCase
assertEquals(101, ids[0]);
assertEquals(102, ids[1]);
}
-
+
+ public void testAggregate() throws Exception
+ {
+ final int AD_Client_ID = Env.getAD_Client_ID(getCtx());
+ final String sqlFrom = "FROM C_InvoiceLine WHERE IsActive='Y' AND AD_Client_ID="+AD_Client_ID;
+ final Query query = new Query(getCtx(), "C_InvoiceLine", null, getTrxName())
+ .setOnlyActiveRecords(true)
+ .setClient_ID();
+ //
+ // Test COUNT:
+ assertEquals("COUNT not match",
+ DB.getSQLValueBDEx(getTrxName(), "SELECT COUNT(*) "+sqlFrom),
+ query.aggregate(null, Query.AGGREGATE_COUNT));
+ //
+ // Test SUM:
+ assertEquals("SUM not match",
+ DB.getSQLValueBDEx(getTrxName(), "SELECT SUM(LineNetAmt+TaxAmt) "+sqlFrom),
+ query.aggregate("LineNetAmt+TaxAmt", Query.AGGREGATE_SUM));
+ //
+ // Test MIN:
+ assertEquals("MIN not match",
+ DB.getSQLValueBDEx(getTrxName(), "SELECT MIN(LineNetAmt) "+sqlFrom),
+ query.aggregate("LineNetAmt", Query.AGGREGATE_MIN));
+ //
+ // Test MAX:
+ assertEquals("MAX not match",
+ DB.getSQLValueBDEx(getTrxName(), "SELECT MAX(LineNetAmt) "+sqlFrom),
+ query.aggregate("LineNetAmt", Query.AGGREGATE_MAX));
+ //
+ // Test Exception : No Aggregate Function defined
+ assertExceptionThrowed("No Aggregate Function defined", DBException.class, new Runnable(){
+ public void run()
+ {
+ query.aggregate("*", null);
+ }});
+ //
+ // Test Exception : No Expression defined
+ assertExceptionThrowed("No Expression defined", DBException.class, new Runnable(){
+ public void run()
+ {
+ query.aggregate(null, Query.AGGREGATE_SUM);
+ }});
+ }
}