fixed issue 22805: Price Adjustments don't update prices while editing lines
authorAsier Lostalé <asier.lostale@openbravo.com>
Wed, 16 Jan 2013 11:55:20 +0100
changeset 19229 38af5fb36bd9
parent 19228 6b727c038284
child 19230 7df2baff13d2
fixed issue 22805: Price Adjustments don't update prices while editing lines
src-db/database/model/functions/M_PROMOTION_CALCULATE.xml
src/org/openbravo/erpCommon/ad_callouts/SL_Invoice_Amt.java
src/org/openbravo/erpCommon/ad_callouts/SL_Invoice_Product.java
src/org/openbravo/erpCommon/ad_callouts/SL_Order_Amt.java
src/org/openbravo/erpCommon/ad_callouts/SL_Order_Product.java
src/org/openbravo/erpCommon/businessUtility/PriceAdjustment.java
--- a/src-db/database/model/functions/M_PROMOTION_CALCULATE.xml	Wed Jan 16 11:04:04 2013 +0100
+++ b/src-db/database/model/functions/M_PROMOTION_CALCULATE.xml	Wed Jan 16 11:55:20 2013 +0100
@@ -22,7 +22,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) 2012 Openbravo SLU
+* All portions are Copyright (C) 2012-2013 Openbravo SLU
 * All Rights Reserved.
 * Contributor(s):  ______________________________________.
 ************************************************************************/
@@ -76,16 +76,17 @@
 	end if;
 
   for Cur_Order in (select c_orderline_id as id, gross_unit_price, c_tax_id, cancelpricead, 
-                           M_Product_ID, ad_client_id, ad_org_id, null as from_order
+                           M_Product_ID, ad_client_id, ad_org_id, null as from_order, line
                       from c_orderline 
                      where c_order_id = p_doc_id
                        and p_type = 'O'
                      union
                     select c_invoiceline_id, gross_unit_price, c_tax_id, 'N', 
-                           M_Product_ID, ad_client_id, ad_org_id, c_orderline_id
+                           M_Product_ID, ad_client_id, ad_org_id, c_orderline_id, line
                       from c_invoiceline 
                      where c_invoice_id = p_doc_id
-                       and p_type = 'I') loop
+                       and p_type = 'I'
+                     order by line, 1) loop
     -- Remove existent promotions as they are going to be recalculated
     if (p_type = 'O') then
       select count(*) 
@@ -93,6 +94,18 @@
         from c_orderline_offer
        where c_orderline_id = Cur_Order.id;
 
+      if (v_count = 0) then
+        -- In case there are any price adjustment, prices need also to be recalculated
+        -- as they are proposed in UI as the adjustments were applied
+        select count(*)
+          into v_count
+          from m_offer
+         where isactive = 'Y'
+          and v_date between DateFrom and coalesce(DateTo, TO_DATE('31-12-9999', 'DD-MM-YYYY'))
+          and AD_Client_ID = Cur_Order.AD_Client_ID
+          and M_Offer_Type_ID = '5D4BAF6BB86D4D2C9ED3D5A6FC051579';
+      end if; 
+
       if (v_count > 0) then
 				if (v_taxIncluded = 'Y') then
 					update c_orderline
@@ -116,6 +129,19 @@
        into v_count
        from c_invoiceline_offer
       where c_invoiceline_id = Cur_Order.id;
+
+      if (v_count = 0) then
+        -- In case there are any price adjustment, prices need also to be recalculated
+        -- as they are proposed in UI as the adjustments were applied
+        select count(*)
+          into v_count
+          from m_offer
+         where isactive = 'Y'
+          and v_date between DateFrom and coalesce(DateTo, TO_DATE('31-12-9999', 'DD-MM-YYYY'))
+          and AD_Client_ID = Cur_Order.AD_Client_ID
+          and M_Offer_Type_ID = '5D4BAF6BB86D4D2C9ED3D5A6FC051579';
+      end if; 
+
       if (v_count > 0) then
         if (Cur_Order.from_order is null) then
           -- Invoice: reset to original prices in case invoice is not coming fom an order
--- a/src/org/openbravo/erpCommon/ad_callouts/SL_Invoice_Amt.java	Wed Jan 16 11:04:04 2013 +0100
+++ b/src/org/openbravo/erpCommon/ad_callouts/SL_Invoice_Amt.java	Wed Jan 16 11:55:20 2013 +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-2013 Openbravo SLU 
  * All Rights Reserved. 
  * Contributor(s):  ______________________________________.
  ************************************************************************
@@ -31,9 +31,11 @@
 import org.openbravo.base.secureApp.HttpSecureAppServlet;
 import org.openbravo.base.secureApp.VariablesSecureApp;
 import org.openbravo.dal.service.OBDal;
+import org.openbravo.erpCommon.businessUtility.PriceAdjustment;
 import org.openbravo.erpCommon.utility.Utility;
 import org.openbravo.financial.FinancialUtils;
 import org.openbravo.model.common.invoice.Invoice;
+import org.openbravo.model.common.plm.Product;
 import org.openbravo.utils.FormatUtilities;
 import org.openbravo.xmlEngine.XmlDocument;
 
