svn commit: r433151 - in /incubator/ofbiz/trunk/applications/product: servicedef/services_pricepromo.xml src/org/ofbiz/product/price/PriceServices.java

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

svn commit: r433151 - in /incubator/ofbiz/trunk/applications/product: servicedef/services_pricepromo.xml src/org/ofbiz/product/price/PriceServices.java

jonesde
Author: jonesde
Date: Sun Aug 20 22:14:06 2006
New Revision: 433151

URL: http://svn.apache.org/viewvc?rev=433151&view=rev
Log:
Refactored the calculateProductPrice service to be a bit more modular and use more sub-routines; this was to support a new in attribute called findAllQuantityPrices which if set to Y will cause the new out attribute allQuantityPrices to be populated; this is meant to be used to more easily show the various quantity break prices that a product might have

Modified:
    incubator/ofbiz/trunk/applications/product/servicedef/services_pricepromo.xml
    incubator/ofbiz/trunk/applications/product/src/org/ofbiz/product/price/PriceServices.java

Modified: incubator/ofbiz/trunk/applications/product/servicedef/services_pricepromo.xml
URL: http://svn.apache.org/viewvc/incubator/ofbiz/trunk/applications/product/servicedef/services_pricepromo.xml?rev=433151&r1=433150&r2=433151&view=diff
==============================================================================
--- incubator/ofbiz/trunk/applications/product/servicedef/services_pricepromo.xml (original)
+++ incubator/ofbiz/trunk/applications/product/servicedef/services_pricepromo.xml Sun Aug 20 22:14:06 2006
@@ -39,6 +39,7 @@
         <attribute name="termUomId" type="String" mode="IN" optional="true"><!-- if specified ProductPrice records will be filtered by this, ensures for purposes like recurring prices that only the recurring term desired is taken into consideration --></attribute>
         <attribute name="autoUserLogin" type="GenericValue" mode="IN" optional="true"/>
         <attribute name="checkIncludeVat" type="String" mode="IN" optional="true"><!-- can be Y or N, defaults to N --></attribute>
+        <attribute name="findAllQuantityPrices" type="String" mode="IN" optional="true"><!-- can be Y or N, defaults to N; see the allQuantityPrices attribute for more details --></attribute>
         
         <attribute name="basePrice" type="Double" mode="OUT" optional="false"><!-- will only be different from price if there is a display price adjustment, for example: checkIncludeVat=Y and a VAT amount was found --></attribute>
         <attribute name="price" type="Double" mode="OUT" optional="false"/>
@@ -52,6 +53,12 @@
         <attribute name="validPriceFound" type="Boolean" mode="OUT" optional="false"/>
         <attribute name="currencyUsed" type="String" mode="OUT" optional="false"/>
         <attribute name="orderItemPriceInfos" type="java.util.List" mode="OUT" optional="false"/>
+        <attribute name="allQuantityPrices" type="java.util.List" mode="OUT" optional="true">
+            <!-- Populated when findAllQuantityPrices is Y;
+                is a List of Map where each Map has all other return attributes normally created for a price calculation;
+                each Map in the List will also contain a field called "quantityProductPriceRule" with the GenericValue object representing the ProductPriceRule with the quantity condition that the List entry is based on
+            -->
+        </attribute>
     </service>
 
     <service name="createProductPriceRule" default-entity-name="ProductPriceRule" engine="simple"

Modified: incubator/ofbiz/trunk/applications/product/src/org/ofbiz/product/price/PriceServices.java
URL: http://svn.apache.org/viewvc/incubator/ofbiz/trunk/applications/product/src/org/ofbiz/product/price/PriceServices.java?rev=433151&r1=433150&r2=433151&view=diff
==============================================================================
--- incubator/ofbiz/trunk/applications/product/src/org/ofbiz/product/price/PriceServices.java (original)
+++ incubator/ofbiz/trunk/applications/product/src/org/ofbiz/product/price/PriceServices.java Sun Aug 20 22:14:06 2006
@@ -27,6 +27,7 @@
 import java.util.TreeSet;
 
 import javolution.util.FastList;
+import javolution.util.FastMap;
 
 import org.ofbiz.base.util.Debug;
 import org.ofbiz.base.util.UtilDateTime;
@@ -88,14 +89,14 @@
         Map result = new HashMap();
         Timestamp nowTimestamp = UtilDateTime.nowTimestamp();
 
-        boolean isSale = false;
-        List orderItemPriceInfos = new LinkedList();
-
         GenericValue product = (GenericValue) context.get("product");
         String productId = product.getString("productId");
         String prodCatalogId = (String) context.get("prodCatalogId");
         String webSiteId = (String) context.get("webSiteId");
         String checkIncludeVat = (String) context.get("checkIncludeVat");
+        
+        String findAllQuantityPricesStr = (String) context.get("findAllQuantityPrices");
+        boolean findAllQuantityPrices = "Y".equals(findAllQuantityPricesStr);
 
         String agreementId = (String) context.get("agreementId");
 
@@ -507,434 +508,113 @@
             result.put("averageCost", averageCostValue != null ? averageCostValue.getDouble("price") : null);
             result.put("promoPrice", promoPriceValue != null ? promoPriceValue.getDouble("price") : null);
             result.put("specialPromoPrice", specialPromoPriceValue != null ? specialPromoPriceValue.getDouble("price") : null);
