details:   https://code.openbravo.com/erp/devel/pi/rev/a5058ea3c279
changeset: 31582:a5058ea3c279
user:      Víctor Martínez Romanos <victor.martinez <at> openbravo.com>
date:      Thu Mar 02 14:03:01 2017 +0100
summary:   Fixed bug 32493: Pareto Product Report performance refactor

The Pareto Product report had very important performance issues due to the way 
data was retrieved from the database.
Multiple (and unnecessary) sequencial scans were executed on high volume tables 
making the report unusable on real environments.
Besides it has been detected that the information shown was not right: products 
without stock might appear in the report, the percentages were clearly not 
right and the ABC category was also wrong because it was based on wrong 
percentages.

The fix mainly includes a total refactor of the queries, which fixes most of 
the performance issues.
It also adds data aggregation support based on Valued Stock aggregated data, 
which should help to keep the report's performance when the time goes by.

It has been tested that, even without aggregated data, the report performs 
really well on high volume environments. Example: An environment without 
aggregated data and with 7.7E+6 transactions took only 38 seconds to get the 
data for an organization with 250E+3 transactions; before it took "days".

List of important changes:
* Deprecated M_GET_PARETO_ABC function and implemented the same logic directly 
into the SQL query

* Added index on M_TRANSACTION_COST table to the DATEACCT column, which is 
heavily used as a filter criteria for many reports (included Pareto Product)

* Modified index M_VALUED_STOCK_AGG_DATETO on M_VALUED_STOCK_AGG to take into 
account AD_ORG_ID and DATETO. This makes the queries to get data from this 
aggregated table to perform really well.

* Modified ReportParetoProduct_data.xsq select query. This is actually the key 
to get the performance improvement:
 * Usage of window functions to calculate:
  * the total value per organization's warehouse,
  * the percentage per product and organization's warehouse,
  * the accumulated percentage per product and organization's warehouse.

 * The ABC is calculated looking at the accumulated percentage gotten before 
instead of calling the M_GET_PARETO_ABC db function.

 * Reduced the usage of functions in select clause because, although 
individually they are quite fast, they must be executed over multiple records 
thus making the whole query really slow (example 2ms multiplied by 10E3 records 
delays the query 20 seconds!):
  * ad_get_org_le_bu is not needed anymore because the legal entity will be 
always the same for each record in the report. Note that the Pareto's 
organization combo only shows legal entities or child organizations (which only 
have a unique legal entity).
  * c_currency_convert_precision is only called when a currency conversion is 
needed, thus reducing unnecessary overhead.
  * ad_column_identifier calls have been removed. Instead we get static 
(translated) values.

 * Transactions are now filtered by trxprocessdate when the costing rule was 
started, so we avoid to compute legacy records.

 * Although not strictly necessary to get a performance improvement, the query 
has been split in several CTEs, thus making the query more readable and easy to 
maintain.

details:   https://code.openbravo.com/erp/devel/pi/rev/9b9243b31573
changeset: 31583:9b9243b31573
user:      Víctor Martínez Romanos <victor.martinez <at> openbravo.com>
date:      Thu Mar 02 14:25:51 2017 +0100
summary:   Fixed issue 35410: Default currency in Pareto Product Report

Set default currency according to the selected organization

details:   https://code.openbravo.com/erp/devel/pi/rev/205a911b298f
changeset: 31584:205a911b298f
user:      David Miguelez <david.miguelez <at> openbravo.com>
date:      Fri Mar 03 12:48:01 2017 +0100
summary:   Related to Issue 35410. Code Review.

Fixes problem when retrieving currency from parameter window.
Also, fixes problem of currency reloading when no organization has been changed.

diffstat:

 src-db/database/model/functions/M_GET_PARETO_ABC.xml                 |    8 +-
 src-db/database/model/tables/M_TRANSACTION_COST.xml                  |    3 +
 src-db/database/model/tables/M_VALUED_STOCK_AGG.xml                  |    7 +-
 src/org/openbravo/erpCommon/ad_reports/ReportParetoProduct.html      |   28 +-
 src/org/openbravo/erpCommon/ad_reports/ReportParetoProduct.java      |   72 
+++-
 src/org/openbravo/erpCommon/ad_reports/ReportParetoProduct_data.xsql |  205 
++++++---
 src/org/openbravo/erpCommon/ad_reports/ReportValuationStock.java     |    2 +-
 7 files changed, 250 insertions(+), 75 deletions(-)

diffs (truncated from 488 to 300 lines):