@@ -65,12 +67,13 @@
       String strLineNetAmt = vars.getNumericParameter("inplinenetamt");
       String strTaxId = vars.getStringParameter("inpcTaxId");
       String strGrossUnitPrice = vars.getNumericParameter("inpgrossUnitPrice");
+      String strBaseGrossUnitPrice = vars.getNumericParameter("inpgrosspricestd");
       String strtaxbaseamt = vars.getNumericParameter("inptaxbaseamt");
 
       try {
         printPage(response, vars, strChanged, strQtyInvoice, strPriceActual, strInvoiceId,
             strProduct, strPriceLimit, strTabId, strPriceList, strPriceStd, strLineNetAmt,
-            strTaxId, strGrossUnitPrice, strtaxbaseamt);
+            strTaxId, strGrossUnitPrice, strBaseGrossUnitPrice, strtaxbaseamt);
       } catch (ServletException ex) {
         pageErrorCallOut(response);
       }
@@ -81,8 +84,8 @@
   void printPage(HttpServletResponse response, VariablesSecureApp vars, String strChanged,
       String strQtyInvoice, String strPriceActual, String strInvoiceId, String strProduct,
       String strPriceLimit, String strTabId, String strPriceList, String strPriceStd,
-      String strLineNetAmt, String strTaxId, String strGrossUnitPrice, String strTaxBaseAmt)
-      throws IOException, ServletException {
+      String strLineNetAmt, String strTaxId, String strGrossUnitPrice,
+      String strBaseGrossUnitPrice, String strTaxBaseAmt) throws IOException, ServletException {
     if (log4j.isDebugEnabled())
       log4j.debug("Output: dataSheet");
     XmlDocument xmlDocument = xmlEngine.readXmlTemplate(
@@ -125,6 +128,11 @@
     lineNetAmt = (!Utility.isBigDecimal(strLineNetAmt) ? ZERO : new BigDecimal(strLineNetAmt));
     taxBaseAmt = (strTaxBaseAmt.equals("") ? ZERO : (new BigDecimal(strTaxBaseAmt))).setScale(
         PricePrecision, BigDecimal.ROUND_HALF_UP);
+
+    Invoice invoice = OBDal.getInstance().get(Invoice.class, strInvoiceId);
+    Product product = OBDal.getInstance().get(Product.class, strProduct);
+    boolean priceIncludeTaxes = invoice.getPriceList().isPriceIncludesTax();
+
     StringBuffer resultado = new StringBuffer();
 
     resultado.append("var calloutName='SL_Invoice_Amt';\n\n");
@@ -146,7 +154,8 @@
       if (log4j.isDebugEnabled())
         log4j.debug("priceActual:" + Double.toString(priceActual.doubleValue()));
 
-      resultado.append("new Array(\"inppricestd\", " + priceActual.toString() + "),");
+      priceStd = PriceAdjustment.calculatePriceStd(invoice, product, qtyInvoice, priceActual);
+      resultado.append("new Array(\"inppricestd\", " + priceStd.toString() + "),");
       resultado.append("new Array(\"inptaxbaseamt\", " + priceActual.multiply(qtyInvoice) + "),");
     }
 
@@ -156,23 +165,33 @@
       if (log4j.isDebugEnabled())
         log4j.debug("strPriceList: " + strPriceList.replace("\"", "") + " product:" + strProduct
             + " qty:" + qtyInvoice.toString());
-      // invoiced qty multiply with gross price
-      BigDecimal grossAmount = new BigDecimal(strGrossUnitPrice.trim()).multiply(new BigDecimal(
-          strQtyInvoice.trim()));
-      resultado.append("new Array(\"inplineGrossAmount\", " + grossAmount.toString() + "),");
+
+      if (priceIncludeTaxes) {
+        BigDecimal baseGrossUnitPrice = new BigDecimal(strBaseGrossUnitPrice.trim());
+        BigDecimal grossUnitPrice = PriceAdjustment.calculatePriceActual(invoice, product,
+            qtyInvoice, baseGrossUnitPrice);
+        BigDecimal grossAmount = grossUnitPrice.multiply(new BigDecimal(strQtyInvoice.trim()));
+        priceActual = FinancialUtils.calculateNetFromGross(strTaxId, grossAmount, invoice
+            .getCurrency().getPricePrecision().intValue(), taxBaseAmt, qtyInvoice);
+        resultado.append("new Array(\"inpgrossUnitPrice\", " + grossUnitPrice.toString() + "),");
+        resultado.append("new Array(\"inplineGrossAmount\", " + grossAmount.toString() + "),");
+      } else {
+        priceActual = PriceAdjustment.calculatePriceActual(invoice, product, qtyInvoice, priceStd);
+      }
     }
     // if taxRate field is changed
     if (strChanged.equals("inpgrossUnitPrice")
-        || (strChanged.equals("inpcTaxId") && (OBDal.getInstance().get(Invoice.class, strInvoiceId)
-            .getPriceList().isPriceIncludesTax()))) {
+        || (strChanged.equals("inpcTaxId") && priceIncludeTaxes)) {
       BigDecimal grossUnitPrice = new BigDecimal(strGrossUnitPrice.trim());
+      BigDecimal baseGrossUnitPrice = PriceAdjustment.calculatePriceStd(invoice, product,
+          qtyInvoice, grossUnitPrice);
       BigDecimal grossAmount = grossUnitPrice.multiply(qtyInvoice);
       BigDecimal netUnitPrice = FinancialUtils.calculateNetFromGross(strTaxId, grossAmount,
           PricePrecision, taxBaseAmt, qtyInvoice);
       priceActual = netUnitPrice;
       priceStd = netUnitPrice;
 
-      resultado.append("new Array(\"inpgrosspricestd\", " + grossUnitPrice.toString() + "),");
+      resultado.append("new Array(\"inpgrosspricestd\", " + baseGrossUnitPrice.toString() + "),");
 
       resultado.append("new Array(\"inppriceactual\"," + netUnitPrice.toString() + "),");
       resultado.append("new Array(\"inppricelimit\", " + netUnitPrice.toString() + "),");
@@ -184,9 +203,10 @@
       }
     }
 