+            result.put("validPriceFound", new Boolean(validPriceFound));
+            result.put("isSale", new Boolean(false));
+            result.put("orderItemPriceInfos", FastList.newInstance());
+
+            Map errorResult = addGeneralResults(result, competitivePriceValue, specialPromoPriceValue, productStore,
+                checkIncludeVat, currencyUomId, productId, quantity, partyId, dispatcher);
+            if (errorResult != null) return errorResult;
         } else {
             try {
-                // get some of the base values to calculate with
-                double listPrice = listPriceDbl.doubleValue();
-                double averageCost = (averageCostValue != null && averageCostValue.get("price") != null) ? averageCostValue.getDouble("price").doubleValue() : listPrice;
-                double margin = listPrice - averageCost;
-
-                // calculate running sum based on listPrice and rules found
-                double price = listPrice;
-
-                Collection productPriceRules = null;
-
-                // At this point we have two options: optimize for large ruleset, or optimize for small ruleset
-                // NOTE: This only effects the way that the rules to be evaluated are selected.
-                // For large rule sets we can do a cached pre-filter to limit the rules that need to be evaled for a specific product.
-                // Genercally I don't think that rule sets will get that big though, so the default is optimize for smaller rule set.
-                if (optimizeForLargeRuleSet) {
-                    // ========= find all rules that must be run for each input type; this is kind of like a pre-filter to slim down the rules to run =========
-                    // utilTimer.timerString("Before create rule id list", module);
-                    TreeSet productPriceRuleIds = new TreeSet();
-
-                    // ------- These are all of the conditions that DON'T depend on the current inputs -------
-
-                    // by productCategoryId
-                    // for we will always include any rules that go by category, shouldn't be too many to iterate through each time and will save on cache entries
-                    // note that we always want to put the category, quantity, etc ones that find all rules with these conditions in separate cache lists so that they can be easily cleared
-                    Collection productCategoryIdConds = delegator.findByAndCache("ProductPriceCond", UtilMisc.toMap("inputParamEnumId", "PRIP_PROD_CAT_ID"));
-                    if (productCategoryIdConds != null && productCategoryIdConds.size() > 0) {
-                        Iterator productCategoryIdCondsIter = productCategoryIdConds.iterator();
-                        while (productCategoryIdCondsIter.hasNext()) {
-                            GenericValue productCategoryIdCond = (GenericValue) productCategoryIdCondsIter.next();
-                            productPriceRuleIds.add(productCategoryIdCond.getString("productPriceRuleId"));
-                        }
-                    }
-
-                    // by quantity -- should we really do this one, ie is it necessary?
-                    // we could say that all rules with quantity on them must have one of these other values
-                    // but, no we'll do it the other way, any that have a quantity will always get compared
-                    Collection quantityConds = delegator.findByAndCache("ProductPriceCond", UtilMisc.toMap("inputParamEnumId", "PRIP_QUANTITY"));
-                    if (quantityConds != null && quantityConds.size() > 0) {
-                        Iterator quantityCondsIter = quantityConds.iterator();
-                        while (quantityCondsIter.hasNext()) {
-                            GenericValue quantityCond = (GenericValue) quantityCondsIter.next();
-                            productPriceRuleIds.add(quantityCond.getString("productPriceRuleId"));
-                        }
-                    }
-
-                    // by roleTypeId
-                    Collection roleTypeIdConds = delegator.findByAndCache("ProductPriceCond", UtilMisc.toMap("inputParamEnumId", "PRIP_ROLE_TYPE"));
-                    if (roleTypeIdConds != null && roleTypeIdConds.size() > 0) {
-                        Iterator roleTypeIdCondsIter = roleTypeIdConds.iterator();
-                        while (roleTypeIdCondsIter.hasNext()) {
-                            GenericValue roleTypeIdCond = (GenericValue) roleTypeIdCondsIter.next();
-                            productPriceRuleIds.add(roleTypeIdCond.getString("productPriceRuleId"));
-                        }
-                    }
-
-                    // TODO, not supported yet: by groupPartyId
-                    // TODO, not supported yet: by partyClassificationGroupId
-                    // later: (by partyClassificationTypeId)
-
-                    // by listPrice
-                    Collection listPriceConds = delegator.findByAndCache("ProductPriceCond", UtilMisc.toMap("inputParamEnumId", "PRIP_LIST_PRICE"));
-                    if (listPriceConds != null && listPriceConds.size() > 0) {
-                        Iterator listPriceCondsIter = listPriceConds.iterator();
-                        while (listPriceCondsIter.hasNext()) {
-                            GenericValue listPriceCond = (GenericValue) listPriceCondsIter.next();
-                            productPriceRuleIds.add(listPriceCond.getString("productPriceRuleId"));
-                        }
-                    }
-
-                    // ------- These are all of them that DO depend on the current inputs -------
+                List allProductPriceRules = makeProducePriceRuleList(delegator, optimizeForLargeRuleSet, productId, virtualProductId, prodCatalogId, productStoreGroupId, webSiteId, partyId, currencyUomId);
 
-                    // by productId
-                    Collection productIdConds = delegator.findByAndCache("ProductPriceCond", UtilMisc.toMap("inputParamEnumId", "PRIP_PRODUCT_ID", "condValue", productId));
-                    if (productIdConds != null && productIdConds.size() > 0) {
-                        Iterator productIdCondsIter = productIdConds.iterator();
-                        while (productIdCondsIter.hasNext()) {
-                            GenericValue productIdCond = (GenericValue) productIdCondsIter.next();
-                            productPriceRuleIds.add(productIdCond.getString("productPriceRuleId"));
-                        }
-                    }
-
-                    // by virtualProductId, if not null
-                    if (virtualProductId != null) {
-                        Collection virtualProductIdConds = delegator.findByAndCache("ProductPriceCond", UtilMisc.toMap("inputParamEnumId", "PRIP_PRODUCT_ID", "condValue", virtualProductId));
-                        if (virtualProductIdConds != null && virtualProductIdConds.size() > 0) {
-                            Iterator virtualProductIdCondsIter = virtualProductIdConds.iterator();
-                            while (virtualProductIdCondsIter.hasNext()) {
-                                GenericValue virtualProductIdCond = (GenericValue) virtualProductIdCondsIter.next();
-                                productPriceRuleIds.add(virtualProductIdCond.getString("productPriceRuleId"));
+                List quantityProductPriceRules = null;
+                List nonQuantityProductPriceRules = null;
+                if (findAllQuantityPrices) {
+                    // split into list with quantity conditions and list without, then iterate through each quantity cond one
+                    quantityProductPriceRules = FastList.newInstance();
+                    nonQuantityProductPriceRules = FastList.newInstance();
+                    Iterator productPriceRulesIter = allProductPriceRules.iterator();
+                    while (productPriceRulesIter.hasNext()) {
+                        GenericValue productPriceRule = (GenericValue) productPriceRulesIter.next();
+                        List productPriceCondList = delegator.findByAndCache("ProductPriceCond", UtilMisc.toMap("productPriceRuleId", productPriceRule.get("productPriceRuleId")));
+                        
+                        boolean foundQuantityInputParam = false;
+                        Iterator productPriceCondIter = productPriceCondList.iterator();
+                        while (productPriceCondIter.hasNext()) {
+                            GenericValue productPriceCond = (GenericValue) productPriceCondIter.next();
+                            if ("PRIP_QUANTITY".equals(productPriceCond.getString("inputParamEnumId"))) {
+                         foundQuantityInputParam = true;
+                         break;
                             }
                         }
-                    }
-
-                    // by prodCatalogId - which is optional in certain cases
-                    if (UtilValidate.isNotEmpty(prodCatalogId)) {
-                        Collection prodCatalogIdConds = delegator.findByAndCache("ProductPriceCond", UtilMisc.toMap("inputParamEnumId", "PRIP_PROD_CLG_ID", "condValue", prodCatalogId));
-                        if (prodCatalogIdConds != null && prodCatalogIdConds.size() > 0) {
-                            Iterator prodCatalogIdCondsIter = prodCatalogIdConds.iterator();
-                            while (prodCatalogIdCondsIter.hasNext()) {
-                                GenericValue prodCatalogIdCond = (GenericValue) prodCatalogIdCondsIter.next();
-                                productPriceRuleIds.add(prodCatalogIdCond.getString("productPriceRuleId"));
-                            }
-                        }
-                    }
-
-                    // by productStoreGroupId
-                    if (UtilValidate.isNotEmpty(productStoreGroupId)) {
-                        Collection storeGroupConds = delegator.findByAndCache("ProductPriceCond", UtilMisc.toMap("inputParamEnumId", "PRIP_PROD_SGRP_ID", "condValue", productStoreGroupId));
-                        if (storeGroupConds != null && storeGroupConds.size() > 0) {
-                            Iterator storeGroupCondsIter = storeGroupConds.iterator();
-                            while (storeGroupCondsIter.hasNext()) {
-                                GenericValue storeGroupCond = (GenericValue) storeGroupCondsIter.next();
-                                productPriceRuleIds.add(storeGroupCond.getString("productPriceRuleId"));
-                            }
+                        
+                        if (foundQuantityInputParam) {
+                            quantityProductPriceRules.add(productPriceRule);
+                        } else {
+                            nonQuantityProductPriceRules.add(productPriceRule);
                         }
-                    }
-
-                    // by webSiteId
-                    if (UtilValidate.isNotEmpty(webSiteId)) {
-                        Collection webSiteIdConds = delegator.findByAndCache("ProductPriceCond", UtilMisc.toMap("inputParamEnumId", "PRIP_WEBSITE_ID", "condValue", webSiteId));
-                        if (webSiteIdConds != null && webSiteIdConds.size() > 0) {
-                            Iterator webSiteIdCondsIter = webSiteIdConds.iterator();
-                            while (webSiteIdCondsIter.hasNext()) {
-                                GenericValue webSiteIdCond = (GenericValue) webSiteIdCondsIter.next();
-                                productPriceRuleIds.add(webSiteIdCond.getString("productPriceRuleId"));
-                            }
-                        }
-                    }
-
-                    // by partyId
-                    if (UtilValidate.isNotEmpty(partyId)) {
-                        Collection partyIdConds = delegator.findByAndCache("ProductPriceCond", UtilMisc.toMap("inputParamEnumId", "PRIP_PARTY_ID", "condValue", partyId));
-                        if (partyIdConds != null && partyIdConds.size() > 0) {
-                            Iterator partyIdCondsIter = partyIdConds.iterator();
-                            while (partyIdCondsIter.hasNext()) {
-                                GenericValue partyIdCond = (GenericValue) partyIdCondsIter.next();
-                                productPriceRuleIds.add(partyIdCond.getString("productPriceRuleId"));
-                            }
-                        }
-                    }
-
-                    // by currencyUomId
-                    Collection currencyUomIdConds = delegator.findByAndCache("ProductPriceCond", UtilMisc.toMap("inputParamEnumId", "PRIP_CURRENCY_UOMID", "condValue", currencyUomId));
-                    if (currencyUomIdConds != null && currencyUomIdConds.size() > 0) {
-                        Iterator currencyUomIdCondsIter = currencyUomIdConds.iterator();
-                        while (currencyUomIdCondsIter.hasNext()) {
-                            GenericValue currencyUomIdCond = (GenericValue) currencyUomIdCondsIter.next();
-                            productPriceRuleIds.add(currencyUomIdCond.getString("productPriceRuleId"));
-                        }
-                    }
-
-                    productPriceRules = new LinkedList();
-                    Iterator productPriceRuleIdsIter = productPriceRuleIds.iterator();
-                    while (productPriceRuleIdsIter.hasNext()) {
-                        String productPriceRuleId = (String) productPriceRuleIdsIter.next();
-                        GenericValue productPriceRule = delegator.findByPrimaryKeyCache("ProductPriceRule", UtilMisc.toMap("productPriceRuleId", productPriceRuleId));
-                        if (productPriceRule == null) continue;
-                        productPriceRules.add(productPriceRule);
-                    }
-                } else {
-                    // this would be nice, but we can't cache this so easily...
-                    // List pprExprs = UtilMisc.toList(new EntityExpr("thruDate", EntityOperator.EQUALS, null),
-                    // new EntityExpr("thruDate", EntityOperator.GREATER_THAN, UtilDateTime.nowTimestamp()));
-                    // productPriceRules = delegator.findByOr("ProductPriceRule", pprExprs);
-
-                    productPriceRules = delegator.findAllCache("ProductPriceRule");
-                    if (productPriceRules == null) productPriceRules = new LinkedList();
-                }
-
-                // ========= go through each price rule by id and eval all conditions =========
-                // utilTimer.timerString("Before eval rules", module);
-                int totalConds = 0;
-                int totalActions = 0;
-                int totalRules = 0;
-
-                Iterator productPriceRulesIter = productPriceRules.iterator();
-                while (productPriceRulesIter.hasNext()) {
-                    GenericValue productPriceRule = (GenericValue) productPriceRulesIter.next();
-                    String productPriceRuleId = productPriceRule.getString("productPriceRuleId");
-
-                    // check from/thru dates
-                    java.sql.Timestamp fromDate = productPriceRule.getTimestamp("fromDate");
-                    java.sql.Timestamp thruDate = productPriceRule.getTimestamp("thruDate");
-
-                    if (fromDate != null && fromDate.after(nowTimestamp)) {
-                        // hasn't started yet
-                        continue;
-                    }
-                    if (thruDate != null && thruDate.before(nowTimestamp)) {
-                        // already expired
-                        continue;
-                    }
-
-                    // check all conditions
-                    boolean allTrue = true;
-                    StringBuffer condsDescription = new StringBuffer();
-                    Collection productPriceConds = delegator.findByAndCache("ProductPriceCond", UtilMisc.toMap("productPriceRuleId", productPriceRuleId));
-                    Iterator productPriceCondsIter = UtilMisc.toIterator(productPriceConds);
-
-                    while (productPriceCondsIter != null && productPriceCondsIter.hasNext()) {
-                        GenericValue productPriceCond = (GenericValue) productPriceCondsIter.next();
-
-                        totalConds++;
-
-                        if (!checkPriceCondition(productPriceCond, productId, prodCatalogId, productStoreGroupId, webSiteId, partyId, new Double(quantity), listPrice, currencyUomId, delegator)) {
-                            // if there is a virtualProductId, try that given that this one has failed
-                            if (virtualProductId != null) {
-                                if (!checkPriceCondition(productPriceCond, virtualProductId, prodCatalogId, productStoreGroupId, webSiteId, partyId, new Double(quantity), listPrice, currencyUomId, delegator)) {
-                                    allTrue = false;
-                                    break;
-                                }
-                                // otherwise, okay, this one made it so carry on checking
-                            } else {
-                                allTrue = false;
-                                break;
-                            }
-                        }
-
-                        // add condsDescription string entry
-                        condsDescription.append("[");
-                        GenericValue inputParamEnum = productPriceCond.getRelatedOneCache("InputParamEnumeration");
-
-                        condsDescription.append(inputParamEnum.getString("enumCode"));
-                        // condsDescription.append(":");
-                        GenericValue operatorEnum = productPriceCond.getRelatedOneCache("OperatorEnumeration");
-
-                        condsDescription.append(operatorEnum.getString("description"));
-                        // condsDescription.append(":");
-                        condsDescription.append(productPriceCond.getString("condValue"));
-                        condsDescription.append("] ");
-                    }
-
-                    // add some info about the prices we are calculating from
-                    condsDescription.append("[list:");
-                    condsDescription.append(listPrice);
-                    condsDescription.append(";avgCost:");
-                    condsDescription.append(averageCost);
-                    condsDescription.append(";margin:");
-                    condsDescription.append(margin);
-                    condsDescription.append("] ");
-
-                    boolean foundFlatOverride = false;
-
-                    // if all true, perform all actions
-                    if (allTrue) {
-                        // check isSale
-                        if ("Y".equals(productPriceRule.getString("isSale"))) {
-                            isSale = true;
-                        }
-
-                        Collection productPriceActions = delegator.findByAndCache("ProductPriceAction", UtilMisc.toMap("productPriceRuleId", productPriceRuleId));
-                        Iterator productPriceActionsIter = UtilMisc.toIterator(productPriceActions);
-
-                        while (productPriceActionsIter != null && productPriceActionsIter.hasNext()) {
-                            GenericValue productPriceAction = (GenericValue) productPriceActionsIter.next();
-
-                            totalActions++;
-
-                            // yeah, finally here, perform the action, ie, modify the price
-                            double modifyAmount = 0;
-
-                            if ("PRICE_POD".equals(productPriceAction.getString("productPriceActionTypeId"))) {
-                                if (productPriceAction.get("amount") != null) {
-                                    modifyAmount = defaultPrice * (productPriceAction.getDouble("amount").doubleValue() / 100.0);
-                                }
-                            } else if ("PRICE_POL".equals(productPriceAction.getString("productPriceActionTypeId"))) {
-                                if (productPriceAction.get("amount") != null) {
-                                    modifyAmount = listPrice * (productPriceAction.getDouble("amount").doubleValue() / 100.0);
-                                }
-                            } else if ("PRICE_POAC".equals(productPriceAction.getString("productPriceActionTypeId"))) {
-                                if (productPriceAction.get("amount") != null) {
-                                    modifyAmount = averageCost * (productPriceAction.getDouble("amount").doubleValue() / 100.0);
-                                }
-                            } else if ("PRICE_POM".equals(productPriceAction.getString("productPriceActionTypeId"))) {
-                                if (productPriceAction.get("amount") != null) {
-                                    modifyAmount = margin * (productPriceAction.getDouble("amount").doubleValue() / 100.0);
-                                }
-                            } else if ("PRICE_FOL".equals(productPriceAction.getString("productPriceActionTypeId"))) {
-                                if (productPriceAction.get("amount") != null) {
-                                    modifyAmount = productPriceAction.getDouble("amount").doubleValue();
-                                }
-                            } else if ("PRICE_FLAT".equals(productPriceAction.getString("productPriceActionTypeId"))) {
-                                // this one is a bit different, break out of the loop because we now have our final price
-                                foundFlatOverride = true;
-                                if (productPriceAction.get("amount") != null) {
-                                    price = productPriceAction.getDouble("amount").doubleValue();
-                                } else {
-                                    Debug.logInfo("ProductPriceAction had null amount, using default price: " + defaultPrice + " for product with id " + productId, module);
-                                    price = defaultPrice;
-                                    isSale = false;                // reverse isSale flag, as this sale rule was actually not applied
-                                }
-                            } else if ("PRICE_PFLAT".equals(productPriceAction.getString("productPriceActionTypeId"))) {
-                                // this one is a bit different too, break out of the loop because we now have our final price
-                                foundFlatOverride = true;
-                                price = promoPrice;
-                                if (productPriceAction.get("amount") != null) {
-                                    price += productPriceAction.getDouble("amount").doubleValue();
-                                }
-                                if (price == 0.00) {
-                                    if (defaultPrice != 0.00) {
-                                        Debug.logInfo("PromoPrice and ProductPriceAction had null amount, using default price: " + defaultPrice + " for product with id " + productId, module);
-                                        price = defaultPrice;
-                                    } else if (listPrice != 0.00) {
-                                        Debug.logInfo("PromoPrice and ProductPriceAction had null amount and no default price was available, using list price: " + listPrice + " for product with id " + productId, module);
-                                        price = listPrice;
-                                    } else {
-                                        Debug.logError("PromoPrice and ProductPriceAction had null amount and no default or list price was available, so price is set to zero for product with id " + productId, module);
-                                        price = 0.00;
-                                    }
-                                    isSale = false;                // reverse isSale flag, as this sale rule was actually not applied
-                                }
-                            } else if ("PRICE_WFLAT".equals(productPriceAction.getString("productPriceActionTypeId"))) {
-                                // same as promo price but using the wholesale price instead
-                                foundFlatOverride = true;
-                                price = wholesalePrice;
-                                if (productPriceAction.get("amount") != null) {
-                                    price += productPriceAction.getDouble("amount").doubleValue();
-                                }
-                                if (price == 0.00) {
-                                    if (defaultPrice != 0.00) {
-                                        Debug.logInfo("WholesalePrice and ProductPriceAction had null amount, using default price: " + defaultPrice + " for product with id " + productId, module);
-                                        price = defaultPrice;
-                                    } else if (listPrice != 0.00) {
-                                        Debug.logInfo("WholesalePrice and ProductPriceAction had null amount and no default price was available, using list price: " + listPrice + " for product with id " + productId, module);
-                                        price = listPrice;
-                                    } else {
-                                        Debug.logError("WholesalePrice and ProductPriceAction had null amount and no default or list price was available, so price is set to zero for product with id " + productId, module);
-                                        price = 0.00;
-                                    }
-                                    isSale = false; // reverse isSale flag, as this sale rule was actually not applied
-                                }
-                            }
-
-                            // add a orderItemPriceInfo element too, without orderId or orderItemId
-                            StringBuffer priceInfoDescription = new StringBuffer();
-
-                            priceInfoDescription.append(condsDescription.toString());
-                            priceInfoDescription.append("[type:");
-                            priceInfoDescription.append(productPriceAction.getString("productPriceActionTypeId"));
-                            priceInfoDescription.append("]");
-
-                            GenericValue orderItemPriceInfo = delegator.makeValue("OrderItemPriceInfo", null);
-
-                            orderItemPriceInfo.set("productPriceRuleId", productPriceAction.get("productPriceRuleId"));
-                            orderItemPriceInfo.set("productPriceActionSeqId", productPriceAction.get("productPriceActionSeqId"));
-                            orderItemPriceInfo.set("modifyAmount", new Double(modifyAmount));
-                            // make sure description is <= than 250 chars
-                            String priceInfoDescriptionString = priceInfoDescription.toString();
-
-                            if (priceInfoDescriptionString.length() > 250) {
-                                priceInfoDescriptionString = priceInfoDescriptionString.substring(0, 250);
-                            }
-                            orderItemPriceInfo.set("description", priceInfoDescriptionString);
-                            orderItemPriceInfos.add(orderItemPriceInfo);
-
-                            if (foundFlatOverride) {
-                                break;
-                            } else {
-                                price += modifyAmount;
-                            }
-                        }
-                    }
-
-                    totalRules++;
-
-                    if (foundFlatOverride) {
-                        break;
-                    }
-                }
-
-                if (Debug.verboseOn()) {
-                    Debug.logVerbose("Unchecked Calculated price: " + price, module);
-                    Debug.logVerbose("PriceInfo:", module);
-                    Iterator orderItemPriceInfosIter = orderItemPriceInfos.iterator();
-                    while (orderItemPriceInfosIter.hasNext()) {
-                        GenericValue orderItemPriceInfo = (GenericValue) orderItemPriceInfosIter.next();
-
-                        Debug.logVerbose(" --- " + orderItemPriceInfo.toString(), module);
-                    }
+                    }                    
                 }
+                
 
-                // if no actions were run on the list price, then use the default price
-                if (totalActions == 0) {
-                    price = defaultPrice;
-                    // here we will leave validPriceFound as it was originally set for the defaultPrice since that is what we are setting the price to...
+                if (findAllQuantityPrices) {
+                    List allQuantityPrices = FastList.newInstance();
+                    
+                    // if findAllQuantityPrices then iterate through quantityProductPriceRules
+                    // foreach create an entry in the out list and eval that rule and all nonQuantityProductPriceRules rather than a single rule
+                    Iterator quantityProductPriceRuleIter = quantityProductPriceRules.iterator();
+                    while (quantityProductPriceRuleIter.hasNext()) {
+                 GenericValue quantityProductPriceRule = (GenericValue) quantityProductPriceRuleIter.next();
+                
+                 List ruleListToUse = FastList.newInstance();
+                 ruleListToUse.add(quantityProductPriceRule);
+                 ruleListToUse.addAll(nonQuantityProductPriceRules);
+                
+                        Map quantCalcResults = calcPriceResultFromRules(ruleListToUse, listPriceDbl.doubleValue(), defaultPrice, promoPrice,
+                            wholesalePrice, maximumPriceValue, minimumPriceValue, validPriceFound,
+                        averageCostValue, productId, virtualProductId, prodCatalogId, productStoreGroupId,
+                        webSiteId, partyId, null, currencyUomId, delegator, nowTimestamp);
+                        Map quantErrorResult = addGeneralResults(quantCalcResults, competitivePriceValue, specialPromoPriceValue, productStore,
+                            checkIncludeVat, currencyUomId, productId, quantity, partyId, dispatcher);
+                        if (quantErrorResult != null) return quantErrorResult;
+                        
+                        // also add the quantityProductPriceRule to the Map so it can be used for quantity break information
+                        quantCalcResults.put("quantityProductPriceRule", quantityProductPriceRule);
+                        
+                        allQuantityPrices.add(quantCalcResults);
+                    }
+                    result.put("allQuantityPrices", allQuantityPrices);
+
+                    // use a quantity 1 to get the main price, then fill in the quantity break prices
+                    Map calcResults = calcPriceResultFromRules(allProductPriceRules, listPriceDbl.doubleValue(), defaultPrice, promoPrice,
+                        wholesalePrice, maximumPriceValue, minimumPriceValue, validPriceFound,
+                    averageCostValue, productId, virtualProductId, prodCatalogId, productStoreGroupId,
+                    webSiteId, partyId, new Double(1.0), currencyUomId, delegator, nowTimestamp);
+                    result.putAll(calcResults);
+                    Map errorResult = addGeneralResults(result, competitivePriceValue, specialPromoPriceValue, productStore,
+                        checkIncludeVat, currencyUomId, productId, quantity, partyId, dispatcher);
+                    if (errorResult != null) return errorResult;
                 } else {
-                    // at least one price rule action was found, so we will consider it valid
-                    validPriceFound = true;
+                    Map calcResults = calcPriceResultFromRules(allProductPriceRules, listPriceDbl.doubleValue(), defaultPrice, promoPrice,
+                        wholesalePrice, maximumPriceValue, minimumPriceValue, validPriceFound,
+                    averageCostValue, productId, virtualProductId, prodCatalogId, productStoreGroupId,
+                    webSiteId, partyId, new Double(quantity), currencyUomId, delegator, nowTimestamp);
+                    result.putAll(calcResults);
+                    Map errorResult = addGeneralResults(result, competitivePriceValue, specialPromoPriceValue, productStore,
+                        checkIncludeVat, currencyUomId, productId, quantity, partyId, dispatcher);
+                    if (errorResult != null) return errorResult;
                 }
-
-                // ========= ensure calculated price is not below minSalePrice or above maxSalePrice =========
-                Double maxSellPrice = maximumPriceValue != null ? maximumPriceValue.getDouble("price") : null;
-                if (maxSellPrice != null && price > maxSellPrice.doubleValue()) {
-                    price = maxSellPrice.doubleValue();
-                }
-                // min price second to override max price, safety net
-                Double minSellPrice = minimumPriceValue != null ? minimumPriceValue.getDouble("price") : null;
-                if (minSellPrice != null && price < minSellPrice.doubleValue()) {
-                    price = minSellPrice.doubleValue();
-                    // since we have found a minimum price that has overriden a the defaultPrice, even if no valid one was found, we will consider it as if one had been...
-                    validPriceFound = true;
-                }
-
-                if (Debug.verboseOn()) Debug.logVerbose("Final Calculated price: " + price + ", rules: " + totalRules + ", conds: " + totalConds + ", actions: " + totalActions, module);
-
-                result.put("basePrice", new Double(price));
-                result.put("price", new Double(price));
-                result.put("listPrice", new Double(listPrice));
-                result.put("defaultPrice", new Double(defaultPrice));
-                result.put("averageCost", new Double(averageCost));
             } catch (GenericEntityException e) {
                 Debug.logError(e, "Error getting rules from the database while calculating price", module);
                 return ServiceUtil.returnError("Error getting rules from the database while calculating price: " + e.toString());
             }
         }
 
+        // utilTimer.timerString("Finished price calc [productId=" + productId + "]", module);
+        return result;
+    }
+    
+    public static Map addGeneralResults(Map result, GenericValue competitivePriceValue, GenericValue specialPromoPriceValue, GenericValue productStore,
+    String checkIncludeVat, String currencyUomId, String productId, double quantity, String partyId, LocalDispatcher dispatcher) {
         result.put("competitivePrice", competitivePriceValue != null ? competitivePriceValue.getDouble("price") : null);
         result.put("specialPromoPrice", specialPromoPriceValue != null ? specialPromoPriceValue.getDouble("price") : null);
-        result.put("orderItemPriceInfos", orderItemPriceInfos);
-        result.put("isSale", new Boolean(isSale));
-        result.put("validPriceFound", new Boolean(validPriceFound));
         result.put("currencyUsed", currencyUomId);
 
         // okay, now we have the calculated price, see if we should add in tax and if so do it
         if ("Y".equals(checkIncludeVat) && productStore != null && "Y".equals(productStore.getString("showPricesWithVatTax"))) {
-            Map calcTaxForDisplayContext = UtilMisc.toMap("productStoreId", productStoreId,
+            Map calcTaxForDisplayContext = UtilMisc.toMap("productStoreId", productStore.get("productStoreId"),
                     "productId", productId, "quantity", new BigDecimal(quantity),
                     "basePrice", new BigDecimal(((Double) result.get("price")).doubleValue()));
             if (UtilValidate.isNotEmpty(partyId)) {
@@ -965,9 +645,438 @@
                 return ServiceUtil.returnError(errMsg);
             }
         }
+        
+        return null;
+    }
+    
+    public static List makeProducePriceRuleList(GenericDelegator delegator, boolean optimizeForLargeRuleSet, String productId, String virtualProductId, String prodCatalogId, String productStoreGroupId, String webSiteId, String partyId, String currencyUomId) throws GenericEntityException {
+        List productPriceRules = null;
 
-        // utilTimer.timerString("Finished price calc [productId=" + productId + "]", module);
-        return result;
+        // At this point we have two options: optimize for large ruleset, or optimize for small ruleset
+        // NOTE: This only effects the way that the rules to be evaluated are selected.
+        // For large rule sets we can do a cached pre-filter to limit the rules that need to be evaled for a specific product.
+        // Genercally I don't think that rule sets will get that big though, so the default is optimize for smaller rule set.
+        if (optimizeForLargeRuleSet) {
+            // ========= find all rules that must be run for each input type; this is kind of like a pre-filter to slim down the rules to run =========
+            // utilTimer.timerString("Before create rule id list", module);
+            TreeSet productPriceRuleIds = new TreeSet();
+
+            // ------- These are all of the conditions that DON'T depend on the current inputs -------
+
+            // by productCategoryId
+            // for we will always include any rules that go by category, shouldn't be too many to iterate through each time and will save on cache entries
+            // note that we always want to put the category, quantity, etc ones that find all rules with these conditions in separate cache lists so that they can be easily cleared
+            Collection productCategoryIdConds = delegator.findByAndCache("ProductPriceCond", UtilMisc.toMap("inputParamEnumId", "PRIP_PROD_CAT_ID"));
+            if (productCategoryIdConds != null && productCategoryIdConds.size() > 0) {
+                Iterator productCategoryIdCondsIter = productCategoryIdConds.iterator();
+                while (productCategoryIdCondsIter.hasNext()) {
+                    GenericValue productCategoryIdCond = (GenericValue) productCategoryIdCondsIter.next();
+                    productPriceRuleIds.add(productCategoryIdCond.getString("productPriceRuleId"));
+                }
+            }
+
+            // by quantity -- should we really do this one, ie is it necessary?
+            // we could say that all rules with quantity on them must have one of these other values
+            // but, no we'll do it the other way, any that have a quantity will always get compared
+            Collection quantityConds = delegator.findByAndCache("ProductPriceCond", UtilMisc.toMap("inputParamEnumId", "PRIP_QUANTITY"));
+            if (quantityConds != null && quantityConds.size() > 0) {
+                Iterator quantityCondsIter = quantityConds.iterator();
+                while (quantityCondsIter.hasNext()) {
+                    GenericValue quantityCond = (GenericValue) quantityCondsIter.next();
+                    productPriceRuleIds.add(quantityCond.getString("productPriceRuleId"));
+                }
+            }
+
+            // by roleTypeId
+            Collection roleTypeIdConds = delegator.findByAndCache("ProductPriceCond", UtilMisc.toMap("inputParamEnumId", "PRIP_ROLE_TYPE"));
+            if (roleTypeIdConds != null && roleTypeIdConds.size() > 0) {
+                Iterator roleTypeIdCondsIter = roleTypeIdConds.iterator();
+                while (roleTypeIdCondsIter.hasNext()) {
+                    GenericValue roleTypeIdCond = (GenericValue) roleTypeIdCondsIter.next();
+                    productPriceRuleIds.add(roleTypeIdCond.getString("productPriceRuleId"));
+                }
+            }
+
+            // TODO, not supported yet: by groupPartyId
+            // TODO, not supported yet: by partyClassificationGroupId
+            // later: (by partyClassificationTypeId)
+
+            // by listPrice
+            Collection listPriceConds = delegator.findByAndCache("ProductPriceCond", UtilMisc.toMap("inputParamEnumId", "PRIP_LIST_PRICE"));
+            if (listPriceConds != null && listPriceConds.size() > 0) {
+                Iterator listPriceCondsIter = listPriceConds.iterator();
+                while (listPriceCondsIter.hasNext()) {
+                    GenericValue listPriceCond = (GenericValue) listPriceCondsIter.next();
+                    productPriceRuleIds.add(listPriceCond.getString("productPriceRuleId"));
+                }
+            }
+
+            // ------- These are all of them that DO depend on the current inputs -------
+
+            // by productId
+            Collection productIdConds = delegator.findByAndCache("ProductPriceCond", UtilMisc.toMap("inputParamEnumId", "PRIP_PRODUCT_ID", "condValue", productId));
+            if (productIdConds != null && productIdConds.size() > 0) {
+                Iterator productIdCondsIter = productIdConds.iterator();
+                while (productIdCondsIter.hasNext()) {
+                    GenericValue productIdCond = (GenericValue) productIdCondsIter.next();
+                    productPriceRuleIds.add(productIdCond.getString("productPriceRuleId"));
+                }
+            }
+
+            // by virtualProductId, if not null
+            if (virtualProductId != null) {
+                Collection virtualProductIdConds = delegator.findByAndCache("ProductPriceCond", UtilMisc.toMap("inputParamEnumId", "PRIP_PRODUCT_ID", "condValue", virtualProductId));
+                if (virtualProductIdConds != null && virtualProductIdConds.size() > 0) {
+                    Iterator virtualProductIdCondsIter = virtualProductIdConds.iterator();
+                    while (virtualProductIdCondsIter.hasNext()) {
+                        GenericValue virtualProductIdCond = (GenericValue) virtualProductIdCondsIter.next();
+                        productPriceRuleIds.add(virtualProductIdCond.getString("productPriceRuleId"));
+                    }
+                }
+            }
+
+            // by prodCatalogId - which is optional in certain cases
+            if (UtilValidate.isNotEmpty(prodCatalogId)) {
+                Collection prodCatalogIdConds = delegator.findByAndCache("ProductPriceCond", UtilMisc.toMap("inputParamEnumId", "PRIP_PROD_CLG_ID", "condValue", prodCatalogId));
+                if (prodCatalogIdConds != null && prodCatalogIdConds.size() > 0) {
+                    Iterator prodCatalogIdCondsIter = prodCatalogIdConds.iterator();
+                    while (prodCatalogIdCondsIter.hasNext()) {
+                        GenericValue prodCatalogIdCond = (GenericValue) prodCatalogIdCondsIter.next();
+                        productPriceRuleIds.add(prodCatalogIdCond.getString("productPriceRuleId"));
+                    }
+                }
+            }
+
+            // by productStoreGroupId
+            if (UtilValidate.isNotEmpty(productStoreGroupId)) {
+                Collection storeGroupConds = delegator.findByAndCache("ProductPriceCond", UtilMisc.toMap("inputParamEnumId", "PRIP_PROD_SGRP_ID", "condValue", productStoreGroupId));
+                if (storeGroupConds != null && storeGroupConds.size() > 0) {
+                    Iterator storeGroupCondsIter = storeGroupConds.iterator();
+                    while (storeGroupCondsIter.hasNext()) {
+                        GenericValue storeGroupCond = (GenericValue) storeGroupCondsIter.next();
+                        productPriceRuleIds.add(storeGroupCond.getString("productPriceRuleId"));
+                    }
+                }
+            }
+
+            // by webSiteId
+            if (UtilValidate.isNotEmpty(webSiteId)) {
+                Collection webSiteIdConds = delegator.findByAndCache("ProductPriceCond", UtilMisc.toMap("inputParamEnumId", "PRIP_WEBSITE_ID", "condValue", webSiteId));
+                if (webSiteIdConds != null && webSiteIdConds.size() > 0) {
+                    Iterator webSiteIdCondsIter = webSiteIdConds.iterator();
+                    while (webSiteIdCondsIter.hasNext()) {
+                        GenericValue webSiteIdCond = (GenericValue) webSiteIdCondsIter.next();
+                        productPriceRuleIds.add(webSiteIdCond.getString("productPriceRuleId"));
+                    }
+                }
+            }
+
+            // by partyId
+            if (UtilValidate.isNotEmpty(partyId)) {
+                Collection partyIdConds = delegator.findByAndCache("ProductPriceCond", UtilMisc.toMap("inputParamEnumId", "PRIP_PARTY_ID", "condValue", partyId));
+                if (partyIdConds != null && partyIdConds.size() > 0) {
+                    Iterator partyIdCondsIter = partyIdConds.iterator();
+                    while (partyIdCondsIter.hasNext()) {
+                        GenericValue partyIdCond = (GenericValue) partyIdCondsIter.next();
+                        productPriceRuleIds.add(partyIdCond.getString("productPriceRuleId"));
+                    }
+                }
+            }
+
+            // by currencyUomId
+            Collection currencyUomIdConds = delegator.findByAndCache("ProductPriceCond", UtilMisc.toMap("inputParamEnumId", "PRIP_CURRENCY_UOMID", "condValue", currencyUomId));
+            if (currencyUomIdConds != null && currencyUomIdConds.size() > 0) {
+                Iterator currencyUomIdCondsIter = currencyUomIdConds.iterator();
+                while (currencyUomIdCondsIter.hasNext()) {
+                    GenericValue currencyUomIdCond = (GenericValue) currencyUomIdCondsIter.next();
+                    productPriceRuleIds.add(currencyUomIdCond.getString("productPriceRuleId"));
+                }
+            }
+
+            productPriceRules = FastList.newInstance();
+            Iterator productPriceRuleIdsIter = productPriceRuleIds.iterator();
+            while (productPriceRuleIdsIter.hasNext()) {
+                String productPriceRuleId = (String) productPriceRuleIdsIter.next();
+                GenericValue productPriceRule = delegator.findByPrimaryKeyCache("ProductPriceRule", UtilMisc.toMap("productPriceRuleId", productPriceRuleId));
+                if (productPriceRule == null) continue;
+                productPriceRules.add(productPriceRule);
+            }
+        } else {
+            // this would be nice, but we can't cache this so easily...
+            // List pprExprs = UtilMisc.toList(new EntityExpr("thruDate", EntityOperator.EQUALS, null),
+            // new EntityExpr("thruDate", EntityOperator.GREATER_THAN, UtilDateTime.nowTimestamp()));
+            // productPriceRules = delegator.findByOr("ProductPriceRule", pprExprs);
+
+            productPriceRules = delegator.findAllCache("ProductPriceRule");
+            if (productPriceRules == null) productPriceRules = new LinkedList();
+        }
+        
+        return productPriceRules;
+    }
+    
+    public static Map calcPriceResultFromRules(List productPriceRules, double listPrice, double defaultPrice, double promoPrice,
+    double wholesalePrice, GenericValue maximumPriceValue, GenericValue minimumPriceValue, boolean validPriceFound,
+    GenericValue averageCostValue, String productId, String virtualProductId, String prodCatalogId, String productStoreGroupId,
+    String webSiteId, String partyId, Double quantity, String currencyUomId, GenericDelegator delegator, Timestamp nowTimestamp) throws GenericEntityException{
+
+ Map calcResults = FastMap.newInstance();
+
+ List orderItemPriceInfos = FastList.newInstance();
+        boolean isSale = false;
+
+        // ========= go through each price rule by id and eval all conditions =========
+        // utilTimer.timerString("Before eval rules", module);
+        int totalConds = 0;
+        int totalActions = 0;
+        int totalRules = 0;
+
+        // get some of the base values to calculate with
+        double averageCost = (averageCostValue != null && averageCostValue.get("price") != null) ? averageCostValue.getDouble("price").doubleValue() : listPrice;
+        double margin = listPrice - averageCost;
+
+        // calculate running sum based on listPrice and rules found
+        double price = listPrice;
+        
+        Iterator productPriceRulesIter = productPriceRules.iterator();
+        while (productPriceRulesIter.hasNext()) {
+            GenericValue productPriceRule = (GenericValue) productPriceRulesIter.next();
+            String productPriceRuleId = productPriceRule.getString("productPriceRuleId");
+
+            // check from/thru dates
+            java.sql.Timestamp fromDate = productPriceRule.getTimestamp("fromDate");
+            java.sql.Timestamp thruDate = productPriceRule.getTimestamp("thruDate");
+
+            if (fromDate != null && fromDate.after(nowTimestamp)) {
+                // hasn't started yet
+                continue;
+            }
+            if (thruDate != null && thruDate.before(nowTimestamp)) {
+                // already expired
+                continue;
+            }
+
+            // check all conditions
+            boolean allTrue = true;
+            StringBuffer condsDescription = new StringBuffer();
+            List productPriceConds = delegator.findByAndCache("ProductPriceCond", UtilMisc.toMap("productPriceRuleId", productPriceRuleId));
+            Iterator productPriceCondsIter = UtilMisc.toIterator(productPriceConds);
+
+            while (productPriceCondsIter != null && productPriceCondsIter.hasNext()) {
+                GenericValue productPriceCond = (GenericValue) productPriceCondsIter.next();
+
+                totalConds++;
+
+                if (!checkPriceCondition(productPriceCond, productId, prodCatalogId, productStoreGroupId, webSiteId, partyId, quantity, listPrice, currencyUomId, delegator)) {
+                    // if there is a virtualProductId, try that given that this one has failed
+                    if (virtualProductId != null) {
+                        if (!checkPriceCondition(productPriceCond, virtualProductId, prodCatalogId, productStoreGroupId, webSiteId, partyId, quantity, listPrice, currencyUomId, delegator)) {
+                            allTrue = false;
+                            break;
+                        }
+                        // otherwise, okay, this one made it so carry on checking
+                    } else {
+                        allTrue = false;
+                        break;
+                    }
+                }
+
+                // add condsDescription string entry
+                condsDescription.append("[");
+                GenericValue inputParamEnum = productPriceCond.getRelatedOneCache("InputParamEnumeration");
+
+                condsDescription.append(inputParamEnum.getString("enumCode"));
+                // condsDescription.append(":");
+                GenericValue operatorEnum = productPriceCond.getRelatedOneCache("OperatorEnumeration");
+
+                condsDescription.append(operatorEnum.getString("description"));
+                // condsDescription.append(":");
+                condsDescription.append(productPriceCond.getString("condValue"));
+                condsDescription.append("] ");
+            }
+
+            // add some info about the prices we are calculating from
+            condsDescription.append("[list:");
+            condsDescription.append(listPrice);
+            condsDescription.append(";avgCost:");
+            condsDescription.append(averageCost);
+            condsDescription.append(";margin:");
+            condsDescription.append(margin);
+            condsDescription.append("] ");
+
+            boolean foundFlatOverride = false;
+
+            // if all true, perform all actions
+            if (allTrue) {
+                // check isSale
+                if ("Y".equals(productPriceRule.getString("isSale"))) {
+                    isSale = true;
+                }
+
+                Collection productPriceActions = delegator.findByAndCache("ProductPriceAction", UtilMisc.toMap("productPriceRuleId", productPriceRuleId));
+                Iterator productPriceActionsIter = UtilMisc.toIterator(productPriceActions);
+
+                while (productPriceActionsIter != null && productPriceActionsIter.hasNext()) {
+                    GenericValue productPriceAction = (GenericValue) productPriceActionsIter.next();
+
+                    totalActions++;
+
+                    // yeah, finally here, perform the action, ie, modify the price
+                    double modifyAmount = 0;
+
+                    if ("PRICE_POD".equals(productPriceAction.getString("productPriceActionTypeId"))) {
+                        if (productPriceAction.get("amount") != null) {
+                            modifyAmount = defaultPrice * (productPriceAction.getDouble("amount").doubleValue() / 100.0);
+                        }
+                    } else if ("PRICE_POL".equals(productPriceAction.getString("productPriceActionTypeId"))) {
+                        if (productPriceAction.get("amount") != null) {
+                            modifyAmount = listPrice * (productPriceAction.getDouble("amount").doubleValue() / 100.0);
+                        }
+                    } else if ("PRICE_POAC".equals(productPriceAction.getString("productPriceActionTypeId"))) {
+                        if (productPriceAction.get("amount") != null) {
+                            modifyAmount = averageCost * (productPriceAction.getDouble("amount").doubleValue() / 100.0);
+                        }
+                    } else if ("PRICE_POM".equals(productPriceAction.getString("productPriceActionTypeId"))) {
+                        if (productPriceAction.get("amount") != null) {
+                            modifyAmount = margin * (productPriceAction.getDouble("amount").doubleValue() / 100.0);
+                        }
+                    } else if ("PRICE_FOL".equals(productPriceAction.getString("productPriceActionTypeId"))) {
+                        if (productPriceAction.get("amount") != null) {
+                            modifyAmount = productPriceAction.getDouble("amount").doubleValue();
+                        }
+                    } else if ("PRICE_FLAT".equals(productPriceAction.getString("productPriceActionTypeId"))) {
+                        // this one is a bit different, break out of the loop because we now have our final price
+                        foundFlatOverride = true;
+                        if (productPriceAction.get("amount") != null) {
+                            price = productPriceAction.getDouble("amount").doubleValue();
+                        } else {
+                            Debug.logInfo("ProductPriceAction had null amount, using default price: " + defaultPrice + " for product with id " + productId, module);
+                            price = defaultPrice;
+                            isSale = false;                // reverse isSale flag, as this sale rule was actually not applied
+                        }
+                    } else if ("PRICE_PFLAT".equals(productPriceAction.getString("productPriceActionTypeId"))) {
+                        // this one is a bit different too, break out of the loop because we now have our final price
+                        foundFlatOverride = true;
+                        price = promoPrice;
+                        if (productPriceAction.get("amount") != null) {
+                            price += productPriceAction.getDouble("amount").doubleValue();
+                        }
+                        if (price == 0.00) {
+                            if (defaultPrice != 0.00) {
+                                Debug.logInfo("PromoPrice and ProductPriceAction had null amount, using default price: " + defaultPrice + " for product with id " + productId, module);
+                                price = defaultPrice;
+                            } else if (listPrice != 0.00) {
+                                Debug.logInfo("PromoPrice and ProductPriceAction had null amount and no default price was available, using list price: " + listPrice + " for product with id " + productId, module);
+                                price = listPrice;
+                            } else {
+                                Debug.logError("PromoPrice and ProductPriceAction had null amount and no default or list price was available, so price is set to zero for product with id " + productId, module);
+                                price = 0.00;
+                            }
+                            isSale = false;                // reverse isSale flag, as this sale rule was actually not applied
+                        }
+                    } else if ("PRICE_WFLAT".equals(productPriceAction.getString("productPriceActionTypeId"))) {
+                        // same as promo price but using the wholesale price instead
+                        foundFlatOverride = true;
+                        price = wholesalePrice;
+