diff -r e349ce84e231 -r 205a911b298f 
src-db/database/model/functions/M_GET_PARETO_ABC.xml
--- a/src-db/database/model/functions/M_GET_PARETO_ABC.xml      Fri Mar 03 
10:49:49 2017 +0100
+++ b/src-db/database/model/functions/M_GET_PARETO_ABC.xml      Fri Mar 03 
12:48:01 2017 +0100
@@ -28,11 +28,17 @@
 * under the License.
 * The Original Code is Openbravo ERP.
 * The Initial Developer of the Original Code is Openbravo SLU
-* All portions are Copyright (C) 2001-2016 Openbravo SLU
+* All portions are Copyright (C) 2001-2017 Openbravo SLU
 * All Rights Reserved.
 * Contributor(s):  ______________________________________.
 ************************************************************************/
 
+/* 
+* This code has very important performance issues and it has been deprecated.
+* ReportParetoProduct_data.xsql, select method has been rewritten so it is not 
needed anymore.
+* @deprecated
+*/
+
   VARaCUM NUMBER:=0;
   VARaCUMB NUMBER:=0;
   v_limitA NUMBER:=80;
diff -r e349ce84e231 -r 205a911b298f 
src-db/database/model/tables/M_TRANSACTION_COST.xml
--- a/src-db/database/model/tables/M_TRANSACTION_COST.xml       Fri Mar 03 
10:49:49 2017 +0100
+++ b/src-db/database/model/tables/M_TRANSACTION_COST.xml       Fri Mar 03 
12:48:01 2017 +0100
@@ -76,6 +76,9 @@
       <foreign-key foreignTable="C_CURRENCY" name="M_TRANSCOST_CURRENCY">
         <reference local="C_CURRENCY_ID" foreign="C_CURRENCY_ID"/>
       </foreign-key>
+      <index name="M_TRANSACTION_COST_DATEACCT" unique="false">
+        <index-column name="DATEACCT"/>
+      </index>
       <index name="M_TRANSACTION_COST_TRX" unique="false">
         <index-column name="M_TRANSACTION_ID"/>
       </index>
diff -r e349ce84e231 -r 205a911b298f 
src-db/database/model/tables/M_VALUED_STOCK_AGG.xml
--- a/src-db/database/model/tables/M_VALUED_STOCK_AGG.xml       Fri Mar 03 
10:49:49 2017 +0100
+++ b/src-db/database/model/tables/M_VALUED_STOCK_AGG.xml       Fri Mar 03 
12:48:01 2017 +0100
@@ -106,12 +106,13 @@
       <index name="M_VALUED_STOCK_AGG_DATEFROM" unique="false">
         <index-column name="DATEFROM"/>
       </index>
-      <index name="M_VALUED_STOCK_AGG_DATETO" unique="false">
-        <index-column name="DATETO"/>
-      </index>
       <index name="M_VALUED_STOCK_AGG_LOCATOR" unique="false">
         <index-column name="M_LOCATOR_ID"/>
       </index>
+      <index name="M_VALUED_STOCK_AGG_ORG_DATETO" unique="false">
+        <index-column name="AD_ORG_ID"/>
+        <index-column name="DATETO"/>
+      </index>
       <index name="M_VALUED_STOCK_AGG_PERIOD" unique="false">
         <index-column name="C_PERIOD_ID"/>
       </index>
diff -r e349ce84e231 -r 205a911b298f 
src/org/openbravo/erpCommon/ad_reports/ReportParetoProduct.html
--- a/src/org/openbravo/erpCommon/ad_reports/ReportParetoProduct.html   Fri Mar 
03 10:49:49 2017 +0100
+++ b/src/org/openbravo/erpCommon/ad_reports/ReportParetoProduct.html   Fri Mar 
03 12:48:01 2017 +0100
@@ -13,7 +13,7 @@
  * under the License.
  * The Original Code is Openbravo ERP.
  * The Initial Developer of the Original Code is Openbravo SLU
- * All portions are Copyright (C) 2001-2011 Openbravo SLU
+ * All portions are Copyright (C) 2001-2017 Openbravo SLU
  * All Rights Reserved.
  * Contributor(s):  ______________________________________.
  ************************************************************************
@@ -70,6 +70,30 @@
   changeComboData(cmbWarehouse, arrWarehouse, 
cmbOrganization.options[cmbOrganization.selectedIndex].value, true);  
   return true;
 }