-    if (!strChanged.equals("inplinenetamt"))
+    if (!strChanged.equals("inplinenetamt")) {
       // Net amount of a line equals quantity x unit price (actual price)
       lineNetAmt = qtyInvoice.multiply(priceActual);
+    }
 
     if (strChanged.equals("inplinenetamt")) {
       DecimalFormat priceEditionFmt = Utility.getFormat(vars, "priceEdition");
@@ -237,5 +257,4 @@
     out.println(xmlDocument.print());
     out.close();
   }
-
 }
--- a/src/org/openbravo/erpCommon/ad_callouts/SL_Invoice_Product.java	Wed Jan 16 11:04:04 2013 +0100
+++ b/src/org/openbravo/erpCommon/ad_callouts/SL_Invoice_Product.java	Wed Jan 16 11:55:20 2013 +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-2013 Openbravo SLU 
  * All Rights Reserved. 
  * Contributor(s):  ______________________________________.
  ************************************************************************
@@ -20,6 +20,7 @@
 
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.math.BigDecimal;
 
 import javax.servlet.ServletConfig;
 import javax.servlet.ServletException;
@@ -34,6 +35,7 @@
 import org.openbravo.data.FieldProvider;
 import org.openbravo.erpCommon.businessUtility.PAttributeSet;
 import org.openbravo.erpCommon.businessUtility.PAttributeSetData;
+import org.openbravo.erpCommon.businessUtility.PriceAdjustment;
 import org.openbravo.erpCommon.businessUtility.Tax;
 import org.openbravo.erpCommon.utility.AccDefUtility;
 import org.openbravo.erpCommon.utility.ComboTableData;
@@ -109,24 +111,20 @@
         "org/openbravo/erpCommon/ad_callouts/CallOut").createXmlDocument();
 
     String strPriceActual = "";
+    Invoice invoice = OBDal.getInstance().get(Invoice.class, strCInvoiceID);
+    boolean priceIncludeTaxes = invoice.getPriceList().isPriceIncludesTax();
     if (!strMProductID.equals("")) {
-      SLOrderProductData[] dataInvoice = SLOrderProductData.selectInvoice(this, strCInvoiceID);
-
-      if (log4j.isDebugEnabled())
-        log4j.debug("get Offers date: " + dataInvoice[0].dateinvoiced + " partner:"
-            + dataInvoice[0].cBpartnerId + " prod:" + strMProductID + " std:"
-            + strPriceStd.replace("\"", ""));
-
-      dataInvoice = null;
+      Product product = OBDal.getInstance().get(Product.class, strMProductID);
+      strPriceActual = PriceAdjustment.calculatePriceActual(invoice, product,
+          new BigDecimal(strQty), new BigDecimal((strPriceStd.equals("") ? "0" : strPriceStd)))
+          .toString();
     }
     StringBuffer resultado = new StringBuffer();
 
-    strPriceActual = strPriceStd;
-
     resultado.append("var calloutName='SL_Invoice_Product';\n\n");
     resultado.append("var respuesta = new Array(");
     resultado.append("new Array(\"inpcUomId\", \"" + strUOM + "\"),");