+function callbackCurrency(paramXMLParticular, XMLHttpRequestObj) {
+    var strText = "";
+    if (getReadyStateHandler(XMLHttpRequestObj)) {
+        try {
+            if (XMLHttpRequestObj.responseText) {
+                strText = XMLHttpRequestObj.responseText;
+                if (strText && document.getElementById('inpCurrencyId').value 
!= strText) {
+                    document.getElementById('inpCurrencyId').value = strText;
+                    refreshWarehouses();
+                }
+            }
+       } catch (e) {
+       }
+    }
+    return true;
+}
+function setDefaultCurrency() {
+    try {
+        var paramXMLReq = null;
+        return submitXmlHttpRequest(callbackCurrency, document.frmMain, 
"CURRENCY", "ReportParetoProduct.html", false, null, paramXMLReq);
+    } catch (e) {
+        alert(e);
+    }
+}
 </script>
 
 <script language="JavaScript" type="text/javascript" id="paramWarehouseArray">
@@ -260,7 +284,7 @@
             <tr>
             <td class="TitleCell"><span 
class="LabelText">Organization</span></td>      
               <td class="Combo_ContentCell" colspan="2"> 
-                <select name="inpadOrgId"  id="inpadOrgId" class="ComboKey 
Combo_TwoCells_width" onchange="refreshWarehouses();logChanges(this);return 
true;" required="true">
+                <select name="inpadOrgId"  id="inpadOrgId" class="ComboKey 
Combo_TwoCells_width" 
onchange="refreshWarehouses();setDefaultCurrency();logChanges(this);return 
true;" required="true">
                   <option value=""> <div id="reportAD_Org_ID"></div></option>
                 </select>
               </td>
diff -r e349ce84e231 -r 205a911b298f 
src/org/openbravo/erpCommon/ad_reports/ReportParetoProduct.java
--- a/src/org/openbravo/erpCommon/ad_reports/ReportParetoProduct.java   Fri Mar 
03 10:49:49 2017 +0100
+++ b/src/org/openbravo/erpCommon/ad_reports/ReportParetoProduct.java   Fri Mar 
03 12:48:01 2017 +0100
@@ -11,7 +11,7 @@
  * under the License. 
  * The Original Code is Openbravo ERP. 
  * The Initial Developer of the Original Code is Openbravo SLU 
- * All portions are Copyright (C) 2001-2015 Openbravo SLU 
+ * All portions are Copyright (C) 2001-2017 Openbravo SLU 
  * All Rights Reserved.
  * Contributor(s):  ______________________________________.
  ************************************************************************
@@ -21,24 +21,38 @@
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.math.BigDecimal;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
 
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.apache.commons.lang.StringUtils;
+import org.openbravo.base.exception.OBException;
+import org.openbravo.base.filter.IsIDFilter;
 import org.openbravo.base.secureApp.HttpSecureAppServlet;
 import org.openbravo.base.secureApp.VariablesSecureApp;
 import org.openbravo.costing.CostingStatus;
+import org.openbravo.costing.CostingUtils;
+import org.openbravo.dal.core.OBContext;
+import org.openbravo.dal.service.OBDal;
 import org.openbravo.erpCommon.businessUtility.WindowTabs;
 import org.openbravo.erpCommon.reference.PInstanceProcessData;
 import org.openbravo.erpCommon.utility.ComboTableData;
 import org.openbravo.erpCommon.utility.LeftTabsBar;
 import org.openbravo.erpCommon.utility.NavigationBar;
+import org.openbravo.erpCommon.utility.OBCurrencyUtils;
+import org.openbravo.erpCommon.utility.OBDateUtils;
 import org.openbravo.erpCommon.utility.OBError;
 import org.openbravo.erpCommon.utility.OBMessageUtils;
 import org.openbravo.erpCommon.utility.SequenceIdData;
 import org.openbravo.erpCommon.utility.ToolBar;
 import org.openbravo.erpCommon.utility.Utility;
+import org.openbravo.model.common.enterprise.Organization;
+import org.openbravo.model.materialmgmt.cost.CostingRule;
 import org.openbravo.xmlEngine.XmlDocument;
 
 public class ReportParetoProduct extends HttpSecureAppServlet {
@@ -56,8 +70,11 @@
       String strClient = vars.getClient();
       String strAD_Org_ID = vars.getGlobalVariable("inpadOrgId", 
"ReportParetoProduct|AD_Org_ID",
           "");
-      String strCurrencyId = vars.getGlobalVariable("inpCurrencyId",
-          "ReportParetoProduct|currency", strUserCurrencyId);
+      String strCurrencyId = OBCurrencyUtils.getOrgCurrency(strAD_Org_ID);
+      if (StringUtils.isEmpty(strCurrencyId)) {
+        strCurrencyId = strUserCurrencyId;
+      }
+
       printPageDataSheet(request, response, vars, strWarehouse, strAD_Org_ID, 
strClient,
           strCurrencyId);
     } else if (vars.commandIn("FIND")) {
@@ -85,6 +102,17 @@
           "ReportParetoProduct|currency", strUserCurrencyId);
       printPageDataSheet(request, response, vars, strWarehouse, strAD_Org_ID, 
strClient,
           strCurrencyId);
+    } else if (vars.commandIn("CURRENCY")) {
+      String strOrg = vars.getRequestGlobalVariable("inpadOrgId", 
"ReportParetoProduct|AD_Org_ID",
+          IsIDFilter.instance);
+      if (StringUtils.isEmpty(strOrg)) {
+        strOrg = vars.getOrg();
+      }
+      String strCurrencyId = OBCurrencyUtils.getOrgCurrency(strOrg);
+      response.setContentType("text/html; charset=UTF-8");
+      PrintWriter out = response.getWriter();
+      out.print(strCurrencyId);
+      out.close();
     } else
       pageError(response);
   }
@@ -116,10 +144,43 @@
       OBError myMessage = null;
       myMessage = new OBError();
       try {
-        data = ReportParetoProductData.select(this, strWarehouse, strClient, 
vars.getLanguage(),
-            strCurrencyId, strAD_Org_ID);
+        OBContext.setAdminMode(true);
+
+        // Get legal entity (for aggregated table and currency conversion. The 
latter is only
+        // possible because the report can be launched only for one legal 
entity at the same time)
+        final Organization legalEntity = OBContext.getOBContext()
+            .getOrganizationStructureProvider(strClient)
+            .getLegalEntity(OBDal.getInstance().get(Organization.class, 
strAD_Org_ID));
+        if (legalEntity == null) {
+          throw new OBException(OBMessageUtils.messageBD("WarehouseNotInLE"));
+        }
+
+        // Calculate max aggregated date or set a default value if not 
aggregated data
+        String strMaxAggDate = 
ReportParetoProductData.selectMaxAggregatedDate(this,
+            legalEntity.getId());
+        if (StringUtils.isBlank(strMaxAggDate)) {
+          final DateFormat formatter = new SimpleDateFormat("dd-MM-yyyy");
+          final Date maxAggDate = formatter.parse("01-01-0000");
+          strMaxAggDate = OBDateUtils.formatDate(maxAggDate);
+        }
+
+        // Process Time (useful to avoid taking into account transactions for 
legacy costing rules)
+        String processTime = "01-01-1970 00:00:00";
+        final CostingRule costRule = 
ReportValuationStock.getLEsCostingAlgortithm(legalEntity);
+        if (costRule != null) {
+          processTime = 
OBDateUtils.formatDate(CostingUtils.getCostingRuleStartingDate(costRule),
+              "dd-MM-yyyy HH:mm:ss");
+        }
+        final String processTimeDateFormat = "DD-MM-YYYY HH24:MI:SS";
+
+        data = ReportParetoProductData.select(this, strCurrencyId, strClient, 
legalEntity.getId(),
+            processTime, processTimeDateFormat, strMaxAggDate, strWarehouse, 
strAD_Org_ID,
+            vars.getLanguage());
       } catch (ServletException ex) {
         myMessage = Utility.translateError(this, vars, vars.getLanguage(), 
ex.getMessage());
+      } catch (ParseException ignore) {
+      } finally {
+        OBContext.restorePreviousMode();
       }
       strConvRateErrorMsg = myMessage.getMessage();
       // If a conversion rate is missing for a certain transaction, an
@@ -287,4 +348,5 @@
   public String getServletInfo() {
     return "Servlet ReportParetoProduct info. Insert here any relevant 
information";
   } // end of getServletInfo() method
+
 }
diff -r e349ce84e231 -r 205a911b298f 
src/org/openbravo/erpCommon/ad_reports/ReportParetoProduct_data.xsql
--- a/src/org/openbravo/erpCommon/ad_reports/ReportParetoProduct_data.xsql      
Fri Mar 03 10:49:49 2017 +0100
+++ b/src/org/openbravo/erpCommon/ad_reports/ReportParetoProduct_data.xsql      
Fri Mar 03 12:48:01 2017 +0100
@@ -11,7 +11,7 @@
  * under the License. 
  * The Original Code is Openbravo ERP. 
  * The Initial Developer of the Original Code is Openbravo SLU 