-    if (OBDal.getInstance().get(Invoice.class, strCInvoiceID).getPriceList().isPriceIncludesTax()) {
+    if (priceIncludeTaxes) {
       resultado.append("new Array(\"inpgrossUnitPrice\", "
           + (strPriceActual.equals("") ? "0" : strPriceActual) + "),");
       resultado.append("new Array(\"inpgrosspricestd\", "
--- a/src/org/openbravo/erpCommon/ad_callouts/SL_Order_Amt.java	Wed Jan 16 11:04:04 2013 +0100
+++ b/src/org/openbravo/erpCommon/ad_callouts/SL_Order_Amt.java	Wed Jan 16 11:55:20 2013 +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-200 Openbravo SLU 
+ * All portions are Copyright (C) 2001-2013 Openbravo SLU 
  * All Rights Reserved. 
  * Contributor(s):  ______________________________________.
  ************************************************************************
@@ -31,8 +31,11 @@
 import org.openbravo.base.secureApp.HttpSecureAppServlet;
 import org.openbravo.base.secureApp.VariablesSecureApp;
 import org.openbravo.dal.service.OBDal;
+import org.openbravo.erpCommon.businessUtility.PriceAdjustment;
 import org.openbravo.erpCommon.utility.Utility;
 import org.openbravo.financial.FinancialUtils;
+import org.openbravo.model.common.order.Order;
+import org.openbravo.model.common.plm.Product;
 import org.openbravo.model.pricing.pricelist.PriceList;
 import org.openbravo.utils.FormatUtilities;
 import org.openbravo.xmlEngine.XmlDocument;
@@ -66,7 +69,7 @@
       String strUOM = vars.getStringParameter("inpcUomId");
       String strAttribute = vars.getStringParameter("inpmAttributesetinstanceId");
       String strQty = vars.getNumericParameter("inpqtyordered");
-      String cancelPriceAd = vars.getStringParameter("inpcancelpricead");
+      boolean cancelPriceAd = "Y".equals(vars.getStringParameter("inpcancelpricead"));
       String strLineNetAmt = vars.getNumericParameter("inplinenetamt");
       String strTaxId = vars.getStringParameter("inpcTaxId");
       String strGrossUnitPrice = vars.getNumericParameter("inpgrossUnitPrice");
@@ -89,10 +92,9 @@
   private void printPage(HttpServletResponse response, VariablesSecureApp vars, String strChanged,
       String strQtyOrdered, String _strPriceActual, String strDiscount, String strPriceLimit,
       String strPriceList, String strCOrderId, String strProduct, String strUOM,
-      String strAttribute, String strQty, String strPriceStd, String cancelPriceAd,
+      String strAttribute, String strQty, String strPriceStd, boolean cancelPriceAd,
       String strLineNetAmt, String strTaxId, String strGrossUnitPrice, String strGrossPriceList,
       String strTaxBaseAmt, String strGrossBaseUnitPrice) throws IOException, ServletException {
-
     XmlDocument xmlDocument = xmlEngine.readXmlTemplate(
         "org/openbravo/erpCommon/ad_callouts/CallOut").createXmlDocument();
     SLOrderAmtData[] data = SLOrderAmtData.select(this, strCOrderId);
@@ -146,21 +148,43 @@
     resultado.append("var calloutName='SL_Order_Amt';\n\n");
     resultado.append("var respuesta = new Array(");
 
+    Order order = OBDal.getInstance().get(Order.class, strCOrderId);
+    Product product = OBDal.getInstance().get(Product.class, strProduct);
+
     if (strChanged.equals("inplinenetamt")) {
       priceActual = lineNetAmt.divide(qtyOrdered, pricePrecision, BigDecimal.ROUND_HALF_UP);
       if (priceActual.compareTo(BigDecimal.ZERO) == 0) {
         lineNetAmt = BigDecimal.ZERO;
       }
     }
+
+    if (strChanged.equals("inpqtyordered") && !cancelPriceAd) {
+      if (isTaxIncludedPriceList) {
+        grossUnitPrice = PriceAdjustment.calculatePriceActual(order, product, qtyOrdered,
+            grossBaseUnitPrice);
+        BigDecimal grossAmount = grossUnitPrice.multiply(qtyOrdered).setScale(stdPrecision,
+            RoundingMode.HALF_UP);
+        priceActual = FinancialUtils.calculateNetFromGross(strTaxId, grossAmount, pricePrecision,
+            taxBaseAmt, qtyOrdered);
+        resultado.append("new Array(\"inpgrossUnitPrice\", " + grossUnitPrice.toString() + "),");
+      } else {
+        priceActual = PriceAdjustment.calculatePriceActual(order, product, qtyOrdered, priceStd);
+      }
+      resultado.append("new Array(\"inppriceactual\", " + priceActual + "),");
+    }
     // Calculating prices for offers...
     if (strChanged.equals("inppriceactual") || strChanged.equals("inplinenetamt")) {
       log4j.debug("priceActual:" + priceActual.toString());
-      priceStd = priceActual;
+      if (!cancelPriceAd) {
+        priceStd = PriceAdjustment.calculatePriceStd(order, product, qtyOrdered, priceActual);
+      } else {
+        priceStd = priceActual;
+      }
       resultado.append("new Array(\"inppricestd\", " + priceStd.toString() + "),");
     }
 
     if (strChanged.equals("inpcancelpricead")) {
-      if ("Y".equals(cancelPriceAd)) {
+      if (cancelPriceAd) {
         resultado.append("new Array(\"inppriceactual\", " + strPriceStd + "),");
       }
     }
@@ -178,11 +202,39 @@
       grossBaseUnitPrice = grossUnitPrice;
       resultado.append("new Array(\"inpgrosspricestd\", " + grossBaseUnitPrice.toString() + "),");
 
-      resultado.append("new Array(\"inppriceactual\"," + netUnitPrice.toString() + "),");
+      resultado.append("new Array(\"inppriceactual\"," + priceActual.toString() + "),");
       resultado.append("new Array(\"inppricelimit\", " + netUnitPrice.toString() + "),");
       resultado.append("new Array(\"inppricestd\"," + netUnitPrice.toString() + "),");
     }
 
+    if (isGrossUnitPriceChanged || (strChanged.equals("inpcTaxId") && isTaxIncludedPriceList)) {
+      BigDecimal grossAmount = grossUnitPrice.multiply(qtyOrdered).setScale(stdPrecision,
+          RoundingMode.HALF_UP);
+
+      final BigDecimal netUnitPrice = FinancialUtils.calculateNetFromGross(strTaxId, grossAmount,
+          pricePrecision, taxBaseAmt, qtyOrdered);
+
+      priceActual = netUnitPrice;
+
+      if (cancelPriceAd) {
+        grossBaseUnitPrice = grossUnitPrice;
+        priceStd = netUnitPrice;
+      } else {
+        grossBaseUnitPrice = PriceAdjustment.calculatePriceStd(order, product, qtyOrdered,
+            grossUnitPrice);
+        BigDecimal baseGrossAmount = grossBaseUnitPrice.multiply(qtyOrdered).setScale(stdPrecision,
+            RoundingMode.HALF_UP);
+        priceStd = FinancialUtils.calculateNetFromGross(strTaxId, baseGrossAmount, pricePrecision,
+            taxBaseAmt, qtyOrdered);
+      }
+
+      resultado.append("new Array(\"inpgrosspricestd\", " + grossBaseUnitPrice.toString() + "),");
+
+      resultado.append("new Array(\"inppriceactual\"," + priceActual.toString() + "),");
+      resultado.append("new Array(\"inppricelimit\", " + netUnitPrice.toString() + "),");
+      resultado.append("new Array(\"inppricestd\"," + priceStd.toString() + "),");
+    }
+
     // calculating discount
     if (strChanged.equals("inppricelist") || strChanged.equals("inppriceactual")
         || strChanged.equals("inplinenetamt") || strChanged.equals("inpgrosspricelist")
@@ -236,9 +288,9 @@
         BigDecimal baseUnitPrice = priceList.subtract(priceList.multiply(newDiscount).divide(
             new BigDecimal("100"), pricePrecision, BigDecimal.ROUND_HALF_UP));
         if (isTaxIncludedPriceList) {
-          grossUnitPrice = baseUnitPrice;
-          isGrossUnitPriceChanged = true;
-          resultado.append("new Array(\"inpgrosspricestd\", " + grossUnitPrice.toString() + "),");
+          grossUnitPrice = PriceAdjustment.calculatePriceActual(order, product, qtyOrdered,
+              baseUnitPrice);
+          resultado.append("new Array(\"inpgrosspricestd\", " + baseUnitPrice.toString() + "),");
           resultado.append("new Array(\"inpgrossUnitPrice\", " + grossUnitPrice.toString() + "),");
 
           // set also net prices
@@ -248,13 +300,17 @@
           final BigDecimal netUnitPrice = FinancialUtils.calculateNetFromGross(strTaxId,
               grossAmount, pricePrecision, taxBaseAmt, qtyOrdered);
 
-          priceActual = netUnitPrice;
           priceStd = netUnitPrice;
         } else {
           priceStd = baseUnitPrice;
-          priceActual = baseUnitPrice;
         }
-        resultado.append("new Array(\"inppriceactual\", " + priceStd.toString() + "),");
+
+        if (!cancelPriceAd) {
+          priceActual = PriceAdjustment.calculatePriceActual(order, product, qtyOrdered, priceStd);
+        } else {
+          priceActual = priceStd;
+        }
+        resultado.append("new Array(\"inppriceactual\", " + priceActual.toString() + "),");
         resultado.append("new Array(\"inppricestd\", " + priceStd.toString() + "),");
       }
     }
@@ -302,7 +358,7 @@
       log4j.debug("Net unit price results: " + resultado.toString());
     }
     // Multiply
-    if ("Y".equals(cancelPriceAd)) {
+    if (cancelPriceAd) {
       lineNetAmt = qtyOrdered.multiply(priceStd);
     } else {
       if (!strChanged.equals("inplinenetamt")) {
--- a/src/org/openbravo/erpCommon/ad_callouts/SL_Order_Product.java	Wed Jan 16 11:04:04 2013 +0100
+++ b/src/org/openbravo/erpCommon/ad_callouts/SL_Order_Product.java	Wed Jan 16 11:55:20 2013 +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-2010 Openbravo SLU 
+ * All portions are Copyright (C) 2001-2013 Openbravo SLU 
  * All Rights Reserved. 
  * Contributor(s):  ______________________________________.
  ************************************************************************
@@ -33,10 +33,12 @@
 import org.openbravo.data.FieldProvider;
 import org.openbravo.erpCommon.businessUtility.PAttributeSet;
 import org.openbravo.erpCommon.businessUtility.PAttributeSetData;
+import org.openbravo.erpCommon.businessUtility.PriceAdjustment;
 import org.openbravo.erpCommon.businessUtility.Tax;
 import org.openbravo.erpCommon.utility.ComboTableData;
 import org.openbravo.erpCommon.utility.Utility;
 import org.openbravo.model.common.order.Order;
+import org.openbravo.model.common.plm.Product;
 import org.openbravo.utils.FormatUtilities;
 import org.openbravo.xmlEngine.XmlDocument;
 
@@ -109,28 +111,29 @@
         .getPriceList().isPriceIncludesTax();
 
     if (!strMProductID.equals("")) {
-      SLOrderProductData[] dataOrder = SLOrderProductData.select(this, strCOrderId);
 
-      log4j.debug("get Offers date: " + dataOrder[0].dateordered + " partner:"
-          + dataOrder[0].cBpartnerId + " prod:" + strMProductID + " std:"
-          + strPriceStd.replace("\"", ""));
+      Order order = OBDal.getInstance().get(Order.class, strCOrderId);
+      Product product = OBDal.getInstance().get(Product.class, strMProductID);
+
       if (isTaxIncludedPriceList) {
+        strPriceActual = PriceAdjustment.calculatePriceActual(order, product,
+            "".equals(strQty) ? BigDecimal.ZERO : new BigDecimal(strQty),
+            new BigDecimal(strGrossBaseUnitPrice.equals("") ? "0" : strGrossBaseUnitPrice))
+            .toString();
         strNetPriceList = "0";
       } else {
+        strPriceActual = PriceAdjustment.calculatePriceActual(order, product,
+            "".equals(strQty) ? BigDecimal.ZERO : new BigDecimal(strQty),
+            new BigDecimal(strPriceStd.equals("") ? "0" : strPriceStd)).toString();
         strGrossPriceList = "0";
       }
-      log4j.debug("get Offers price:" + strPriceActual);
-
-      dataOrder = null;
     } else {
       strUOM = strNetPriceList = strGrossPriceList = strPriceLimit = strPriceStd = "";
     }
     StringBuffer resultado = new StringBuffer();
-
-    strPriceActual = strPriceStd;
-
     // Discount...
     BigDecimal discount = BigDecimal.ZERO;
+    BigDecimal priceStd = null;
     if (isTaxIncludedPriceList) {
       BigDecimal priceList = (strGrossPriceList.equals("") ? BigDecimal.ZERO : new BigDecimal(
           strGrossPriceList));
@@ -143,7 +146,7 @@
     } else {
       BigDecimal priceList = (strNetPriceList.equals("") ? BigDecimal.ZERO : new BigDecimal(
           strNetPriceList));
-      BigDecimal priceStd = (strPriceStd.equals("") ? BigDecimal.ZERO : new BigDecimal(strPriceStd));
+      priceStd = (strPriceStd.equals("") ? BigDecimal.ZERO : new BigDecimal(strPriceStd));
       if (priceList.compareTo(BigDecimal.ZERO) != 0) {
         discount = priceList.subtract(priceStd).multiply(new BigDecimal("100"))
             .divide(priceList, 2, BigDecimal.ROUND_HALF_UP);
@@ -167,8 +170,7 @@
           + (strPriceLimit.equals("") ? "0" : strPriceLimit) + "),");
       resultado.append("new Array(\"inppricestd\", " + (strPriceStd.equals("") ? "0" : strPriceStd)
           + "),");
-      resultado.append("new Array(\"inppriceactual\", "
-          + (strPriceActual.equals("") ? "0" : strPriceActual) + "),");
+      resultado.append("new Array(\"inppriceactual\", " + strPriceActual + "),");
     }
     if (!"".equals(strCurrency)) {
       resultado.append("new Array(\"inpcCurrencyId\", \"" + strCurrency + "\"),");
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/openbravo/erpCommon/businessUtility/PriceAdjustment.java	Wed Jan 16 11:55:20 2013 +0100
@@ -0,0 +1,249 @@
+/*
+ *************************************************************************
+ * The contents of this file are subject to the Openbravo  Public  License
+ * Version  1.1  (the  "License"),  being   the  Mozilla   Public  License
+ * Version 1.1  with a permitted attribution clause; you may not  use this
+ * file except in compliance with the License. You  may  obtain  a copy of
+ * the License at http://www.openbravo.com/legal/license.html 
+ * Software distributed under the License  is  distributed  on  an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+ * License for the specific  language  governing  rights  and  limitations
+ * under the License. 
+ * The Original Code is Openbravo ERP. 
+ * The Initial Developer of the Original Code is Openbravo SLU 
+ * All portions are Copyright (C) 2013 Openbravo SLU 
+ * All Rights Reserved. 
+ * Contributor(s):  ______________________________________.
+ ************************************************************************
+ */
+package org.openbravo.erpCommon.businessUtility;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+import org.openbravo.base.structure.BaseOBObject;
+import org.openbravo.dal.service.OBDal;
+import org.openbravo.dal.service.OBQuery;
+import org.openbravo.model.common.currency.Currency;
+import org.openbravo.model.common.invoice.Invoice;
+import org.openbravo.model.common.order.Order;
+import org.openbravo.model.common.plm.Product;
+
+/**
+ * This class is in charge of calculating prices for Discounts & Promotions of Price Adjustment
+ * type. It is intended to be used from callouts so final price can be seen in advance when
+ * editing/creating the line, opposite to the rest of promotions that are not calculated until the
+ * document is posted. This is done in this way to keep backwards compatibility.
+ * 
+ * @author alostale
+ * 
+ */
+public class PriceAdjustment {
+  private static final Logger log = Logger.getLogger(PriceAdjustment.class);
+
+  /**
+   * Calculates price actual from price standard applying the Price Adjustments that fit the rules.
+   * 
+   */
+  public static BigDecimal calculatePriceActual(BaseOBObject orderOrInvoice, Product product,
+      BigDecimal qty, BigDecimal priceStd) {
+    BigDecimal priceActual = priceStd;
+    try {
+      int precision = ((Currency) orderOrInvoice.get(Invoice.PROPERTY_CURRENCY))
+          .getPricePrecision().intValue();
+      for (org.openbravo.model.pricing.priceadjustment.PriceAdjustment promo : getApplicablePriceAdjustments(
+          orderOrInvoice, qty, product, false)) {
+        log.debug("promo: " + promo + "- " + promo.getDiscount());
+        if (promo.getFixedPrice() != null) {
+          priceActual = promo.getFixedPrice();
+        } else {
+          priceActual = priceActual
+              .subtract(promo.getDiscountAmount())
+              .multiply(
+                  BigDecimal.ONE.subtract(promo.getDiscount().divide(BigDecimal.valueOf(100),
+                      precision, BigDecimal.ROUND_HALF_UP)))
+              .setScale(precision, BigDecimal.ROUND_HALF_UP);
+        }
+        if (!promo.isApplyNext()) {
+          break;
+        }
+      }
+      log.debug("Actual:" + priceStd + "->" + priceActual);
+      return priceActual;
+    } catch (Throwable t) {
+      log.error("Error calculating price actual with adjustments, returning price std (" + priceStd
+          + ") order/invoice:" + orderOrInvoice + " - product: " + product + " - qty:" + qty);
+      return priceStd;
+    }
+  }
+
+  /**
+   * Calculates price standard from price actual reverse applying the Price Adjustments that fit the
+   * rules.
+   * 
+   */
+  public static BigDecimal calculatePriceStd(BaseOBObject orderOrInvoice, Product product,
+      BigDecimal qty, BigDecimal priceActual) {
+    BigDecimal priceStd = priceActual;
+    try {
+      int precision = ((Currency) orderOrInvoice.get(Invoice.PROPERTY_CURRENCY))
+          .getPricePrecision().intValue();
+      for (org.openbravo.model.pricing.priceadjustment.PriceAdjustment promo : getApplicablePriceAdjustments(
+          orderOrInvoice, qty, product, true)) {
+        log.debug("promo: " + promo + "- " + promo.getDiscount());
+        priceStd = priceStd.add(promo.getDiscountAmount()).divide(
+            BigDecimal.ONE.subtract(promo.getDiscount().divide(BigDecimal.valueOf(100), precision,
+                BigDecimal.ROUND_HALF_UP)), precision, BigDecimal.ROUND_HALF_UP);
+      }
+
+      log.debug("Std:" + priceActual + "->" + priceStd);
+      return priceStd;
+    } catch (Throwable t) {
+      log.error("Error calculating price std with adjustments, returning price actual ("
+          + priceActual + ") order/invoice:" + orderOrInvoice + " - product: " + product
+          + " - qty:" + qty);
+      return priceActual;
+    }
+  }
+
+  private static List<org.openbravo.model.pricing.priceadjustment.PriceAdjustment> getApplicablePriceAdjustments(
+      BaseOBObject orderOrInvoice, BigDecimal qty, Product product, boolean reverse) {
+    String hql = "as p ";
+    hql += "where active = true ";
+    hql += "and client = :client ";
+    hql += "and (endingDate is null or endingDate>:date) ";
+    hql += "and p.discountType.id = '5D4BAF6BB86D4D2C9ED3D5A6FC051579' ";
+    hql += "and (minQuantity is null or minQuantity <= :qty) ";
+    hql += "and (maxQuantity is null or maxQuantity >= :qty) ";
+
+    // price list
+    hql += "and ((includePriceLists='Y' ";
+    hql += "  and not exists (select 1 ";
+    hql += "         from PricingAdjustmentPriceList pl";
+    hql += "        where active = true";
+    hql += "          and pl.priceAdjustment = p";
+    hql += "          and pl.priceList = :priceList)) ";
+    hql += "   or (includePriceLists='N' ";
+    hql += "  and  exists (select 1 ";
+    hql += "         from PricingAdjustmentPriceList pl";
+    hql += "        where active = true";
+    hql += "          and pl.priceAdjustment = p";
+    hql += "          and pl.priceList.id = :priceList)) ";
+    hql += "    ) ";
+
+    // Business Partner
+    hql += "and ((includedBusinessPartners = 'Y' ";
+    hql += "and not exists (select 1 ";
+    hql += "        from PricingAdjustmentBusinessPartner bp";
+    hql += "       where active = true";
+    hql += "         and bp.priceAdjustment = p";
+    hql += "         and bp.businessPartner = :bp)) ";
+    hql += "      or (includedBusinessPartners = 'N'";
+    hql += "  and exists (select 1";
+    hql += "        from PricingAdjustmentBusinessPartner bp";
+    hql += "         where active = true";
+    hql += "         and bp.priceAdjustment = p";
+    hql += "         and bp.businessPartner = :bp)) ";
+    hql += "    ) ";
+
+    // Business Partner Category
+    hql += "and ((includedBPCategories = 'Y' ";
+    hql += "and not exists (select 1 ";
+    hql += "          from BusinessPartner bp, ";
+    hql += "               PricingAdjustmentBusinessPartnerGroup bpc";
+    hql += "         where bpc.active = true";
+    hql += "           and bpc.priceAdjustment = p";
+    hql += "           and bp = :bp";
+    hql += "           and bp.businessPartnerCategory = bpc.businessPartnerCategory))";
+    hql += "  or (includedBPCategories = 'N'";
+    hql += " and exists (select 1 ";
+    hql += "          from BusinessPartner bp, ";
+    hql += "               PricingAdjustmentBusinessPartnerGroup bpc";
+    hql += "         where bpc.active = true";
+    hql += "           and bpc.priceAdjustment = p";
+    hql += "           and bp = :bp";
+    hql += "           and bp.businessPartnerCategory = bpc.businessPartnerCategory))";
+    hql += "    ) ";
+
+    // Product
+    hql += "and ((includedProducts = 'Y' ";
+    hql += "and not exists (select 1";
+    hql += "          from PricingAdjustmentProduct pr";
+    hql += "         where active = true";
+    hql += "           and pr.priceAdjustment = p";
+    hql += "           and pr.product = :product)) ";
+    hql += "        or (includedProducts = 'N'";
+    hql += "and exists (select 1";
+    hql += "          from PricingAdjustmentProduct pr";
+    hql += "         where active = true";
+    hql += "           and pr.priceAdjustment = p";
+    hql += "           and pr.product = :product)) ";
+    hql += "    ) ";
+
+    // Product Category
+    hql += "and ((includedProductCategories ='Y' ";
+    hql += "and not exists (select 1";
+    hql += "          from PricingAdjustmentProductCategory pc, Product pr";
+    hql += "          where pc.active = true";
+    hql += "            and pc.priceAdjustment = p";
+    hql += "            and pr = :product";
+    hql += "            and pc.productCategory = pr.productCategory)) ";
+    hql += "           or (includedProductCategories ='N'";
+    hql += "and exists (select 1";
+    hql += "          from PricingAdjustmentProductCategory pc, Product pr";
+    hql += "          where pc.active = true";
+    hql += "            and pc.priceAdjustment = p";
+    hql += "            and pr = :product";
+    hql += "            and pc.productCategory = pr.productCategory)) ";
+    hql += "    ) ";
+
+    // organization
+    hql += "and ((includedOrganizations='Y' ";
+    hql += "  and not exists (select 1 ";
+    hql += "         from PricingAdjustmentOrganization o";
+    hql += "        where active = true";
+    hql += "          and o.priceAdjustment = p";
+    hql += "          and o.organization.id = :org)) ";
+    hql += "   or (includedOrganizations='N' ";
+    hql += "  and  exists (select 1 ";
+    hql += "         from PricingAdjustmentOrganization o";
+    hql += "        where active = true";
+    hql += "          and o.priceAdjustment = p";
+    hql += "          and o.organization.id = :org )) ";
+    hql += "    ) ";
+
+    hql += " order by priority, id";
+
+    OBQuery<org.openbravo.model.pricing.priceadjustment.PriceAdjustment> q = OBDal.getInstance()
+        .createQuery(org.openbravo.model.pricing.priceadjustment.PriceAdjustment.class, hql);
+    q.setNamedParameter("client", orderOrInvoice.get(Invoice.PROPERTY_CLIENT));
+    q.setNamedParameter("org", orderOrInvoice.get(Invoice.PROPERTY_ORGANIZATION));
+    q.setNamedParameter("priceList", orderOrInvoice.get(Invoice.PROPERTY_PRICELIST));
+    q.setNamedParameter("bp", orderOrInvoice.get(Invoice.PROPERTY_BUSINESSPARTNER));
+
+    if (orderOrInvoice instanceof Invoice) {
+      q.setNamedParameter("date", ((Invoice) orderOrInvoice).getInvoiceDate());
+    } else {
+      q.setNamedParameter("date", ((Order) orderOrInvoice).getOrderDate());
+    }
+    q.setNamedParameter("qty", qty);
+    q.setNamedParameter("product", product);
+
+    List<org.openbravo.model.pricing.priceadjustment.PriceAdjustment> ql = q.list();
+    List<org.openbravo.model.pricing.priceadjustment.PriceAdjustment> result = ql;
+    if (reverse) {
+      // when reversing the list, special care must be taken with cascades
+      result = new ArrayList<org.openbravo.model.pricing.priceadjustment.PriceAdjustment>();
+      for (int i = ql.size() - 1; i >= 0; i--) {
+        org.openbravo.model.pricing.priceadjustment.PriceAdjustment promo = ql.get(i);
+        if (!promo.isApplyNext()) {
+          result = new ArrayList<org.openbravo.model.pricing.priceadjustment.PriceAdjustment>();
+        }
+        result.add(promo);
+      }
+    }
+    return result;
+  }
+}