- * All portions are Copyright (C) 2001-2012 Openbravo SLU 
+ * All portions are Copyright (C) 2001-2017 Openbravo SLU 
  * All Rights Reserved. 
  * Contributor(s):  ______________________________________.
  ************************************************************************
@@ -24,75 +24,142 @@
 <SqlClass name="ReportParetoProductData" 
package="org.openbravo.erpCommon.ad_reports">
   <SqlClassComment></SqlClassComment>
   <SqlMethod name="select" type="preparedStatement" return="multiple">
-    <SqlMethodComment></SqlMethodComment>
+    <SqlMethodComment>Five CTEs have been declared:
+  * org_prod_nonagg: this calculates the Value per warehouse's organization 
and product (it transforms each transaction cost to the given currency), 
+                     and it calculates the sum of movement quantities per 
warehouse's organization and product using the M_Transaction and 
M_Transaction_Cost tables directly.
+                     It takes into account transactions just after the last 
costing rule available for the legal entity was started, and transactions and 
transaction costs non-aggregated yet 
+                     (if aggregation is not ready, it takes all the 
information from the M_Transaction and M_Transaction_Cost tables).
+  * org_prod_agg: It takes all the information from the M_Valued_Stock_Agg 
table (if available). Note this table aggregates information per legal entity, 
so if the Pareto is launched for 
+                  a non-legal entity organization, this CTE will return no 
records. If the report's currency is different from the aggregated currency a 
conversion is performed.
+  * org_prod: Creates the union all between org_prod_nonagg and org_prod_agg, 
grouping both datasets. Note that at this point the valuation of both datasets 
is in the same currency
+  * org_prod_perc: based on org_prod, calculates the cost as (the Value per 
org and product calculated on org_prod) / (sum of movement quantities 
calculated on org_prod). This could have been calculated in org_prod cte 
directly, but it's done here for clarity.       
+     It also calculates the percentage per warehouse's organization and 
product as 100 * (the Value per record calculated on org_prod) / (total Value 
per warehouse's org).
+  * org_prod_perc_accum: based on org_prod_perc, calculates the percentage 
accumulated so far per warehouse's organization order by percentage desc. The 
accumulated percentages will be used to set ABC later on.
+  
+  The query outside the CTEs just gets the information from 
org_prod_perc_accum and calculates the ABC value based on the accumulated 
percentage, and joins with other tables to get the data to be printed.
+   </SqlMethodComment>
     <Sql><![CDATA[
-      select orgid, searchkey, name, unit, qty, cost, value, percentage,
-      m_get_pareto_abc(?, ad_org_id, ?, percentage) as isabc, '' as padre, '' 
as id
-      from (
-        select ad_column_identifier('AD_Org', ad_org_id, ?) as orgid,
-          value as searchkey, name as name,
-          ad_column_identifier('C_Uom', c_uom_id, ?) as unit,
-          ad_org_id, m_product_id, sum(movementqty) as qty,
-          sum(value_per_currency)/sum(movementqty) as cost, 
-          sum(value_per_currency) as value, 
-          100 * sum(value_per_currency) / (select sum(cost_per_currency)
-                             from (
-                                  select c_currency_convert_precision(sum(case 
when t.movementqty>=0 then tc.cost else -tc.cost end),
-                                         tc.c_currency_id, ?, to_date(now()), 
null, ?, ad_get_org_le_bu (w.ad_org_id, 'LE')) as cost_per_currency,
-                                         sum(t.movementqty) as movementqty, 
w.m_warehouse_id
-                                  from m_transaction_cost tc, m_transaction t
-                                    left join m_locator l on 
(t.m_locator_id=l.m_locator_id)
-                                    left join m_warehouse w on 
(l.m_warehouse_id=w.m_warehouse_id)
-                                  where tc.m_transaction_id = 
t.m_transaction_id
-                                    and t.iscostcalculated = 'Y'
-                                    and t.transactioncost is not null
-                                    and t.ad_client_id = ?
-                                    and 1=1
-                                    and 2=2
-                                    AND ad_isorgincluded(w.AD_ORG_ID, ?, 
w.ad_client_id) <> -1
-                                  group by tc.c_currency_id, w.ad_org_id, 
w.ad_client_id, w.m_warehouse_id
-                                ) a

------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, SlashDot.org! http://sdm.link/slashdot
_______________________________________________
Openbravo-commits mailing list
Openbravo-commits@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/openbravo-commits

Reply via email to