Source Code Cross Referenced for ProductPromoWorker.java in  » ERP-CRM-Financial » ofbiz » org » ofbiz » order » shoppingcart » product » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » ERP CRM Financial » ofbiz » org.ofbiz.order.shoppingcart.product 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*******************************************************************************
0002:         * Licensed to the Apache Software Foundation (ASF) under one
0003:         * or more contributor license agreements.  See the NOTICE file
0004:         * distributed with this work for additional information
0005:         * regarding copyright ownership.  The ASF licenses this file
0006:         * to you under the Apache License, Version 2.0 (the
0007:         * "License"); you may not use this file except in compliance
0008:         * with the License.  You may obtain a copy of the License at
0009:         * 
0010:         * http://www.apache.org/licenses/LICENSE-2.0
0011:         * 
0012:         * Unless required by applicable law or agreed to in writing,
0013:         * software distributed under the License is distributed on an
0014:         * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
0015:         * KIND, either express or implied.  See the License for the
0016:         * specific language governing permissions and limitations
0017:         * under the License.
0018:         *******************************************************************************/package org.ofbiz.order.shoppingcart.product;
0019:
0020:        import java.sql.Timestamp;
0021:        import java.util.ArrayList;
0022:        import java.util.HashMap;
0023:        import java.util.HashSet;
0024:        import java.util.Iterator;
0025:        import java.util.List;
0026:        import java.util.Locale;
0027:        import java.util.Map;
0028:        import java.util.Set;
0029:
0030:        import javax.servlet.ServletRequest;
0031:        import javax.servlet.http.HttpServletRequest;
0032:
0033:        import javolution.util.FastList;
0034:
0035:        import org.ofbiz.base.util.Debug;
0036:        import org.ofbiz.base.util.GeneralException;
0037:        import org.ofbiz.base.util.UtilDateTime;
0038:        import org.ofbiz.base.util.UtilMisc;
0039:        import org.ofbiz.base.util.UtilProperties;
0040:        import org.ofbiz.base.util.UtilValidate;
0041:        import org.ofbiz.entity.GenericDelegator;
0042:        import org.ofbiz.entity.GenericEntityException;
0043:        import org.ofbiz.entity.GenericValue;
0044:        import org.ofbiz.entity.condition.EntityCondition;
0045:        import org.ofbiz.entity.condition.EntityConditionList;
0046:        import org.ofbiz.entity.condition.EntityExpr;
0047:        import org.ofbiz.entity.condition.EntityOperator;
0048:        import org.ofbiz.entity.util.EntityUtil;
0049:        import org.ofbiz.order.shoppingcart.CartItemModifyException;
0050:        import org.ofbiz.order.shoppingcart.ShoppingCart;
0051:        import org.ofbiz.order.shoppingcart.ShoppingCartEvents;
0052:        import org.ofbiz.order.shoppingcart.ShoppingCartItem;
0053:        import org.ofbiz.product.product.ProductContentWrapper;
0054:        import org.ofbiz.product.product.ProductSearch;
0055:        import org.ofbiz.service.GenericServiceException;
0056:        import org.ofbiz.service.LocalDispatcher;
0057:        import org.ofbiz.service.ModelService;
0058:        import org.ofbiz.service.ServiceUtil;
0059:
0060:        /**
0061:         * ProductPromoWorker - Worker class for catalog/product promotion related functionality
0062:         */
0063:        public class ProductPromoWorker {
0064:
0065:            public static final String module = ProductPromoWorker.class
0066:                    .getName();
0067:            public static final String resource_error = "OrderErrorUiLabels";
0068:
0069:            public static List getStoreProductPromos(
0070:                    GenericDelegator delegator, LocalDispatcher dispatcher,
0071:                    ServletRequest request) {
0072:                List productPromos = FastList.newInstance();
0073:                Timestamp nowTimestamp = UtilDateTime.nowTimestamp();
0074:
0075:                // get the ShoppingCart out of the session.
0076:                HttpServletRequest req = null;
0077:                ShoppingCart cart = null;
0078:                try {
0079:                    req = (HttpServletRequest) request;
0080:                    cart = ShoppingCartEvents.getCartObject(req);
0081:                } catch (ClassCastException cce) {
0082:                    Debug
0083:                            .logError(
0084:                                    "Not a HttpServletRequest, no shopping cart found.",
0085:                                    module);
0086:                    return null;
0087:                } catch (IllegalArgumentException e) {
0088:                    Debug.logError(e, module);
0089:                    return null;
0090:                }
0091:
0092:                boolean condResult = true;
0093:
0094:                try {
0095:                    String productStoreId = cart.getProductStoreId();
0096:                    GenericValue productStore = null;
0097:                    try {
0098:                        productStore = delegator.findByPrimaryKeyCache(
0099:                                "ProductStore", UtilMisc.toMap(
0100:                                        "productStoreId", productStoreId));
0101:                    } catch (GenericEntityException e) {
0102:                        Debug.logError(e, "Error looking up store with id "
0103:                                + productStoreId, module);
0104:                    }
0105:                    if (productStore == null) {
0106:                        Debug
0107:                                .logWarning(
0108:                                        UtilProperties
0109:                                                .getMessage(
0110:                                                        resource_error,
0111:                                                        "OrderNoStoreFoundWithIdNotDoingPromotions",
0112:                                                        UtilMisc
0113:                                                                .toMap(
0114:                                                                        "productStoreId",
0115:                                                                        productStoreId),
0116:                                                        cart.getLocale()),
0117:                                        module);
0118:                        return productPromos;
0119:                    }
0120:
0121:                    if (productStore != null) {
0122:                        Iterator productStorePromoAppls = UtilMisc
0123:                                .toIterator(EntityUtil
0124:                                        .filterByDate(
0125:                                                productStore
0126:                                                        .getRelatedCache(
0127:                                                                "ProductStorePromoAppl",
0128:                                                                UtilMisc
0129:                                                                        .toMap(
0130:                                                                                "productStoreId",
0131:                                                                                productStoreId),
0132:                                                                UtilMisc
0133:                                                                        .toList("sequenceNum")),
0134:                                                true));
0135:                        while (productStorePromoAppls != null
0136:                                && productStorePromoAppls.hasNext()) {
0137:                            GenericValue productStorePromoAppl = (GenericValue) productStorePromoAppls
0138:                                    .next();
0139:                            GenericValue productPromo = productStorePromoAppl
0140:                                    .getRelatedOneCache("ProductPromo");
0141:                            List productPromoRules = productPromo
0142:                                    .getRelatedCache("ProductPromoRule", null,
0143:                                            null);
0144:
0145:                            if (productPromoRules != null) {
0146:                                Iterator promoRulesItr = productPromoRules
0147:                                        .iterator();
0148:
0149:                                while (condResult && promoRulesItr != null
0150:                                        && promoRulesItr.hasNext()) {
0151:                                    GenericValue promoRule = (GenericValue) promoRulesItr
0152:                                            .next();
0153:                                    Iterator productPromoConds = UtilMisc
0154:                                            .toIterator(promoRule
0155:                                                    .getRelatedCache(
0156:                                                            "ProductPromoCond",
0157:                                                            null,
0158:                                                            UtilMisc
0159:                                                                    .toList("productPromoCondSeqId")));
0160:
0161:                                    while (condResult
0162:                                            && productPromoConds != null
0163:                                            && productPromoConds.hasNext()) {
0164:                                        GenericValue productPromoCond = (GenericValue) productPromoConds
0165:                                                .next();
0166:
0167:                                        // evaluate the party related conditions; so we don't show the promo if it doesn't apply.
0168:                                        if ("PPIP_PARTY_ID"
0169:                                                .equals(productPromoCond
0170:                                                        .getString("inputParamEnumId"))) {
0171:                                            condResult = checkCondition(
0172:                                                    productPromoCond, cart,
0173:                                                    delegator, dispatcher,
0174:                                                    nowTimestamp);
0175:                                        } else if ("PPIP_PARTY_GRP_MEM"
0176:                                                .equals(productPromoCond
0177:                                                        .getString("inputParamEnumId"))) {
0178:                                            condResult = checkCondition(
0179:                                                    productPromoCond, cart,
0180:                                                    delegator, dispatcher,
0181:                                                    nowTimestamp);
0182:                                        } else if ("PPIP_PARTY_CLASS"
0183:                                                .equals(productPromoCond
0184:                                                        .getString("inputParamEnumId"))) {
0185:                                            condResult = checkCondition(
0186:                                                    productPromoCond, cart,
0187:                                                    delegator, dispatcher,
0188:                                                    nowTimestamp);
0189:                                        } else if ("PPIP_ROLE_TYPE"
0190:                                                .equals(productPromoCond
0191:                                                        .getString("inputParamEnumId"))) {
0192:                                            condResult = checkCondition(
0193:                                                    productPromoCond, cart,
0194:                                                    delegator, dispatcher,
0195:                                                    nowTimestamp);
0196:                                        }
0197:                                    }
0198:                                }
0199:                                if (!condResult)
0200:                                    productPromo = null;
0201:                            }
0202:                            if (productPromo != null)
0203:                                productPromos.add(productPromo);
0204:                        }
0205:                    }
0206:                } catch (GenericEntityException e) {
0207:                    Debug.logError(e, module);
0208:                }
0209:                return productPromos;
0210:            }
0211:
0212:            public static List getProductStorePromotions(ShoppingCart cart,
0213:                    Timestamp nowTimestamp, LocalDispatcher dispatcher) {
0214:                List productPromoList = FastList.newInstance();
0215:
0216:                GenericDelegator delegator = cart.getDelegator();
0217:
0218:                String productStoreId = cart.getProductStoreId();
0219:                GenericValue productStore = null;
0220:                try {
0221:                    productStore = delegator.findByPrimaryKeyCache(
0222:                            "ProductStore", UtilMisc.toMap("productStoreId",
0223:                                    productStoreId));
0224:                } catch (GenericEntityException e) {
0225:                    Debug.logError(e, "Error looking up store with id "
0226:                            + productStoreId, module);
0227:                }
0228:                if (productStore == null) {
0229:                    Debug.logWarning(UtilProperties.getMessage(resource_error,
0230:                            "OrderNoStoreFoundWithIdNotDoingPromotions",
0231:                            UtilMisc.toMap("productStoreId", productStoreId),
0232:                            cart.getLocale()), module);
0233:                    return productPromoList;
0234:                }
0235:
0236:                try {
0237:                    // loop through promotions and get a list of all of the rules...
0238:                    List productStorePromoApplsList = productStore
0239:                            .getRelatedCache("ProductStorePromoAppl", null,
0240:                                    UtilMisc.toList("sequenceNum"));
0241:                    productStorePromoApplsList = EntityUtil.filterByDate(
0242:                            productStorePromoApplsList, nowTimestamp);
0243:
0244:                    if (productStorePromoApplsList == null
0245:                            || productStorePromoApplsList.size() == 0) {
0246:                        if (Debug.verboseOn())
0247:                            Debug.logVerbose(
0248:                                    "Not doing promotions, none applied to store with ID "
0249:                                            + productStoreId, module);
0250:                    }
0251:
0252:                    Iterator prodCatalogPromoAppls = UtilMisc
0253:                            .toIterator(productStorePromoApplsList);
0254:                    while (prodCatalogPromoAppls != null
0255:                            && prodCatalogPromoAppls.hasNext()) {
0256:                        GenericValue prodCatalogPromoAppl = (GenericValue) prodCatalogPromoAppls
0257:                                .next();
0258:                        GenericValue productPromo = prodCatalogPromoAppl
0259:                                .getRelatedOneCache("ProductPromo");
0260:                        productPromoList.add(productPromo);
0261:                    }
0262:                } catch (GenericEntityException e) {
0263:                    Debug
0264:                            .logError(
0265:                                    e,
0266:                                    "Error looking up promotion data while doing promotions",
0267:                                    module);
0268:                }
0269:                return productPromoList;
0270:            }
0271:
0272:            public static List getAgreementPromotions(ShoppingCart cart,
0273:                    Timestamp nowTimestamp, LocalDispatcher dispatcher) {
0274:                List productPromoList = FastList.newInstance();
0275:
0276:                GenericDelegator delegator = cart.getDelegator();
0277:
0278:                String agreementId = cart.getAgreementId();
0279:                GenericValue agreement = null;
0280:                try {
0281:                    agreement = delegator.findByPrimaryKeyCache("Agreement",
0282:                            UtilMisc.toMap("agreementId", agreementId));
0283:                } catch (GenericEntityException e) {
0284:                    Debug.logError(e, "Error looking up agreement with id "
0285:                            + agreementId, module);
0286:                }
0287:                if (agreement == null) {
0288:                    Debug.logWarning(UtilProperties.getMessage(resource_error,
0289:                            "OrderNoAgreementFoundWithIdNotDoingPromotions",
0290:                            UtilMisc.toMap("agreementId", agreementId), cart
0291:                                    .getLocale()), module);
0292:                    return productPromoList;
0293:                }
0294:                GenericValue agreementItem = null;
0295:                try {
0296:                    List agreementItems = delegator.findByAndCache(
0297:                            "AgreementItem", UtilMisc.toMap("agreementId",
0298:                                    agreementId, "agreementItemTypeId",
0299:                                    "AGREEMENT_PRICING_PR", "currencyUomId",
0300:                                    cart.getCurrency()));
0301:                    agreementItem = EntityUtil.getFirst(agreementItems);
0302:                } catch (GenericEntityException e) {
0303:                    Debug.logError(e,
0304:                            "Error looking up agreement items for agreement with id "
0305:                                    + agreementId, module);
0306:                }
0307:                if (agreementItem == null) {
0308:                    Debug
0309:                            .logWarning(
0310:                                    UtilProperties
0311:                                            .getMessage(
0312:                                                    resource_error,
0313:                                                    "OrderNoAgreementItemFoundForAgreementWithIdNotDoingPromotions",
0314:                                                    UtilMisc.toMap(
0315:                                                            "agreementId",
0316:                                                            agreementId), cart
0317:                                                            .getLocale()),
0318:                                    module);
0319:                    return productPromoList;
0320:                }
0321:
0322:                try {
0323:                    // loop through promotions and get a list of all of the rules...
0324:                    List agreementPromoApplsList = agreementItem
0325:                            .getRelatedCache("AgreementPromoAppl", null,
0326:                                    UtilMisc.toList("sequenceNum"));
0327:                    agreementPromoApplsList = EntityUtil.filterByDate(
0328:                            agreementPromoApplsList, nowTimestamp);
0329:
0330:                    if (agreementPromoApplsList == null
0331:                            || agreementPromoApplsList.size() == 0) {
0332:                        if (Debug.verboseOn())
0333:                            Debug.logVerbose(
0334:                                    "Not doing promotions, none applied to agreement with ID "
0335:                                            + agreementId, module);
0336:                    }
0337:
0338:                    Iterator agreementPromoAppls = UtilMisc
0339:                            .toIterator(agreementPromoApplsList);
0340:                    while (agreementPromoAppls != null
0341:                            && agreementPromoAppls.hasNext()) {
0342:                        GenericValue agreementPromoAppl = (GenericValue) agreementPromoAppls
0343:                                .next();
0344:                        GenericValue productPromo = agreementPromoAppl
0345:                                .getRelatedOneCache("ProductPromo");
0346:                        productPromoList.add(productPromo);
0347:                    }
0348:                } catch (GenericEntityException e) {
0349:                    Debug
0350:                            .logError(
0351:                                    e,
0352:                                    "Error looking up promotion data while doing promotions",
0353:                                    module);
0354:                }
0355:                return productPromoList;
0356:            }
0357:
0358:            public static void doPromotions(ShoppingCart cart,
0359:                    LocalDispatcher dispatcher) {
0360:                ProductPromoWorker.doPromotions(cart, null, dispatcher);
0361:            }
0362:
0363:            public static void doPromotions(ShoppingCart cart,
0364:                    List productPromoList, LocalDispatcher dispatcher) {
0365:                // this is called when a user logs in so that per customer limits are honored, called by cart when new userlogin is set
0366:                // there is code to store ProductPromoUse information when an order is placed
0367:                // ProductPromoUses are ignored if the corresponding order is cancelled
0368:                // limits sub total for promos to not use gift cards (products with a don't use in promo indicator), also exclude gift cards from all other promotion considerations including subTotals for discounts, etc
0369:                // TODO: (not done, delay, still considering...) add code to check ProductPromoUse limits per promo (customer, promo), and per code (customer, code) to avoid use of promos or codes getting through due to multiple carts getting promos applied at the same time, possibly on totally different servers
0370:
0371:                GenericDelegator delegator = cart.getDelegator();
0372:                Timestamp nowTimestamp = UtilDateTime.nowTimestamp();
0373:
0374:                // start out by clearing all existing promotions, then we can just add all that apply
0375:                cart.clearAllPromotionInformation();
0376:
0377:                // there will be a ton of db access, so just do a big catch entity exception block
0378:                try {
0379:                    if (productPromoList == null) {
0380:                        if (cart.getOrderType().equals("SALES_ORDER")) {
0381:                            productPromoList = ProductPromoWorker
0382:                                    .getProductStorePromotions(cart,
0383:                                            nowTimestamp, dispatcher);
0384:                        } else {
0385:                            productPromoList = ProductPromoWorker
0386:                                    .getAgreementPromotions(cart, nowTimestamp,
0387:                                            dispatcher);
0388:                        }
0389:                    }
0390:                    // do a calculate only run through the promotions, then order by descending totalDiscountAmount for each promotion
0391:                    // NOTE: on this run, with isolatedTestRun passed as false it should not apply any adjustments 
0392:                    //  or track which cart items are used for which promotions, but it will track ProductPromoUseInfo and 
0393:                    //  useLimits; we are basicly just trying to run each promo "independently" to see how much each is worth
0394:                    runProductPromos(productPromoList, cart, delegator,
0395:                            dispatcher, nowTimestamp, true);
0396:
0397:                    // NOTE: after that first pass we could remove any that have a 0 totalDiscountAmount from the run list, but we won't because by the time they are run the cart may have changed enough to get them to go; also, certain actions like free shipping should always be run even though we won't know what the totalDiscountAmount is at the time the promotion is run
0398:                    // each ProductPromoUseInfo on the shopping cart will contain it's total value, so add up all totals for each promoId and put them in a List of Maps
0399:                    // create a List of Maps with productPromo and totalDiscountAmount, use the Map sorter to sort them descending by totalDiscountAmount
0400:
0401:                    // before sorting split into two lists and sort each list; one list for promos that have a order total condition, and the other list for all promos that don't; then we'll always run the ones that have no condition on the order total first
0402:                    List productPromoDiscountMapList = FastList.newInstance();
0403:                    List productPromoDiscountMapListOrderTotal = FastList
0404:                            .newInstance();
0405:                    Iterator productPromoIter = productPromoList.iterator();
0406:                    while (productPromoIter.hasNext()) {
0407:                        GenericValue productPromo = (GenericValue) productPromoIter
0408:                                .next();
0409:                        Map productPromoDiscountMap = UtilMisc
0410:                                .toMap(
0411:                                        "productPromo",
0412:                                        productPromo,
0413:                                        "totalDiscountAmount",
0414:                                        new Double(
0415:                                                cart
0416:                                                        .getProductPromoUseTotalDiscount(productPromo
0417:                                                                .getString("productPromoId"))));
0418:                        if (hasOrderTotalCondition(productPromo, delegator)) {
0419:                            productPromoDiscountMapListOrderTotal
0420:                                    .add(productPromoDiscountMap);
0421:                        } else {
0422:                            productPromoDiscountMapList
0423:                                    .add(productPromoDiscountMap);
0424:                        }
0425:                    }
0426:
0427:                    // sort the Map List, do it ascending because the discount amounts will be negative, so the lowest number is really the highest discount
0428:                    productPromoDiscountMapList = UtilMisc.sortMaps(
0429:                            productPromoDiscountMapList, UtilMisc
0430:                                    .toList("+totalDiscountAmount"));
0431:                    productPromoDiscountMapListOrderTotal = UtilMisc.sortMaps(
0432:                            productPromoDiscountMapListOrderTotal, UtilMisc
0433:                                    .toList("+totalDiscountAmount"));
0434:
0435:                    productPromoDiscountMapList
0436:                            .addAll(productPromoDiscountMapListOrderTotal);
0437:
0438:                    List sortedProductPromoList = new ArrayList(
0439:                            productPromoDiscountMapList.size());
0440:                    Iterator productPromoDiscountMapIter = productPromoDiscountMapList
0441:                            .iterator();
0442:                    while (productPromoDiscountMapIter.hasNext()) {
0443:                        Map productPromoDiscountMap = (Map) productPromoDiscountMapIter
0444:                                .next();
0445:                        GenericValue productPromo = (GenericValue) productPromoDiscountMap
0446:                                .get("productPromo");
0447:                        sortedProductPromoList.add(productPromo);
0448:                        if (Debug.verboseOn())
0449:                            Debug
0450:                                    .logVerbose(
0451:                                            "Sorted Promo ["
0452:                                                    + productPromo
0453:                                                            .getString("productPromoId")
0454:                                                    + "] with total discount: "
0455:                                                    + productPromoDiscountMap
0456:                                                            .get("totalDiscountAmount"),
0457:                                            module);
0458:                    }
0459:
0460:                    // okay, all ready, do the real run, clearing the temporary result first...
0461:                    cart.clearAllPromotionInformation();
0462:                    runProductPromos(sortedProductPromoList, cart, delegator,
0463:                            dispatcher, nowTimestamp, false);
0464:                } catch (NumberFormatException e) {
0465:                    Debug
0466:                            .logError(
0467:                                    e,
0468:                                    "Number not formatted correctly in promotion rules, not completed...",
0469:                                    module);
0470:                } catch (GenericEntityException e) {
0471:                    Debug
0472:                            .logError(
0473:                                    e,
0474:                                    "Error looking up promotion data while doing promotions",
0475:                                    module);
0476:                } catch (Exception e) {
0477:                    Debug.logError(e, "Error running promotions, will ignore: "
0478:                            + e.toString(), module);
0479:                }
0480:            }
0481:
0482:            protected static boolean hasOrderTotalCondition(
0483:                    GenericValue productPromo, GenericDelegator delegator)
0484:                    throws GenericEntityException {
0485:                boolean hasOtCond = false;
0486:                List productPromoConds = delegator.findByAndCache(
0487:                        "ProductPromoCond", UtilMisc.toMap("productPromoId",
0488:                                productPromo.get("productPromoId")), UtilMisc
0489:                                .toList("productPromoCondSeqId"));
0490:                Iterator productPromoCondIter = productPromoConds.iterator();
0491:                while (productPromoCondIter.hasNext()) {
0492:                    GenericValue productPromoCond = (GenericValue) productPromoCondIter
0493:                            .next();
0494:                    String inputParamEnumId = productPromoCond
0495:                            .getString("inputParamEnumId");
0496:                    if ("PPIP_ORDER_TOTAL".equals(inputParamEnumId)) {
0497:                        hasOtCond = true;
0498:                        break;
0499:                    }
0500:                }
0501:                return hasOtCond;
0502:            }
0503:
0504:            protected static void runProductPromos(List productPromoList,
0505:                    ShoppingCart cart, GenericDelegator delegator,
0506:                    LocalDispatcher dispatcher, Timestamp nowTimestamp,
0507:                    boolean isolatedTestRun) throws GeneralException {
0508:                String partyId = cart.getPartyId();
0509:
0510:                // this is our safety net; we should never need to loop through the rules more than a certain number of times, this is that number and may have to be changed for insanely large promo sets...
0511:                long maxIterations = 1000;
0512:                // part of the safety net to avoid infinite iteration
0513:                long numberOfIterations = 0;
0514:
0515:                // set a max limit on how many times each promo can be run, for cases where there is no use limit this will be the use limit
0516:                //default to 2 times the number of items in the cart
0517:                long maxUseLimit = 2 * Math.round(cart.getTotalQuantity());
0518:
0519:                try {
0520:                    // repeat until no more rules to run: either all rules are run, or no changes to the cart in a loop
0521:                    boolean cartChanged = true;
0522:                    while (cartChanged) {
0523:                        cartChanged = false;
0524:                        numberOfIterations++;
0525:                        if (numberOfIterations > maxIterations) {
0526:                            Debug
0527:                                    .logError(
0528:                                            "ERROR: While calculating promotions the promotion rules where run more than "
0529:                                                    + maxIterations
0530:                                                    + " times, so the calculation has been ended. This should generally never happen unless you have bad rule definitions.",
0531:                                            module);
0532:                            break;
0533:                        }
0534:
0535:                        Iterator productPromoIter = productPromoList.iterator();
0536:                        while (productPromoIter.hasNext()) {
0537:                            GenericValue productPromo = (GenericValue) productPromoIter
0538:                                    .next();
0539:                            String productPromoId = productPromo
0540:                                    .getString("productPromoId");
0541:
0542:                            List productPromoRules = productPromo
0543:                                    .getRelatedCache("ProductPromoRule", null,
0544:                                            null);
0545:                            if (productPromoRules != null
0546:                                    && productPromoRules.size() > 0) {
0547:                                // always have a useLimit to avoid unlimited looping, default to 1 if no other is specified
0548:                                Long candidateUseLimit = getProductPromoUseLimit(
0549:                                        productPromo, partyId, delegator);
0550:                                Long useLimit = candidateUseLimit;
0551:                                if (Debug.verboseOn())
0552:                                    Debug.logVerbose("Running promotion ["
0553:                                            + productPromoId + "], useLimit="
0554:                                            + useLimit + ", # of rules="
0555:                                            + productPromoRules.size(), module);
0556:
0557:                                boolean requireCode = "Y".equals(productPromo
0558:                                        .getString("requireCode"));
0559:                                // check if promo code required
0560:                                if (requireCode) {
0561:                                    Set enteredCodes = cart
0562:                                            .getProductPromoCodesEntered();
0563:                                    if (enteredCodes.size() > 0) {
0564:                                        // get all promo codes entered, do a query with an IN condition to see if any of those are related
0565:                                        EntityCondition codeCondition = new EntityExpr(
0566:                                                new EntityExpr(
0567:                                                        "productPromoId",
0568:                                                        EntityOperator.EQUALS,
0569:                                                        productPromoId),
0570:                                                EntityOperator.AND,
0571:                                                new EntityExpr(
0572:                                                        "productPromoCodeId",
0573:                                                        EntityOperator.IN,
0574:                                                        enteredCodes));
0575:                                        // may want to sort by something else to decide which code to use if there is more than one candidate
0576:                                        List productPromoCodeList = delegator
0577:                                                .findByCondition(
0578:                                                        "ProductPromoCode",
0579:                                                        codeCondition,
0580:                                                        null,
0581:                                                        UtilMisc
0582:                                                                .toList("productPromoCodeId"));
0583:                                        Iterator productPromoCodeIter = productPromoCodeList
0584:                                                .iterator();
0585:                                        // support multiple promo codes for a single promo, ie if we run into a use limit for one code see if we can find another for this promo
0586:                                        // check the use limit before each pass so if the promo use limit has been hit we don't keep on trying for the promo code use limit, if there is one of course
0587:                                        while ((useLimit == null || useLimit
0588:                                                .longValue() > cart
0589:                                                .getProductPromoUseCount(productPromoId))
0590:                                                && productPromoCodeIter
0591:                                                        .hasNext()) {
0592:                                            GenericValue productPromoCode = (GenericValue) productPromoCodeIter
0593:                                                    .next();
0594:                                            String productPromoCodeId = productPromoCode
0595:                                                    .getString("productPromoCodeId");
0596:                                            Long codeUseLimit = getProductPromoCodeUseLimit(
0597:                                                    productPromoCode, partyId,
0598:                                                    delegator);
0599:                                            if (runProductPromoRules(cart,
0600:                                                    cartChanged, useLimit,
0601:                                                    true, productPromoCodeId,
0602:                                                    codeUseLimit, maxUseLimit,
0603:                                                    productPromo,
0604:                                                    productPromoRules,
0605:                                                    dispatcher, delegator,
0606:                                                    nowTimestamp)) {
0607:                                                cartChanged = true;
0608:                                            }
0609:
0610:                                            if (cart
0611:                                                    .getProductPromoUseCount(productPromoId) > maxUseLimit) {
0612:                                                Debug
0613:                                                        .logError(
0614:                                                                "ERROR: While calculating promotions the promotion ["
0615:                                                                        + productPromoId
0616:                                                                        + "] action was applied more than "
0617:                                                                        + maxUseLimit
0618:                                                                        + " times, so the calculation has been ended. This should generally never happen unless you have bad rule definitions.",
0619:                                                                module);
0620:                                                break;
0621:                                            }
0622:                                        }
0623:                                    }
0624:                                } else {
0625:                                    try {
0626:                                        if (runProductPromoRules(cart,
0627:                                                cartChanged, useLimit, false,
0628:                                                null, null, maxUseLimit,
0629:                                                productPromo,
0630:                                                productPromoRules, dispatcher,
0631:                                                delegator, nowTimestamp)) {
0632:                                            cartChanged = true;
0633:                                        }
0634:                                    } catch (RuntimeException e) {
0635:                                        throw new GeneralException(
0636:                                                "Error running promotion with ID ["
0637:                                                        + productPromoId + "]",
0638:                                                e);
0639:                                    }
0640:                                }
0641:                            }
0642:
0643:                            // if this is an isolatedTestRun clear out adjustments and cart item promo use info
0644:                            if (isolatedTestRun) {
0645:                                cart.clearAllPromotionAdjustments();
0646:                                cart.clearCartItemUseInPromoInfo();
0647:                            }
0648:                        }
0649:
0650:                        // if this is an isolatedTestRun, then only go through it once, never retry
0651:                        if (isolatedTestRun) {
0652:                            cartChanged = false;
0653:                        }
0654:                    }
0655:                } catch (UseLimitException e) {
0656:                    Debug.logError(e, e.toString(), module);
0657:                }
0658:            }
0659:
0660:            /** calculate low use limit for this promo for the current "order", check per order, customer, promo */
0661:            public static Long getProductPromoUseLimit(
0662:                    GenericValue productPromo, String partyId,
0663:                    GenericDelegator delegator) throws GenericEntityException {
0664:                String productPromoId = productPromo
0665:                        .getString("productPromoId");
0666:                Long candidateUseLimit = null;
0667:
0668:                Long useLimitPerOrder = productPromo
0669:                        .getLong("useLimitPerOrder");
0670:                if (useLimitPerOrder != null) {
0671:                    if (candidateUseLimit == null
0672:                            || candidateUseLimit.longValue() > useLimitPerOrder
0673:                                    .longValue()) {
0674:                        candidateUseLimit = useLimitPerOrder;
0675:                    }
0676:                }
0677:
0678:                // Debug.logInfo("Promo [" + productPromoId + "] use limit after per order check: " + candidateUseLimit, module);
0679:
0680:                Long useLimitPerCustomer = productPromo
0681:                        .getLong("useLimitPerCustomer");
0682:                // check this whether or not there is a party right now
0683:                if (useLimitPerCustomer != null) {
0684:                    // if partyId is not empty check previous usage
0685:                    long productPromoCustomerUseSize = 0;
0686:                    if (UtilValidate.isNotEmpty(partyId)) {
0687:                        // check to see how many times this has been used for other orders for this customer, the remainder is the limit for this order
0688:                        EntityCondition checkCondition = new EntityConditionList(
0689:                                UtilMisc
0690:                                        .toList(
0691:                                                new EntityExpr(
0692:                                                        "productPromoId",
0693:                                                        EntityOperator.EQUALS,
0694:                                                        productPromoId),
0695:                                                new EntityExpr("partyId",
0696:                                                        EntityOperator.EQUALS,
0697:                                                        partyId),
0698:                                                new EntityExpr(
0699:                                                        "statusId",
0700:                                                        EntityOperator.NOT_EQUAL,
0701:                                                        "ORDER_REJECTED"),
0702:                                                new EntityExpr(
0703:                                                        "statusId",
0704:                                                        EntityOperator.NOT_EQUAL,
0705:                                                        "ORDER_CANCELLED")),
0706:                                EntityOperator.AND);
0707:                        productPromoCustomerUseSize = delegator
0708:                                .findCountByCondition("ProductPromoUseCheck",
0709:                                        checkCondition, null);
0710:                    }
0711:                    long perCustomerThisOrder = useLimitPerCustomer.longValue()
0712:                            - productPromoCustomerUseSize;
0713:                    if (candidateUseLimit == null
0714:                            || candidateUseLimit.longValue() > perCustomerThisOrder) {
0715:                        candidateUseLimit = new Long(perCustomerThisOrder);
0716:                    }
0717:                }
0718:
0719:                // Debug.logInfo("Promo [" + productPromoId + "] use limit after per customer check: " + candidateUseLimit, module);
0720:
0721:                Long useLimitPerPromotion = productPromo
0722:                        .getLong("useLimitPerPromotion");
0723:                if (useLimitPerPromotion != null) {
0724:                    // check to see how many times this has been used for other orders for this customer, the remainder is the limit for this order
0725:                    EntityCondition checkCondition = new EntityConditionList(
0726:                            UtilMisc.toList(new EntityExpr("productPromoId",
0727:                                    EntityOperator.EQUALS, productPromoId),
0728:                                    new EntityExpr("statusId",
0729:                                            EntityOperator.NOT_EQUAL,
0730:                                            "ORDER_REJECTED"), new EntityExpr(
0731:                                            "statusId",
0732:                                            EntityOperator.NOT_EQUAL,
0733:                                            "ORDER_CANCELLED")),
0734:                            EntityOperator.AND);
0735:                    long productPromoUseSize = delegator.findCountByCondition(
0736:                            "ProductPromoUseCheck", checkCondition, null);
0737:                    long perPromotionThisOrder = useLimitPerPromotion
0738:                            .longValue()
0739:                            - productPromoUseSize;
0740:                    if (candidateUseLimit == null
0741:                            || candidateUseLimit.longValue() > perPromotionThisOrder) {
0742:                        candidateUseLimit = new Long(perPromotionThisOrder);
0743:                    }
0744:                }
0745:
0746:                // Debug.logInfo("Promo [" + productPromoId + "] use limit after per promotion check: " + candidateUseLimit, module);
0747:
0748:                return candidateUseLimit;
0749:            }
0750:
0751:            public static Long getProductPromoCodeUseLimit(
0752:                    GenericValue productPromoCode, String partyId,
0753:                    GenericDelegator delegator) throws GenericEntityException {
0754:                String productPromoCodeId = productPromoCode
0755:                        .getString("productPromoCodeId");
0756:                Long codeUseLimit = null;
0757:
0758:                // check promo code use limits, per customer, code
0759:                Long codeUseLimitPerCustomer = productPromoCode
0760:                        .getLong("useLimitPerCustomer");
0761:                if (codeUseLimitPerCustomer != null) {
0762:                    long productPromoCustomerUseSize = 0;
0763:                    if (UtilValidate.isNotEmpty(partyId)) {
0764:                        // check to see how many times this has been used for other orders for this customer, the remainder is the limit for this order
0765:                        EntityCondition checkCondition = new EntityConditionList(
0766:                                UtilMisc.toList(new EntityExpr(
0767:                                        "productPromoCodeId",
0768:                                        EntityOperator.EQUALS,
0769:                                        productPromoCodeId), new EntityExpr(
0770:                                        "partyId", EntityOperator.EQUALS,
0771:                                        partyId), new EntityExpr("statusId",
0772:                                        EntityOperator.NOT_EQUAL,
0773:                                        "ORDER_REJECTED"), new EntityExpr(
0774:                                        "statusId", EntityOperator.NOT_EQUAL,
0775:                                        "ORDER_CANCELLED")), EntityOperator.AND);
0776:                        productPromoCustomerUseSize = delegator
0777:                                .findCountByCondition("ProductPromoUseCheck",
0778:                                        checkCondition, null);
0779:                    }
0780:                    long perCustomerThisOrder = codeUseLimitPerCustomer
0781:                            .longValue()
0782:                            - productPromoCustomerUseSize;
0783:                    if (codeUseLimit == null
0784:                            || codeUseLimit.longValue() > perCustomerThisOrder) {
0785:                        codeUseLimit = new Long(perCustomerThisOrder);
0786:                    }
0787:                }
0788:
0789:                Long codeUseLimitPerCode = productPromoCode
0790:                        .getLong("useLimitPerCode");
0791:                if (codeUseLimitPerCode != null) {
0792:                    // check to see how many times this has been used for other orders for this customer, the remainder is the limit for this order
0793:                    EntityCondition checkCondition = new EntityConditionList(
0794:                            UtilMisc.toList(new EntityExpr(
0795:                                    "productPromoCodeId",
0796:                                    EntityOperator.EQUALS, productPromoCodeId),
0797:                                    new EntityExpr("statusId",
0798:                                            EntityOperator.NOT_EQUAL,
0799:                                            "ORDER_REJECTED"), new EntityExpr(
0800:                                            "statusId",
0801:                                            EntityOperator.NOT_EQUAL,
0802:                                            "ORDER_CANCELLED")),
0803:                            EntityOperator.AND);
0804:                    long productPromoCodeUseSize = delegator
0805:                            .findCountByCondition("ProductPromoUseCheck",
0806:                                    checkCondition, null);
0807:                    long perCodeThisOrder = codeUseLimitPerCode.longValue()
0808:                            - productPromoCodeUseSize;
0809:                    if (codeUseLimit == null
0810:                            || codeUseLimit.longValue() > perCodeThisOrder) {
0811:                        codeUseLimit = new Long(perCodeThisOrder);
0812:                    }
0813:                }
0814:
0815:                return codeUseLimit;
0816:            }
0817:
0818:            public static String checkCanUsePromoCode(
0819:                    String productPromoCodeId, String partyId,
0820:                    GenericDelegator delegator) {
0821:                try {
0822:                    GenericValue productPromoCode = delegator.findByPrimaryKey(
0823:                            "ProductPromoCode", UtilMisc.toMap(
0824:                                    "productPromoCodeId", productPromoCodeId));
0825:                    if (productPromoCode == null) {
0826:                        return "The promotion code [" + productPromoCodeId
0827:                                + "] is not valid.";
0828:                    }
0829:
0830:                    if ("Y".equals(productPromoCode
0831:                            .getString("requireEmailOrParty"))) {
0832:                        boolean hasEmailOrParty = false;
0833:
0834:                        // check partyId
0835:                        if (UtilValidate.isNotEmpty(partyId)) {
0836:                            if (delegator.findByPrimaryKey(
0837:                                    "ProductPromoCodeParty", UtilMisc.toMap(
0838:                                            "productPromoCodeId",
0839:                                            productPromoCodeId, "partyId",
0840:                                            partyId)) != null) {
0841:                                // found party associated with the code, looks good...
0842:                                return null;
0843:                            }
0844:
0845:                            // check email address in ProductPromoCodeEmail
0846:                            Timestamp nowTimestamp = UtilDateTime
0847:                                    .nowTimestamp();
0848:                            List validEmailCondList = new ArrayList();
0849:                            validEmailCondList.add(new EntityExpr("partyId",
0850:                                    EntityOperator.EQUALS, partyId));
0851:                            validEmailCondList.add(new EntityExpr(
0852:                                    "productPromoCodeId",
0853:                                    EntityOperator.EQUALS, productPromoCodeId));
0854:                            validEmailCondList.add(new EntityExpr("fromDate",
0855:                                    EntityOperator.LESS_THAN_EQUAL_TO,
0856:                                    nowTimestamp));
0857:                            validEmailCondList
0858:                                    .add(new EntityExpr(
0859:                                            new EntityExpr(
0860:                                                    "thruDate",
0861:                                                    EntityOperator.GREATER_THAN_EQUAL_TO,
0862:                                                    nowTimestamp),
0863:                                            EntityOperator.OR,
0864:                                            new EntityExpr("thruDate",
0865:                                                    EntityOperator.EQUALS, null)));
0866:                            EntityCondition validEmailCondition = new EntityConditionList(
0867:                                    validEmailCondList, EntityOperator.AND);
0868:                            long validEmailCount = delegator
0869:                                    .findCountByCondition(
0870:                                            "ProductPromoCodeEmailParty",
0871:                                            validEmailCondition, null);
0872:                            if (validEmailCount > 0) {
0873:                                // there was an email in the list, looks good... 
0874:                                return null;
0875:                            }
0876:                        }
0877:
0878:                        if (!hasEmailOrParty) {
0879:                            return "This promotion code ["
0880:                                    + productPromoCodeId
0881:                                    + "] requires you to be associated with it by account or email address and you are not associated with it.";
0882:                        }
0883:                    }
0884:
0885:                    // check per customer and per promotion code use limits
0886:                    Long useLimit = getProductPromoCodeUseLimit(
0887:                            productPromoCode, partyId, delegator);
0888:                    if (useLimit != null && useLimit.longValue() <= 0) {
0889:                        return "This promotion code ["
0890:                                + productPromoCodeId
0891:                                + "] has reached it's maximum use limit for you and can no longer be used.";
0892:                    }
0893:
0894:                    return null;
0895:                } catch (GenericEntityException e) {
0896:                    Debug.logError(e, "Error looking up ProductPromoCode",
0897:                            module);
0898:                    return "Error looking up code [" + productPromoCodeId
0899:                            + "]:" + e.toString();
0900:                }
0901:            }
0902:
0903:            public static String makeAutoDescription(GenericValue productPromo,
0904:                    GenericDelegator delegator, Locale locale)
0905:                    throws GenericEntityException {
0906:                if (productPromo == null) {
0907:                    return "";
0908:                }
0909:                StringBuffer promoDescBuf = new StringBuffer();
0910:                List productPromoRules = productPromo.getRelatedCache(
0911:                        "ProductPromoRule", null, null);
0912:                Iterator promoRulesIter = productPromoRules.iterator();
0913:                while (promoRulesIter != null && promoRulesIter.hasNext()) {
0914:                    GenericValue productPromoRule = (GenericValue) promoRulesIter
0915:                            .next();
0916:
0917:                    List productPromoConds = delegator.findByAndCache(
0918:                            "ProductPromoCond", UtilMisc.toMap(
0919:                                    "productPromoId", productPromo
0920:                                            .get("productPromoId")), UtilMisc
0921:                                    .toList("productPromoCondSeqId"));
0922:                    productPromoConds = EntityUtil.filterByAnd(
0923:                            productPromoConds, UtilMisc.toMap(
0924:                                    "productPromoRuleId", productPromoRule
0925:                                            .get("productPromoRuleId")));
0926:                    // using the other method to consolodate cache entries because the same cache is used elsewhere: List productPromoConds = productPromoRule.getRelatedCache("ProductPromoCond", null, UtilMisc.toList("productPromoCondSeqId"));
0927:                    Iterator productPromoCondIter = UtilMisc
0928:                            .toIterator(productPromoConds);
0929:                    while (productPromoCondIter != null
0930:                            && productPromoCondIter.hasNext()) {
0931:                        GenericValue productPromoCond = (GenericValue) productPromoCondIter
0932:                                .next();
0933:
0934:                        String equalityOperator = UtilProperties.getMessage(
0935:                                "promotext", "operator.equality."
0936:                                        + productPromoCond
0937:                                                .getString("operatorEnumId"),
0938:                                locale);
0939:                        String quantityOperator = UtilProperties.getMessage(
0940:                                "promotext", "operator.quantity."
0941:                                        + productPromoCond
0942:                                                .getString("operatorEnumId"),
0943:                                locale);
0944:
0945:                        String condValue = "invalid";
0946:                        if (UtilValidate.isNotEmpty(productPromoCond
0947:                                .getString("condValue"))) {
0948:                            condValue = productPromoCond.getString("condValue");
0949:                        }
0950:
0951:                        Map messageContext = UtilMisc.toMap("condValue",
0952:                                condValue, "equalityOperator",
0953:                                equalityOperator, "quantityOperator",
0954:                                quantityOperator);
0955:                        String msgProp = UtilProperties.getMessage("promotext",
0956:                                "condition."
0957:                                        + productPromoCond
0958:                                                .getString("inputParamEnumId"),
0959:                                messageContext, locale);
0960:                        promoDescBuf.append(msgProp);
0961:                        promoDescBuf.append(" ");
0962:
0963:                        if (promoRulesIter.hasNext()) {
0964:                            promoDescBuf.append(" and ");
0965:                        }
0966:                    }
0967:
0968:                    List productPromoActions = productPromoRule
0969:                            .getRelatedCache("ProductPromoAction", null,
0970:                                    UtilMisc.toList("productPromoActionSeqId"));
0971:                    Iterator productPromoActionIter = UtilMisc
0972:                            .toIterator(productPromoActions);
0973:                    while (productPromoActionIter != null
0974:                            && productPromoActionIter.hasNext()) {
0975:                        GenericValue productPromoAction = (GenericValue) productPromoActionIter
0976:                                .next();
0977:
0978:                        String productId = productPromoAction
0979:                                .getString("productId");
0980:
0981:                        Map messageContext = UtilMisc.toMap("quantity",
0982:                                productPromoAction.get("quantity"), "amount",
0983:                                productPromoAction.get("amount"), "productId",
0984:                                productId, "partyId", productPromoAction
0985:                                        .get("partyId"));
0986:
0987:                        if (UtilValidate.isEmpty((String) messageContext
0988:                                .get("productId")))
0989:                            messageContext.put("productId", "any");
0990:                        if (UtilValidate.isEmpty((String) messageContext
0991:                                .get("partyId")))
0992:                            messageContext.put("partyId", "any");
0993:                        GenericValue product = delegator.findByPrimaryKeyCache(
0994:                                "Product", UtilMisc.toMap("productId",
0995:                                        productId));
0996:                        if (product != null) {
0997:                            messageContext.put("productName",
0998:                                    ProductContentWrapper
0999:                                            .getProductContentAsText(product,
1000:                                                    "PRODUCT_NAME", locale,
1001:                                                    null));
1002:                        }
1003:
1004:                        String msgProp = UtilProperties
1005:                                .getMessage(
1006:                                        "promotext",
1007:                                        "action."
1008:                                                + productPromoAction
1009:                                                        .getString("productPromoActionEnumId"),
1010:                                        messageContext, locale);
1011:                        promoDescBuf.append(msgProp);
1012:                        promoDescBuf.append(" ");
1013:
1014:                        if (promoRulesIter.hasNext()) {
1015:                            promoDescBuf.append(" and ");
1016:                        }
1017:                    }
1018:
1019:                    if (promoRulesIter.hasNext()) {
1020:                        promoDescBuf.append(" or ");
1021:                    }
1022:                }
1023:
1024:                if (promoDescBuf.length() > 0) {
1025:                    // remove any trailing space
1026:                    if (promoDescBuf.charAt(promoDescBuf.length() - 1) == ' ')
1027:                        promoDescBuf.deleteCharAt(promoDescBuf.length() - 1);
1028:                    // add a period
1029:                    promoDescBuf.append(". ");
1030:                    // capitalize the first letter
1031:                    promoDescBuf.setCharAt(0, Character
1032:                            .toUpperCase(promoDescBuf.charAt(0)));
1033:                }
1034:
1035:                if ("Y".equals(productPromo.getString("requireCode"))) {
1036:                    promoDescBuf.append("Requires code to use. ");
1037:                }
1038:                if (productPromo.getLong("useLimitPerOrder") != null) {
1039:                    promoDescBuf.append("Limit ");
1040:                    promoDescBuf.append(productPromo
1041:                            .getLong("useLimitPerOrder"));
1042:                    promoDescBuf.append(" per order. ");
1043:                }
1044:                if (productPromo.getLong("useLimitPerCustomer") != null) {
1045:                    promoDescBuf.append("Limit ");
1046:                    promoDescBuf.append(productPromo
1047:                            .getLong("useLimitPerCustomer"));
1048:                    promoDescBuf.append(" per customer. ");
1049:                }
1050:                if (productPromo.getLong("useLimitPerPromotion") != null) {
1051:                    promoDescBuf.append("Limit ");
1052:                    promoDescBuf.append(productPromo
1053:                            .getLong("useLimitPerPromotion"));
1054:                    promoDescBuf.append(" per promotion. ");
1055:                }
1056:
1057:                return promoDescBuf.toString();
1058:            }
1059:
1060:            protected static boolean runProductPromoRules(ShoppingCart cart,
1061:                    boolean cartChanged, Long useLimit, boolean requireCode,
1062:                    String productPromoCodeId, Long codeUseLimit,
1063:                    long maxUseLimit, GenericValue productPromo,
1064:                    List productPromoRules, LocalDispatcher dispatcher,
1065:                    GenericDelegator delegator, Timestamp nowTimestamp)
1066:                    throws GenericEntityException, UseLimitException {
1067:                String productPromoId = productPromo
1068:                        .getString("productPromoId");
1069:                while ((useLimit == null || useLimit.longValue() > cart
1070:                        .getProductPromoUseCount(productPromoId))
1071:                        && (!requireCode || UtilValidate
1072:                                .isNotEmpty(productPromoCodeId))
1073:                        && (codeUseLimit == null || codeUseLimit.longValue() > cart
1074:                                .getProductPromoCodeUse(productPromoCodeId))) {
1075:                    boolean promoUsed = false;
1076:                    double totalDiscountAmount = 0;
1077:                    double quantityLeftInActions = 0;
1078:
1079:                    Iterator promoRulesIter = productPromoRules.iterator();
1080:                    while (promoRulesIter != null && promoRulesIter.hasNext()) {
1081:                        GenericValue productPromoRule = (GenericValue) promoRulesIter
1082:                                .next();
1083:
1084:                        // if apply then performActions when no conditions are false, so default to true
1085:                        boolean performActions = true;
1086:
1087:                        // loop through conditions for rule, if any false, set allConditionsTrue to false
1088:                        List productPromoConds = delegator.findByAndCache(
1089:                                "ProductPromoCond", UtilMisc.toMap(
1090:                                        "productPromoId", productPromo
1091:                                                .get("productPromoId")),
1092:                                UtilMisc.toList("productPromoCondSeqId"));
1093:                        productPromoConds = EntityUtil.filterByAnd(
1094:                                productPromoConds, UtilMisc.toMap(
1095:                                        "productPromoRuleId", productPromoRule
1096:                                                .get("productPromoRuleId")));
1097:                        // using the other method to consolodate cache entries because the same cache is used elsewhere: List productPromoConds = productPromoRule.getRelatedCache("ProductPromoCond", null, UtilMisc.toList("productPromoCondSeqId"));
1098:                        if (Debug.verboseOn())
1099:                            Debug.logVerbose("Checking "
1100:                                    + productPromoConds.size()
1101:                                    + " conditions for rule "
1102:                                    + productPromoRule, module);
1103:
1104:                        Iterator productPromoCondIter = UtilMisc
1105:                                .toIterator(productPromoConds);
1106:                        while (productPromoCondIter != null
1107:                                && productPromoCondIter.hasNext()) {
1108:                            GenericValue productPromoCond = (GenericValue) productPromoCondIter
1109:                                    .next();
1110:
1111:                            boolean condResult = checkCondition(
1112:                                    productPromoCond, cart, delegator,
1113:                                    dispatcher, nowTimestamp);
1114:
1115:                            // any false condition will cause it to NOT perform the action
1116:                            if (condResult == false) {
1117:                                performActions = false;
1118:                                break;
1119:                            }
1120:                        }
1121:
1122:                        if (performActions) {
1123:                            // perform all actions, either apply or unapply
1124:
1125:                            List productPromoActions = productPromoRule
1126:                                    .getRelatedCache(
1127:                                            "ProductPromoAction",
1128:                                            null,
1129:                                            UtilMisc
1130:                                                    .toList("productPromoActionSeqId"));
1131:                            if (Debug.verboseOn())
1132:                                Debug.logVerbose("Performing "
1133:                                        + productPromoActions.size()
1134:                                        + " actions for rule "
1135:                                        + productPromoRule, module);
1136:                            Iterator productPromoActionIter = UtilMisc
1137:                                    .toIterator(productPromoActions);
1138:                            while (productPromoActionIter != null
1139:                                    && productPromoActionIter.hasNext()) {
1140:                                GenericValue productPromoAction = (GenericValue) productPromoActionIter
1141:                                        .next();
1142:
1143:                                // Debug.logInfo("Doing action: " + productPromoAction, module);
1144:                                try {
1145:                                    ActionResultInfo actionResultInfo = performAction(
1146:                                            productPromoAction, cart,
1147:                                            delegator, dispatcher, nowTimestamp);
1148:                                    totalDiscountAmount += actionResultInfo.totalDiscountAmount;
1149:                                    quantityLeftInActions += actionResultInfo.quantityLeftInAction;
1150:
1151:                                    // only set if true, don't set back to false: implements OR logic (ie if ANY actions change content, redo loop)
1152:                                    boolean actionChangedCart = actionResultInfo.ranAction;
1153:                                    if (actionChangedCart) {
1154:                                        promoUsed = true;
1155:                                        cartChanged = true;
1156:                                    }
1157:                                } catch (CartItemModifyException e) {
1158:                                    Debug.logError(
1159:                                            "Error modifying the cart while performing promotion action ["
1160:                                                    + productPromoAction
1161:                                                            .getPrimaryKey()
1162:                                                    + "]: " + e.toString(),
1163:                                            module);
1164:                                }
1165:                            }
1166:                        }
1167:                    }
1168:
1169:                    if (promoUsed) {
1170:                        cart.addProductPromoUse(productPromo
1171:                                .getString("productPromoId"),
1172:                                productPromoCodeId, totalDiscountAmount,
1173:                                quantityLeftInActions);
1174:                    } else {
1175:                        // the promotion was not used, don't try again until we finish a full pass and come back to see the promo conditions are now satisfied based on changes to the cart
1176:                        break;
1177:                    }
1178:
1179:                    if (cart.getProductPromoUseCount(productPromoId) > maxUseLimit) {
1180:                        throw new UseLimitException(
1181:                                "ERROR: While calculating promotions the promotion ["
1182:                                        + productPromoId
1183:                                        + "] action was applied more than "
1184:                                        + maxUseLimit
1185:                                        + " times, so the calculation has been ended. This should generally never happen unless you have bad rule definitions.");
1186:                    }
1187:                }
1188:
1189:                return cartChanged;
1190:            }
1191:
1192:            protected static boolean checkCondition(
1193:                    GenericValue productPromoCond, ShoppingCart cart,
1194:                    GenericDelegator delegator, LocalDispatcher dispatcher,
1195:                    Timestamp nowTimestamp) throws GenericEntityException {
1196:                String condValue = productPromoCond.getString("condValue");
1197:                String otherValue = productPromoCond.getString("otherValue");
1198:                String inputParamEnumId = productPromoCond
1199:                        .getString("inputParamEnumId");
1200:                String operatorEnumId = productPromoCond
1201:                        .getString("operatorEnumId");
1202:
1203:                String partyId = cart.getPartyId();
1204:                GenericValue userLogin = cart.getUserLogin();
1205:                if (userLogin == null) {
1206:                    userLogin = cart.getAutoUserLogin();
1207:                }
1208:
1209:                if (Debug.verboseOn())
1210:                    Debug.logVerbose("Checking promotion condition: "
1211:                            + productPromoCond, module);
1212:                Integer compareBase = null;
1213:
1214:                if ("PPIP_PRODUCT_AMOUNT".equals(inputParamEnumId)) {
1215:                    // for this type of promo force the operatorEnumId = PPC_EQ, effectively ignore that setting because the comparison is implied in the code
1216:                    operatorEnumId = "PPC_EQ";
1217:
1218:                    // this type of condition requires items involved to not be involved in any other quantity consuming cond/action, and does not pro-rate the price, just uses the base price
1219:                    double amountNeeded = 0.0;
1220:                    if (UtilValidate.isNotEmpty(condValue)) {
1221:                        amountNeeded = Double.parseDouble(condValue);
1222:                    }
1223:
1224:                    // Debug.logInfo("Doing Amount Cond with Value: " + amountNeeded, module);
1225:
1226:                    Set productIds = ProductPromoWorker
1227:                            .getPromoRuleCondProductIds(productPromoCond,
1228:                                    delegator, nowTimestamp);
1229:
1230:                    List lineOrderedByBasePriceList = cart
1231:                            .getLineListOrderedByBasePrice(false);
1232:                    Iterator lineOrderedByBasePriceIter = lineOrderedByBasePriceList
1233:                            .iterator();
1234:                    while (amountNeeded > 0
1235:                            && lineOrderedByBasePriceIter.hasNext()) {
1236:                        ShoppingCartItem cartItem = (ShoppingCartItem) lineOrderedByBasePriceIter
1237:                                .next();
1238:                        // only include if it is in the productId Set for this check and if it is not a Promo (GWP) item
1239:                        GenericValue product = cartItem.getProduct();
1240:                        String parentProductId = cartItem.getParentProductId();
1241:                        if (!cartItem.getIsPromo()
1242:                                && (productIds
1243:                                        .contains(cartItem.getProductId()) || (parentProductId != null && productIds
1244:                                        .contains(parentProductId)))
1245:                                && (product == null || !"N".equals(product
1246:                                        .getString("includeInPromotions")))) {
1247:
1248:                            double basePrice = cartItem.getBasePrice();
1249:                            // get a rough price, round it up to an integer
1250:                            double quantityNeeded = Math.ceil(amountNeeded
1251:                                    / basePrice);
1252:
1253:                            // reduce amount still needed to qualify for promo (amountNeeded)
1254:                            double quantity = cartItem
1255:                                    .addPromoQuantityCandidateUse(
1256:                                            quantityNeeded, productPromoCond,
1257:                                            false);
1258:                            // get pro-rated amount based on discount
1259:                            amountNeeded -= (quantity * basePrice);
1260:                        }
1261:                    }
1262:
1263:                    // Debug.logInfo("Doing Amount Cond with Value after finding applicable cart lines: " + amountNeeded, module);
1264:
1265:                    // if amountNeeded > 0 then the promo condition failed, so remove candidate promo uses and increment the promoQuantityUsed to restore it
1266:                    if (amountNeeded > 0) {
1267:                        // failed, reset the entire rule, ie including all other conditions that might have been done before
1268:                        cart.resetPromoRuleUse(productPromoCond
1269:                                .getString("productPromoId"), productPromoCond
1270:                                .getString("productPromoRuleId"));
1271:                        compareBase = new Integer(-1);
1272:                    } else {
1273:                        // we got it, the conditions are in place...
1274:                        compareBase = new Integer(0);
1275:                        // NOTE: don't confirm promo rule use here, wait until actions are complete for the rule to do that
1276:                    }
1277:                } else if ("PPIP_PRODUCT_TOTAL".equals(inputParamEnumId)) {
1278:                    // this type of condition allows items involved to be involved in other quantity consuming cond/action, and does pro-rate the price
1279:                    Double amountNeeded = Double.valueOf(condValue);
1280:                    double amountAvailable = 0;
1281:
1282:                    // Debug.logInfo("Doing Amount Not Counted Cond with Value: " + amountNeeded, module);
1283:
1284:                    Set productIds = ProductPromoWorker
1285:                            .getPromoRuleCondProductIds(productPromoCond,
1286:                                    delegator, nowTimestamp);
1287:
1288:                    List lineOrderedByBasePriceList = cart
1289:                            .getLineListOrderedByBasePrice(false);
1290:                    Iterator lineOrderedByBasePriceIter = lineOrderedByBasePriceList
1291:                            .iterator();
1292:                    while (lineOrderedByBasePriceIter.hasNext()) {
1293:                        ShoppingCartItem cartItem = (ShoppingCartItem) lineOrderedByBasePriceIter
1294:                                .next();
1295:                        // only include if it is in the productId Set for this check and if it is not a Promo (GWP) item
1296:                        GenericValue product = cartItem.getProduct();
1297:                        String parentProductId = cartItem.getParentProductId();
1298:                        if (!cartItem.getIsPromo()
1299:                                && (productIds
1300:                                        .contains(cartItem.getProductId()) || (parentProductId != null && productIds
1301:                                        .contains(parentProductId)))
1302:                                && (product == null || !"N".equals(product
1303:                                        .getString("includeInPromotions")))) {
1304:
1305:                            // just count the entire sub-total of the item
1306:                            amountAvailable += cartItem.getItemSubTotal();
1307:                        }
1308:                    }
1309:
1310:                    // Debug.logInfo("Doing Amount Not Counted Cond with Value after finding applicable cart lines: " + amountNeeded, module);
1311:
1312:                    compareBase = new Integer(new Double(amountAvailable)
1313:                            .compareTo(amountNeeded));
1314:                } else if ("PPIP_PRODUCT_QUANT".equals(inputParamEnumId)) {
1315:                    // for this type of promo force the operatorEnumId = PPC_EQ, effectively ignore that setting because the comparison is implied in the code
1316:                    operatorEnumId = "PPC_EQ";
1317:
1318:                    double quantityNeeded = 1.0;
1319:                    if (UtilValidate.isNotEmpty(condValue)) {
1320:                        quantityNeeded = Double.parseDouble(condValue);
1321:                    }
1322:
1323:                    Set productIds = ProductPromoWorker
1324:                            .getPromoRuleCondProductIds(productPromoCond,
1325:                                    delegator, nowTimestamp);
1326:
1327:                    List lineOrderedByBasePriceList = cart
1328:                            .getLineListOrderedByBasePrice(false);
1329:                    Iterator lineOrderedByBasePriceIter = lineOrderedByBasePriceList
1330:                            .iterator();
1331:                    while (quantityNeeded > 0
1332:                            && lineOrderedByBasePriceIter.hasNext()) {
1333:                        ShoppingCartItem cartItem = (ShoppingCartItem) lineOrderedByBasePriceIter
1334:                                .next();
1335:                        // only include if it is in the productId Set for this check and if it is not a Promo (GWP) item
1336:                        GenericValue product = cartItem.getProduct();
1337:                        String parentProductId = cartItem.getParentProductId();
1338:                        if (!cartItem.getIsPromo()
1339:                                && (productIds
1340:                                        .contains(cartItem.getProductId()) || (parentProductId != null && productIds
1341:                                        .contains(parentProductId)))
1342:                                && (product == null || !"N".equals(product
1343:                                        .getString("includeInPromotions")))) {
1344:                            // reduce quantity still needed to qualify for promo (quantityNeeded)
1345:                            quantityNeeded -= cartItem
1346:                                    .addPromoQuantityCandidateUse(
1347:                                            quantityNeeded, productPromoCond,
1348:                                            false);
1349:                        }
1350:                    }
1351:
1352:                    // if quantityNeeded > 0 then the promo condition failed, so remove candidate promo uses and increment the promoQuantityUsed to restore it
1353:                    if (quantityNeeded > 0) {
1354:                        // failed, reset the entire rule, ie including all other conditions that might have been done before
1355:                        cart.resetPromoRuleUse(productPromoCond
1356:                                .getString("productPromoId"), productPromoCond
1357:                                .getString("productPromoRuleId"));
1358:                        compareBase = new Integer(-1);
1359:                    } else {
1360:                        // we got it, the conditions are in place...
1361:                        compareBase = new Integer(0);
1362:                        // NOTE: don't confirm rpomo rule use here, wait until actions are complete for the rule to do that
1363:                    }
1364:
1365:                    /* replaced by PPIP_PRODUCT_QUANT
1366:                    } else if ("PPIP_PRODUCT_ID_IC".equals(inputParamEnumId)) {
1367:                        String candidateProductId = condValue;
1368:
1369:                        if (candidateProductId == null) {
1370:                            // if null, then it's not in the cart
1371:                            compareBase = new Integer(1);
1372:                        } else {
1373:                            // Debug.logInfo("Testing to see if productId \"" + candidateProductId + "\" is in the cart", module);
1374:                            List productCartItems = cart.findAllCartItems(candidateProductId);
1375:
1376:                            // don't count promotion items in this count...
1377:                            Iterator pciIter = productCartItems.iterator();
1378:                            while (pciIter.hasNext()) {
1379:                                ShoppingCartItem productCartItem = (ShoppingCartItem) pciIter.next();
1380:                                if (productCartItem.getIsPromo()) pciIter.remove();
1381:                            }
1382:
1383:                            if (productCartItems.size() > 0) {
1384:                                //Debug.logError("Item with productId \"" + candidateProductId + "\" IS in the cart", module);
1385:                                compareBase = new Integer(0);
1386:                            } else {
1387:                                //Debug.logError("Item with productId \"" + candidateProductId + "\" IS NOT in the cart", module);
1388:                                compareBase = new Integer(1);
1389:                            }
1390:                        }
1391:                    } else if ("PPIP_CATEGORY_ID_IC".equals(inputParamEnumId)) {
1392:                        String productCategoryId = condValue;
1393:                        Set productIds = new HashSet();
1394:
1395:                        Iterator cartItemIter = cart.iterator();
1396:                        while (cartItemIter.hasNext()) {
1397:                            ShoppingCartItem cartItem = (ShoppingCartItem) cartItemIter.next();
1398:                            if (!cartItem.getIsPromo()) {
1399:                                productIds.add(cartItem.getProductId());
1400:                            }
1401:                        }
1402:
1403:                        compareBase = new Integer(1);
1404:                        // NOTE: this technique is efficient for a smaller number of items in the cart, if there are a lot of lines
1405:                        //in the cart then a non-cached query with a set of productIds using the IN operator would be better
1406:                        Iterator productIdIter = productIds.iterator();
1407:                        while (productIdIter.hasNext()) {
1408:                            String productId = (String) productIdIter.next();
1409:
1410:                            // if a ProductCategoryMember exists for this productId and the specified productCategoryId
1411:                            List productCategoryMembers = delegator.findByAndCache("ProductCategoryMember", UtilMisc.toMap("productId", productId, "productCategoryId", productCategoryId));
1412:                            // and from/thru date within range
1413:                            productCategoryMembers = EntityUtil.filterByDate(productCategoryMembers, nowTimestamp);
1414:                            if (productCategoryMembers != null && productCategoryMembers.size() > 0) {
1415:                                // if any product is in category, set true and break
1416:                                // then 0 (equals), otherwise 1 (not equals)
1417:                                compareBase = new Integer(0);
1418:                                break;
1419:                            }
1420:                        }
1421:                     */
1422:                } else if ("PPIP_NEW_ACCT".equals(inputParamEnumId)) {
1423:                    Double acctDays = cart
1424:                            .getPartyDaysSinceCreated(nowTimestamp);
1425:                    if (acctDays == null) {
1426:                        // condition always fails if we don't know how many days since account created
1427:                        return false;
1428:                    }
1429:                    compareBase = new Integer(acctDays.compareTo(Double
1430:                            .valueOf(condValue)));
1431:                } else if ("PPIP_PARTY_ID".equals(inputParamEnumId)) {
1432:                    if (partyId != null) {
1433:                        compareBase = new Integer(partyId.compareTo(condValue));
1434:                    } else {
1435:                        compareBase = new Integer(1);
1436:                    }
1437:                } else if ("PPIP_PARTY_GRP_MEM".equals(inputParamEnumId)) {
1438:                    if (UtilValidate.isEmpty(partyId)) {
1439:                        compareBase = new Integer(1);
1440:                    } else {
1441:                        String groupPartyId = condValue;
1442:                        if (partyId.equals(groupPartyId)) {
1443:                            compareBase = new Integer(0);
1444:                        } else {
1445:                            // look for PartyRelationship with partyRelationshipTypeId=GROUP_ROLLUP, the partyIdTo is the group member, so the partyIdFrom is the groupPartyId
1446:                            List partyRelationshipList = delegator
1447:                                    .findByAndCache("PartyRelationship",
1448:                                            UtilMisc.toMap("partyIdFrom",
1449:                                                    groupPartyId, "partyIdTo",
1450:                                                    partyId,
1451:                                                    "partyRelationshipTypeId",
1452:                                                    "GROUP_ROLLUP"));
1453:                            // and from/thru date within range
1454:                            partyRelationshipList = EntityUtil.filterByDate(
1455:                                    partyRelationshipList, true);
1456:                            // then 0 (equals), otherwise 1 (not equals)
1457:                            if (partyRelationshipList != null
1458:                                    && partyRelationshipList.size() > 0) {
1459:                                compareBase = new Integer(0);
1460:                            } else {
1461:                                compareBase = new Integer(1);
1462:                            }
1463:                        }
1464:                    }
1465:                } else if ("PPIP_PARTY_CLASS".equals(inputParamEnumId)) {
1466:                    if (UtilValidate.isEmpty(partyId)) {
1467:                        compareBase = new Integer(1);
1468:                    } else {
1469:                        String partyClassificationGroupId = condValue;
1470:                        // find any PartyClassification
1471:                        List partyClassificationList = delegator
1472:                                .findByAndCache("PartyClassification", UtilMisc
1473:                                        .toMap("partyId", partyId,
1474:                                                "partyClassificationGroupId",
1475:                                                partyClassificationGroupId));
1476:                        // and from/thru date within range
1477:                        partyClassificationList = EntityUtil.filterByDate(
1478:                                partyClassificationList, true);
1479:                        // then 0 (equals), otherwise 1 (not equals)
1480:                        if (partyClassificationList != null
1481:                                && partyClassificationList.size() > 0) {
1482:                            compareBase = new Integer(0);
1483:                        } else {
1484:                            compareBase = new Integer(1);
1485:                        }
1486:                    }
1487:                } else if ("PPIP_ROLE_TYPE".equals(inputParamEnumId)) {
1488:                    if (partyId != null) {
1489:                        // if a PartyRole exists for this partyId and the specified roleTypeId
1490:                        GenericValue partyRole = delegator
1491:                                .findByPrimaryKeyCache("PartyRole", UtilMisc
1492:                                        .toMap("partyId", partyId,
1493:                                                "roleTypeId", condValue));
1494:
1495:                        // then 0 (equals), otherwise 1 (not equals)
1496:                        if (partyRole != null) {
1497:                            compareBase = new Integer(0);
1498:                        } else {
1499:                            compareBase = new Integer(1);
1500:                        }
1501:                    } else {
1502:                        compareBase = new Integer(1);
1503:                    }
1504:                } else if ("PPIP_ORDER_TOTAL".equals(inputParamEnumId)) {
1505:                    Double orderSubTotal = new Double(cart
1506:                            .getSubTotalForPromotions());
1507:                    if (Debug.verboseOn())
1508:                        Debug.logVerbose(
1509:                                "Doing order total compare: orderSubTotal="
1510:                                        + orderSubTotal, module);
1511:                    compareBase = new Integer(orderSubTotal.compareTo(Double
1512:                            .valueOf(condValue)));
1513:                } else if ("PPIP_ORST_HIST".equals(inputParamEnumId)) {
1514:                    // description="Order sub-total X in last Y Months"
1515:                    if (partyId != null && userLogin != null) {
1516:                        // call the getOrderedSummaryInformation service to get the sub-total
1517:                        int monthsToInclude = 12;
1518:                        if (otherValue != null) {
1519:                            monthsToInclude = Integer.parseInt(condValue);
1520:                        }
1521:                        Map serviceIn = UtilMisc.toMap("partyId", partyId,
1522:                                "roleTypeId", "PLACING_CUSTOMER",
1523:                                "orderTypeId", "SALES_ORDER", "statusId",
1524:                                "ORDER_COMPLETED", "monthsToInclude",
1525:                                new Integer(monthsToInclude), "userLogin",
1526:                                userLogin);
1527:                        try {
1528:                            Map result = dispatcher.runSync(
1529:                                    "getOrderedSummaryInformation", serviceIn);
1530:                            if (ServiceUtil.isError(result)) {
1531:                                Debug
1532:                                        .logError(
1533:                                                "Error calling getOrderedSummaryInformation service for the PPIP_ORST_HIST ProductPromo condition input value: "
1534:                                                        + ServiceUtil
1535:                                                                .getErrorMessage(result),
1536:                                                module);
1537:                                return false;
1538:                            } else {
1539:                                Double orderSubTotal = (Double) result
1540:                                        .get("totalSubRemainingAmount");
1541:                                if (Debug.verboseOn())
1542:                                    Debug.logVerbose(
1543:                                            "Doing order history sub-total compare: orderSubTotal="
1544:                                                    + orderSubTotal
1545:                                                    + ", for the last "
1546:                                                    + monthsToInclude
1547:                                                    + " months.", module);
1548:                                compareBase = new Integer(orderSubTotal
1549:                                        .compareTo(Double.valueOf(condValue)));
1550:                            }
1551:                        } catch (GenericServiceException e) {
1552:                            Debug
1553:                                    .logError(
1554:                                            e,
1555:                                            "Error getting order history sub-total in the getOrderedSummaryInformation service, evaluating condition to false.",
1556:                                            module);
1557:                            return false;
1558:                        }
1559:                    } else {
1560:                        return false;
1561:                    }
1562:                } else {
1563:                    Debug
1564:                            .logWarning(
1565:                                    UtilProperties
1566:                                            .getMessage(
1567:                                                    resource_error,
1568:                                                    "OrderAnUnSupportedProductPromoCondInputParameterLhs",
1569:                                                    UtilMisc
1570:                                                            .toMap(
1571:                                                                    "inputParamEnumId",
1572:                                                                    productPromoCond
1573:                                                                            .getString("inputParamEnumId")),
1574:                                                    cart.getLocale()), module);
1575:                    return false;
1576:                }
1577:
1578:                if (Debug.verboseOn())
1579:                    Debug.logVerbose("Condition compare done, compareBase="
1580:                            + compareBase, module);
1581:
1582:                if (compareBase != null) {
1583:                    int compare = compareBase.intValue();
1584:                    if ("PPC_EQ".equals(operatorEnumId)) {
1585:                        if (compare == 0)
1586:                            return true;
1587:                    } else if ("PPC_NEQ".equals(operatorEnumId)) {
1588:                        if (compare != 0)
1589:                            return true;
1590:                    } else if ("PPC_LT".equals(operatorEnumId)) {
1591:                        if (compare < 0)
1592:                            return true;
1593:                    } else if ("PPC_LTE".equals(operatorEnumId)) {
1594:                        if (compare <= 0)
1595:                            return true;
1596:                    } else if ("PPC_GT".equals(operatorEnumId)) {
1597:                        if (compare > 0)
1598:                            return true;
1599:                    } else if ("PPC_GTE".equals(operatorEnumId)) {
1600:                        if (compare >= 0)
1601:                            return true;
1602:                    } else {
1603:                        Debug
1604:                                .logWarning(
1605:                                        UtilProperties
1606:                                                .getMessage(
1607:                                                        resource_error,
1608:                                                        "OrderAnUnSupportedProductPromoCondCondition",
1609:                                                        UtilMisc
1610:                                                                .toMap(
1611:                                                                        "operatorEnumId",
1612:                                                                        operatorEnumId),
1613:                                                        cart.getLocale()),
1614:                                        module);
1615:                        return false;
1616:                    }
1617:                }
1618:                // default to not meeting the condition
1619:                return false;
1620:            }
1621:
1622:            public static class ActionResultInfo {
1623:                public boolean ranAction = false;
1624:                public double totalDiscountAmount = 0;
1625:                public double quantityLeftInAction = 0;
1626:            }
1627:
1628:            /** returns true if the cart was changed and rules need to be re-evaluted */
1629:            protected static ActionResultInfo performAction(
1630:                    GenericValue productPromoAction, ShoppingCart cart,
1631:                    GenericDelegator delegator, LocalDispatcher dispatcher,
1632:                    Timestamp nowTimestamp) throws GenericEntityException,
1633:                    CartItemModifyException {
1634:                ActionResultInfo actionResultInfo = new ActionResultInfo();
1635:
1636:                String productPromoActionEnumId = productPromoAction
1637:                        .getString("productPromoActionEnumId");
1638:
1639:                if ("PROMO_GWP".equals(productPromoActionEnumId)) {
1640:                    String productStoreId = cart.getProductStoreId();
1641:
1642:                    // the code was in there for this, so even though I don't think we want to restrict this, just adding this flag to make it easy to change; could make option dynamic, but now implied by the use limit
1643:                    boolean allowMultipleGwp = true;
1644:
1645:                    Integer itemLoc = findPromoItem(productPromoAction, cart);
1646:                    if (!allowMultipleGwp && itemLoc != null) {
1647:                        if (Debug.verboseOn())
1648:                            Debug.logVerbose(
1649:                                    "Not adding promo item, already there; action: "
1650:                                            + productPromoAction, module);
1651:                        actionResultInfo.ranAction = false;
1652:                    } else {
1653:                        double quantity = productPromoAction.get("quantity") == null ? 0.0
1654:                                : productPromoAction.getDouble("quantity")
1655:                                        .doubleValue();
1656:
1657:                        List optionProductIds = FastList.newInstance();
1658:                        String productId = productPromoAction
1659:                                .getString("productId");
1660:
1661:                        GenericValue product = null;
1662:                        if (UtilValidate.isNotEmpty(productId)) {
1663:                            // Debug.logInfo("======== Got GWP productId [" + productId + "]", module);
1664:                            product = delegator.findByPrimaryKeyCache(
1665:                                    "Product", UtilMisc.toMap("productId",
1666:                                            productId));
1667:                            if (product == null) {
1668:                                String errMsg = "GWP Product not found with ID ["
1669:                                        + productId
1670:                                        + "] for ProductPromoAction ["
1671:                                        + productPromoAction
1672:                                                .get("productPromoId")
1673:                                        + ":"
1674:                                        + productPromoAction
1675:                                                .get("productPromoRuleId")
1676:                                        + ":"
1677:                                        + productPromoAction
1678:                                                .get("productPromoActionSeqId")
1679:                                        + "]";
1680:                                Debug.logError(errMsg, module);
1681:                                throw new CartItemModifyException(errMsg);
1682:                            }
1683:                            if ("Y".equals(product.getString("isVirtual"))) {
1684:                                List productAssocs = EntityUtil
1685:                                        .filterByDate(
1686:                                                product
1687:                                                        .getRelatedCache(
1688:                                                                "MainProductAssoc",
1689:                                                                UtilMisc
1690:                                                                        .toMap(
1691:                                                                                "productAssocTypeId",
1692:                                                                                "PRODUCT_VARIANT"),
1693:                                                                UtilMisc
1694:                                                                        .toList("sequenceNum")),
1695:                                                true);
1696:                                Iterator productAssocIter = productAssocs
1697:                                        .iterator();
1698:                                while (productAssocIter.hasNext()) {
1699:                                    GenericValue productAssoc = (GenericValue) productAssocIter
1700:                                            .next();
1701:                                    optionProductIds.add(productAssoc
1702:                                            .get("productIdTo"));
1703:                                }
1704:                                productId = null;
1705:                                product = null;
1706:                                // Debug.logInfo("======== GWP productId [" + productId + "] is a virtual with " + productAssocs.size() + " variants", module);
1707:                            } else {
1708:                                // check inventory on this product, make sure it is available before going on
1709:                                //NOTE: even though the store may not require inventory for purchase, we will always require inventory for gifts
1710:                                try {
1711:                                    Map invReqResult = dispatcher.runSync(
1712:                                            "isStoreInventoryAvailable",
1713:                                            UtilMisc.toMap("productStoreId",
1714:                                                    productStoreId,
1715:                                                    "productId", productId,
1716:                                                    "product", product,
1717:                                                    "quantity", new Double(
1718:                                                            quantity)));
1719:                                    if (ServiceUtil.isError(invReqResult)) {
1720:                                        Debug.logError(
1721:                                                "Error calling isStoreInventoryAvailable service, result is: "
1722:                                                        + invReqResult, module);
1723:                                        throw new CartItemModifyException(
1724:                                                (String) invReqResult
1725:                                                        .get(ModelService.ERROR_MESSAGE));
1726:                                    } else if (!"Y"
1727:                                            .equals((String) invReqResult
1728:                                                    .get("available"))) {
1729:                                        productId = null;
1730:                                        product = null;
1731:                                        Debug
1732:                                                .logWarning(
1733:                                                        UtilProperties
1734:                                                                .getMessage(
1735:                                                                        resource_error,
1736:                                                                        "OrderNotApplyingGwpBecauseProductIdIsOutOfStockForProductPromoAction",
1737:                                                                        cart
1738:                                                                                .getLocale()),
1739:                                                        module);
1740:                                    }
1741:                                } catch (GenericServiceException e) {
1742:                                    String errMsg = "Fatal error calling inventory checking services: "
1743:                                            + e.toString();
1744:                                    Debug.logError(e, errMsg, module);
1745:                                    throw new CartItemModifyException(errMsg);
1746:                                }
1747:                            }
1748:                        }
1749:
1750:                        // support multiple gift options if products are attached to the action, or if the productId on the action is a virtual product
1751:                        Set productIds = ProductPromoWorker
1752:                                .getPromoRuleActionProductIds(
1753:                                        productPromoAction, delegator,
1754:                                        nowTimestamp);
1755:                        if (productIds != null) {
1756:                            optionProductIds.addAll(productIds);
1757:                        }
1758:
1759:                        // make sure these optionProducts have inventory...
1760:                        Iterator optionProductIdIter = optionProductIds
1761:                                .iterator();
1762:                        while (optionProductIdIter.hasNext()) {
1763:                            String optionProductId = (String) optionProductIdIter
1764:                                    .next();
1765:
1766:                            try {
1767:                                Map invReqResult = dispatcher.runSync(
1768:                                        "isStoreInventoryAvailable", UtilMisc
1769:                                                .toMap("productStoreId",
1770:                                                        productStoreId,
1771:                                                        "productId",
1772:                                                        optionProductId,
1773:                                                        "product", product,
1774:                                                        "quantity", new Double(
1775:                                                                quantity)));
1776:                                if (ServiceUtil.isError(invReqResult)) {
1777:                                    Debug.logError(
1778:                                            "Error calling isStoreInventoryAvailable service, result is: "
1779:                                                    + invReqResult, module);
1780:                                    throw new CartItemModifyException(
1781:                                            (String) invReqResult
1782:                                                    .get(ModelService.ERROR_MESSAGE));
1783:                                } else if (!"Y".equals((String) invReqResult
1784:                                        .get("available"))) {
1785:                                    optionProductIdIter.remove();
1786:                                }
1787:                            } catch (GenericServiceException e) {
1788:                                String errMsg = "Fatal error calling inventory checking services: "
1789:                                        + e.toString();
1790:                                Debug.logError(e, errMsg, module);
1791:                                throw new CartItemModifyException(errMsg);
1792:                            }
1793:                        }
1794:
1795:                        // check to see if any desired productIds have been selected for this promo action
1796:                        String alternateGwpProductId = cart
1797:                                .getDesiredAlternateGiftByAction(productPromoAction
1798:                                        .getPrimaryKey());
1799:                        if (UtilValidate.isNotEmpty(alternateGwpProductId)) {
1800:                            // also check to make sure this isn't a spoofed ID somehow, check to see if it is in the Set
1801:                            if (optionProductIds
1802:                                    .contains(alternateGwpProductId)) {
1803:                                if (UtilValidate.isNotEmpty(productId)) {
1804:                                    optionProductIds.add(productId);
1805:                                }
1806:                                optionProductIds.remove(alternateGwpProductId);
1807:                                productId = alternateGwpProductId;
1808:                                product = delegator.findByPrimaryKeyCache(
1809:                                        "Product", UtilMisc.toMap("productId",
1810:                                                productId));
1811:                            } else {
1812:                                Debug
1813:                                        .logWarning(
1814:                                                UtilProperties
1815:                                                        .getMessage(
1816:                                                                resource_error,
1817:                                                                "OrderAnAlternateGwpProductIdWasInPlaceButWasEitherNotValidOrIsNoLongerInStockForId",
1818:                                                                UtilMisc
1819:                                                                        .toMap(
1820:                                                                                "alternateGwpProductId",
1821:                                                                                alternateGwpProductId),
1822:                                                                cart
1823:                                                                        .getLocale()),
1824:                                                module);
1825:                            }
1826:                        }
1827:
1828:                        // if product is null, get one from the productIds set
1829:                        if (product == null && optionProductIds.size() > 0) {
1830:                            // get the first from an iterator and remove it since it will be the current one
1831:                            Iterator optionProductIdTempIter = optionProductIds
1832:                                    .iterator();
1833:                            productId = (String) optionProductIdTempIter.next();
1834:                            optionProductIdTempIter.remove();
1835:                            product = delegator.findByPrimaryKeyCache(
1836:                                    "Product", UtilMisc.toMap("productId",
1837:                                            productId));
1838:                        }
1839:
1840:                        if (product == null) {
1841:                            // no product found to add as GWP, just return
1842:                            return actionResultInfo;
1843:                        }
1844:
1845:                        // pass null for cartLocation to add to end of cart, pass false for doPromotions to avoid infinite recursion
1846:                        ShoppingCartItem gwpItem = null;
1847:                        try {
1848:                            // just leave the prodCatalogId null, this line won't be associated with a catalog
1849:                            String prodCatalogId = null;
1850:                            gwpItem = ShoppingCartItem.makeItem(null, product,
1851:                                    null, quantity, null, null, null, null,
1852:                                    null, null, null, null, prodCatalogId,
1853:                                    null, null, null, dispatcher, cart,
1854:                                    Boolean.FALSE, Boolean.TRUE, null,
1855:                                    Boolean.FALSE, Boolean.FALSE);
1856:                            if (optionProductIds.size() > 0) {
1857:                                gwpItem
1858:                                        .setAlternativeOptionProductIds(optionProductIds);
1859:                            } else {
1860:                                gwpItem.setAlternativeOptionProductIds(null);
1861:                            }
1862:                        } catch (CartItemModifyException e) {
1863:                            int gwpItemIndex = cart.getItemIndex(gwpItem);
1864:                            cart.removeCartItem(gwpItemIndex, dispatcher);
1865:                            throw e;
1866:                        }
1867:
1868:                        double discountAmount = -(quantity * gwpItem
1869:                                .getBasePrice());
1870:
1871:                        doOrderItemPromoAction(productPromoAction, gwpItem,
1872:                                discountAmount, "amount", delegator);
1873:
1874:                        // set promo after create; note that to setQuantity we must clear this flag, setQuantity, then re-set the flag
1875:                        gwpItem.setIsPromo(true);
1876:                        if (Debug.verboseOn())
1877:                            Debug.logVerbose("gwpItem adjustments: "
1878:                                    + gwpItem.getAdjustments(), module);
1879:
1880:                        actionResultInfo.ranAction = true;
1881:                        actionResultInfo.totalDiscountAmount = discountAmount;
1882:                    }
1883:                } else if ("PROMO_FREE_SHIPPING"
1884:                        .equals(productPromoActionEnumId)) {
1885:                    // this may look a bit funny: on each pass all rules that do free shipping will set their own rule id for it,
1886:                    // and on unapply if the promo and rule ids are the same then it will clear it; essentially on any pass
1887:                    // through the promos and rules if any free shipping should be there, it will be there
1888:                    cart.addFreeShippingProductPromoAction(productPromoAction);
1889:                    // don't consider this as a cart change?
1890:                    actionResultInfo.ranAction = true;
1891:                    // should probably set the totalDiscountAmount to something, but we have no idea what it will be, so leave at 0, will still get run
1892:                } else if ("PROMO_PROD_DISC".equals(productPromoActionEnumId)) {
1893:                    double quantityDesired = productPromoAction.get("quantity") == null ? 1.0
1894:                            : productPromoAction.getDouble("quantity")
1895:                                    .doubleValue();
1896:                    double startingQuantity = quantityDesired;
1897:                    double discountAmountTotal = 0;
1898:
1899:                    Set productIds = ProductPromoWorker
1900:                            .getPromoRuleActionProductIds(productPromoAction,
1901:                                    delegator, nowTimestamp);
1902:
1903:                    List lineOrderedByBasePriceList = cart
1904:                            .getLineListOrderedByBasePrice(false);
1905:                    Iterator lineOrderedByBasePriceIter = lineOrderedByBasePriceList
1906:                            .iterator();
1907:                    while (quantityDesired > 0
1908:                            && lineOrderedByBasePriceIter.hasNext()) {
1909:                        ShoppingCartItem cartItem = (ShoppingCartItem) lineOrderedByBasePriceIter
1910:                                .next();
1911:                        // only include if it is in the productId Set for this check and if it is not a Promo (GWP) item
1912:                        GenericValue product = cartItem.getProduct();
1913:                        String parentProductId = cartItem.getParentProductId();
1914:                        if (!cartItem.getIsPromo()
1915:                                && (productIds
1916:                                        .contains(cartItem.getProductId()) || (parentProductId != null && productIds
1917:                                        .contains(parentProductId)))
1918:                                && (product == null || !"N".equals(product
1919:                                        .getString("includeInPromotions")))) {
1920:                            // reduce quantity still needed to qualify for promo (quantityNeeded)
1921:                            double quantityUsed = cartItem
1922:                                    .addPromoQuantityCandidateUse(
1923:                                            quantityDesired,
1924:                                            productPromoAction, false);
1925:                            if (quantityUsed > 0) {
1926:                                quantityDesired -= quantityUsed;
1927:
1928:                                // create an adjustment and add it to the cartItem that implements the promotion action
1929:                                double percentModifier = productPromoAction
1930:                                        .get("amount") == null ? 0.0
1931:                                        : (productPromoAction.getDouble(
1932:                                                "amount").doubleValue() / 100.0);
1933:                                double lineAmount = quantityUsed
1934:                                        * cartItem.getBasePrice()
1935:                                        * cartItem.getRentalAdjustment();
1936:                                double discountAmount = -(lineAmount * percentModifier);
1937:                                discountAmountTotal += discountAmount;
1938:                                // not doing this any more, now distributing among conditions and actions (see call below): doOrderItemPromoAction(productPromoAction, cartItem, discountAmount, "amount", delegator);
1939:                            }
1940:                        }
1941:                    }
1942:
1943:                    if (quantityDesired == startingQuantity
1944:                            || quantityDesired > 0) {
1945:                        // couldn't find any (or enough) cart items to give a discount to, don't consider action run
1946:                        actionResultInfo.ranAction = false;
1947:                        // clear out any action uses for this so they don't become part of anything else
1948:                        cart.resetPromoRuleUse(productPromoAction
1949:                                .getString("productPromoId"),
1950:                                productPromoAction
1951:                                        .getString("productPromoRuleId"));
1952:                    } else {
1953:                        double totalAmount = getCartItemsUsedTotalAmount(cart,
1954:                                productPromoAction);
1955:                        if (Debug.verboseOn())
1956:                            Debug.logVerbose("Applying promo ["
1957:                                    + productPromoAction.getPrimaryKey()
1958:                                    + "]\n totalAmount=" + totalAmount
1959:                                    + ", discountAmountTotal="
1960:                                    + discountAmountTotal, module);
1961:                        distributeDiscountAmount(discountAmountTotal,
1962:                                totalAmount, getCartItemsUsed(cart,
1963:                                        productPromoAction),
1964:                                productPromoAction, delegator);
1965:                        actionResultInfo.ranAction = true;
1966:                        actionResultInfo.totalDiscountAmount = discountAmountTotal;
1967:                        actionResultInfo.quantityLeftInAction = quantityDesired;
1968:                    }
1969:                } else if ("PROMO_PROD_AMDISC".equals(productPromoActionEnumId)) {
1970:                    double quantityDesired = productPromoAction.get("quantity") == null ? 1.0
1971:                            : productPromoAction.getDouble("quantity")
1972:                                    .doubleValue();
1973:                    double startingQuantity = quantityDesired;
1974:                    double discountAmountTotal = 0;
1975:
1976:                    Set productIds = ProductPromoWorker
1977:                            .getPromoRuleActionProductIds(productPromoAction,
1978:                                    delegator, nowTimestamp);
1979:
1980:                    List lineOrderedByBasePriceList = cart
1981:                            .getLineListOrderedByBasePrice(false);
1982:                    Iterator lineOrderedByBasePriceIter = lineOrderedByBasePriceList
1983:                            .iterator();
1984:                    while (quantityDesired > 0
1985:                            && lineOrderedByBasePriceIter.hasNext()) {
1986:                        ShoppingCartItem cartItem = (ShoppingCartItem) lineOrderedByBasePriceIter
1987:                                .next();
1988:                        // only include if it is in the productId Set for this check and if it is not a Promo (GWP) item
1989:                        String parentProductId = cartItem.getParentProductId();
1990:                        GenericValue product = cartItem.getProduct();
1991:                        if (!cartItem.getIsPromo()
1992:                                && (productIds
1993:                                        .contains(cartItem.getProductId()) || (parentProductId != null && productIds
1994:                                        .contains(parentProductId)))
1995:                                && (product == null || !"N".equals(product
1996:                                        .getString("includeInPromotions")))) {
1997:                            // reduce quantity still needed to qualify for promo (quantityNeeded)
1998:                            double quantityUsed = cartItem
1999:                                    .addPromoQuantityCandidateUse(
2000:                                            quantityDesired,
2001:                                            productPromoAction, false);
2002:                            quantityDesired -= quantityUsed;
2003:
2004:                            // create an adjustment and add it to the cartItem that implements the promotion action
2005:                            double discount = productPromoAction.get("amount") == null ? 0.0
2006:                                    : productPromoAction.getDouble("amount")
2007:                                            .doubleValue();
2008:                            // don't allow the discount to be greater than the price
2009:                            if (discount > cartItem.getBasePrice()
2010:                                    * cartItem.getRentalAdjustment()) {
2011:                                discount = cartItem.getBasePrice()
2012:                                        * cartItem.getRentalAdjustment();
2013:                            }
2014:                            double discountAmount = -(quantityUsed * discount);
2015:                            discountAmountTotal += discountAmount;
2016:                            // not doing this any more, now distributing among conditions and actions (see call below): doOrderItemPromoAction(productPromoAction, cartItem, discountAmount, "amount", delegator);
2017:                        }
2018:                    }
2019:
2020:                    if (quantityDesired == startingQuantity) {
2021:                        // couldn't find any cart items to give a discount to, don't consider action run
2022:                        actionResultInfo.ranAction = false;
2023:                    } else {
2024:                        double totalAmount = getCartItemsUsedTotalAmount(cart,
2025:                                productPromoAction);
2026:                        if (Debug.verboseOn())
2027:                            Debug.logVerbose("Applying promo ["
2028:                                    + productPromoAction.getPrimaryKey()
2029:                                    + "]\n totalAmount=" + totalAmount
2030:                                    + ", discountAmountTotal="
2031:                                    + discountAmountTotal, module);
2032:                        distributeDiscountAmount(discountAmountTotal,
2033:                                totalAmount, getCartItemsUsed(cart,
2034:                                        productPromoAction),
2035:                                productPromoAction, delegator);
2036:                        actionResultInfo.ranAction = true;
2037:                        actionResultInfo.totalDiscountAmount = discountAmountTotal;
2038:                        actionResultInfo.quantityLeftInAction = quantityDesired;
2039:                    }
2040:                } else if ("PROMO_PROD_PRICE".equals(productPromoActionEnumId)) {
2041:                    // with this we want the set of used items to be one price, so total the price for all used items, subtract the amount we want them to cost, and create an adjustment for what is left
2042:                    double quantityDesired = productPromoAction.get("quantity") == null ? 1.0
2043:                            : productPromoAction.getDouble("quantity")
2044:                                    .doubleValue();
2045:                    double desiredAmount = productPromoAction.get("amount") == null ? 0.0
2046:                            : productPromoAction.getDouble("amount")
2047:                                    .doubleValue();
2048:                    double totalAmount = 0;
2049:
2050:                    Set productIds = ProductPromoWorker
2051:                            .getPromoRuleActionProductIds(productPromoAction,
2052:                                    delegator, nowTimestamp);
2053:
2054:                    List cartItemsUsed = FastList.newInstance();
2055:                    List lineOrderedByBasePriceList = cart
2056:                            .getLineListOrderedByBasePrice(false);
2057:                    Iterator lineOrderedByBasePriceIter = lineOrderedByBasePriceList
2058:                            .iterator();
2059:                    while (quantityDesired > 0
2060:                            && lineOrderedByBasePriceIter.hasNext()) {
2061:                        ShoppingCartItem cartItem = (ShoppingCartItem) lineOrderedByBasePriceIter
2062:                                .next();
2063:                        // only include if it is in the productId Set for this check and if it is not a Promo (GWP) item
2064:                        String parentProductId = cartItem.getParentProductId();
2065:                        GenericValue product = cartItem.getProduct();
2066:                        if (!cartItem.getIsPromo()
2067:                                && (productIds
2068:                                        .contains(cartItem.getProductId()) || (parentProductId != null && productIds
2069:                                        .contains(parentProductId)))
2070:                                && (product == null || !"N".equals(product
2071:                                        .getString("includeInPromotions")))) {
2072:                            // reduce quantity still needed to qualify for promo (quantityNeeded)
2073:                            double quantityUsed = cartItem
2074:                                    .addPromoQuantityCandidateUse(
2075:                                            quantityDesired,
2076:                                            productPromoAction, false);
2077:                            if (quantityUsed > 0) {
2078:                                quantityDesired -= quantityUsed;
2079:                                totalAmount += quantityUsed
2080:                                        * cartItem.getBasePrice()
2081:                                        * cartItem.getRentalAdjustment();
2082:                                cartItemsUsed.add(cartItem);
2083:                            }
2084:                        }
2085:                    }
2086:
2087:                    if (totalAmount > desiredAmount && quantityDesired == 0) {
2088:                        double discountAmountTotal = -(totalAmount - desiredAmount);
2089:                        distributeDiscountAmount(discountAmountTotal,
2090:                                totalAmount, cartItemsUsed, productPromoAction,
2091:                                delegator);
2092:                        actionResultInfo.ranAction = true;
2093:                        actionResultInfo.totalDiscountAmount = discountAmountTotal;
2094:                        // no use setting the quantityLeftInAction because that does not apply for buy X for $Y type promotions, it is all or nothing
2095:                    } else {
2096:                        actionResultInfo.ranAction = false;
2097:                        // clear out any action uses for this so they don't become part of anything else
2098:                        cart.resetPromoRuleUse(productPromoAction
2099:                                .getString("productPromoId"),
2100:                                productPromoAction
2101:                                        .getString("productPromoRuleId"));
2102:                    }
2103:                } else if ("PROMO_ORDER_PERCENT"
2104:                        .equals(productPromoActionEnumId)) {
2105:                    double percentage = -(productPromoAction.get("amount") == null ? 0.0
2106:                            : (productPromoAction.getDouble("amount")
2107:                                    .doubleValue() / 100.0));
2108:                    double amount = cart.getSubTotalForPromotions()
2109:                            * percentage;
2110:                    if (amount != 0) {
2111:                        doOrderPromoAction(productPromoAction, cart, amount,
2112:                                "amount", delegator);
2113:                        actionResultInfo.ranAction = true;
2114:                        actionResultInfo.totalDiscountAmount = amount;
2115:                    }
2116:                } else if ("PROMO_ORDER_AMOUNT"
2117:                        .equals(productPromoActionEnumId)) {
2118:                    double amount = -(productPromoAction.get("amount") == null ? 0.0
2119:                            : productPromoAction.getDouble("amount")
2120:                                    .doubleValue());
2121:                    // if amount is greater than the order sub total, set equal to order sub total, this normally wouldn't happen because there should be a condition that the order total be above a certain amount, but just in case...
2122:                    double subTotal = cart.getSubTotalForPromotions();
2123:                    if (-amount > subTotal) {
2124:                        amount = -subTotal;
2125:                    }
2126:                    if (amount != 0) {
2127:                        doOrderPromoAction(productPromoAction, cart, amount,
2128:                                "amount", delegator);
2129:                        actionResultInfo.ranAction = true;
2130:                        actionResultInfo.totalDiscountAmount = amount;
2131:                    }
2132:                } else if ("PROMO_PROD_SPPRC".equals(productPromoActionEnumId)) {
2133:                    // if there are productIds associated with the action then restrict to those productIds, otherwise apply for all products
2134:                    Set productIds = ProductPromoWorker
2135:                            .getPromoRuleActionProductIds(productPromoAction,
2136:                                    delegator, nowTimestamp);
2137:
2138:                    // go through the cart items and for each product that has a specialPromoPrice use that price
2139:                    Iterator cartItemIter = cart.items().iterator();
2140:                    while (cartItemIter.hasNext()) {
2141:                        ShoppingCartItem cartItem = (ShoppingCartItem) cartItemIter
2142:                                .next();
2143:                        String itemProductId = cartItem.getProductId();
2144:                        if (UtilValidate.isEmpty(itemProductId)) {
2145:                            continue;
2146:                        }
2147:
2148:                        if (productIds.size() > 0
2149:                                && !productIds.contains(itemProductId)) {
2150:                            continue;
2151:                        }
2152:
2153:                        if (cartItem.getSpecialPromoPrice() == null) {
2154:                            continue;
2155:                        }
2156:
2157:                        // get difference between basePrice and specialPromoPrice and adjust for that
2158:                        double difference = -(cartItem.getBasePrice()
2159:                                * cartItem.getRentalAdjustment() - cartItem
2160:                                .getSpecialPromoPrice().doubleValue());
2161:
2162:                        if (difference != 0.0) {
2163:                            double quantityUsed = cartItem
2164:                                    .addPromoQuantityCandidateUse(cartItem
2165:                                            .getQuantity(), productPromoAction,
2166:                                            false);
2167:                            if (quantityUsed > 0) {
2168:                                double amount = difference * quantityUsed;
2169:                                doOrderItemPromoAction(productPromoAction,
2170:                                        cartItem, amount, "amount", delegator);
2171:                                actionResultInfo.ranAction = true;
2172:                                actionResultInfo.totalDiscountAmount = amount;
2173:                            }
2174:                        }
2175:                    }
2176:                } else {
2177:                    Debug.logError(
2178:                            "An un-supported productPromoActionType was used: "
2179:                                    + productPromoActionEnumId
2180:                                    + ", not performing any action", module);
2181:                    actionResultInfo.ranAction = false;
2182:                }
2183:
2184:                if (actionResultInfo.ranAction) {
2185:                    // in action, if doesn't have enough quantity to use the promo at all, remove candidate promo uses and increment promoQuantityUsed; this should go for all actions, if any action runs we confirm
2186:                    cart.confirmPromoRuleUse(productPromoAction
2187:                            .getString("productPromoId"), productPromoAction
2188:                            .getString("productPromoRuleId"));
2189:                } else {
2190:                    cart.resetPromoRuleUse(productPromoAction
2191:                            .getString("productPromoId"), productPromoAction
2192:                            .getString("productPromoRuleId"));
2193:                }
2194:
2195:                return actionResultInfo;
2196:            }
2197:
2198:            protected static List getCartItemsUsed(ShoppingCart cart,
2199:                    GenericValue productPromoAction) {
2200:                List cartItemsUsed = FastList.newInstance();
2201:                Iterator cartItemsIter = cart.iterator();
2202:                while (cartItemsIter.hasNext()) {
2203:                    ShoppingCartItem cartItem = (ShoppingCartItem) cartItemsIter
2204:                            .next();
2205:                    double quantityUsed = cartItem
2206:                            .getPromoQuantityCandidateUseActionAndAllConds(productPromoAction);
2207:                    if (quantityUsed > 0) {
2208:                        cartItemsUsed.add(cartItem);
2209:                    }
2210:                }
2211:                return cartItemsUsed;
2212:            }
2213:
2214:            protected static double getCartItemsUsedTotalAmount(
2215:                    ShoppingCart cart, GenericValue productPromoAction) {
2216:                double totalAmount = 0;
2217:                Iterator cartItemsIter = cart.iterator();
2218:                while (cartItemsIter.hasNext()) {
2219:                    ShoppingCartItem cartItem = (ShoppingCartItem) cartItemsIter
2220:                            .next();
2221:                    double quantityUsed = cartItem
2222:                            .getPromoQuantityCandidateUseActionAndAllConds(productPromoAction);
2223:                    if (quantityUsed > 0) {
2224:                        totalAmount += quantityUsed * cartItem.getBasePrice();
2225:                    }
2226:                }
2227:                return totalAmount;
2228:            }
2229:
2230:            protected static void distributeDiscountAmount(
2231:                    double discountAmountTotal, double totalAmount,
2232:                    List cartItemsUsed, GenericValue productPromoAction,
2233:                    GenericDelegator delegator) {
2234:                double discountAmount = discountAmountTotal;
2235:                // distribute the discount evenly weighted according to price over the order items that the individual quantities came from; avoids a number of issues with tax/shipping calc, inclusion in the sub-total for other promotions, etc
2236:                Iterator cartItemsUsedIter = cartItemsUsed.iterator();
2237:                while (cartItemsUsedIter.hasNext()) {
2238:                    ShoppingCartItem cartItem = (ShoppingCartItem) cartItemsUsedIter
2239:                            .next();
2240:                    // to minimize rounding issues use the remaining total for the last one, otherwise use a calculated value
2241:                    if (cartItemsUsedIter.hasNext()) {
2242:                        double quantityUsed = cartItem
2243:                                .getPromoQuantityCandidateUseActionAndAllConds(productPromoAction);
2244:                        double ratioOfTotal = (quantityUsed * cartItem
2245:                                .getBasePrice())
2246:                                / totalAmount;
2247:                        double weightedAmount = ratioOfTotal
2248:                                * discountAmountTotal;
2249:                        // round the weightedAmount to 2 decimal places, ie a whole number of cents or 2 decimal place monetary units
2250:                        weightedAmount = weightedAmount * 100.0;
2251:                        long roundedAmount = Math.round(weightedAmount);
2252:                        weightedAmount = ((double) roundedAmount) / 100.0;
2253:                        discountAmount -= weightedAmount;
2254:                        doOrderItemPromoAction(productPromoAction, cartItem,
2255:                                weightedAmount, "amount", delegator);
2256:                    } else {
2257:                        // last one, just use discountAmount
2258:                        doOrderItemPromoAction(productPromoAction, cartItem,
2259:                                discountAmount, "amount", delegator);
2260:                    }
2261:                }
2262:                // this is the old way that causes problems: doOrderPromoAction(productPromoAction, cart, discountAmount, "amount", delegator);
2263:            }
2264:
2265:            protected static Integer findPromoItem(
2266:                    GenericValue productPromoAction, ShoppingCart cart) {
2267:                List cartItems = cart.items();
2268:
2269:                for (int i = 0; i < cartItems.size(); i++) {
2270:                    ShoppingCartItem checkItem = (ShoppingCartItem) cartItems
2271:                            .get(i);
2272:
2273:                    if (checkItem.getIsPromo()) {
2274:                        // found a promo item, see if it has a matching adjustment on it
2275:                        Iterator checkOrderAdjustments = UtilMisc
2276:                                .toIterator(checkItem.getAdjustments());
2277:                        while (checkOrderAdjustments != null
2278:                                && checkOrderAdjustments.hasNext()) {
2279:                            GenericValue checkOrderAdjustment = (GenericValue) checkOrderAdjustments
2280:                                    .next();
2281:                            if (productPromoAction.getString("productPromoId")
2282:                                    .equals(
2283:                                            checkOrderAdjustment
2284:                                                    .get("productPromoId"))
2285:                                    && productPromoAction.getString(
2286:                                            "productPromoRuleId").equals(
2287:                                            checkOrderAdjustment
2288:                                                    .get("productPromoRuleId"))
2289:                                    && productPromoAction
2290:                                            .getString(
2291:                                                    "productPromoActionSeqId")
2292:                                            .equals(
2293:                                                    checkOrderAdjustment
2294:                                                            .get("productPromoActionSeqId"))) {
2295:                                return new Integer(i);
2296:                            }
2297:                        }
2298:                    }
2299:                }
2300:                return null;
2301:            }
2302:
2303:            public static void doOrderItemPromoAction(
2304:                    GenericValue productPromoAction, ShoppingCartItem cartItem,
2305:                    double amount, String amountField,
2306:                    GenericDelegator delegator) {
2307:                GenericValue orderAdjustment = delegator.makeValue(
2308:                        "OrderAdjustment", UtilMisc.toMap(
2309:                                "orderAdjustmentTypeId",
2310:                                "PROMOTION_ADJUSTMENT", amountField,
2311:                                new Double(amount), "productPromoId",
2312:                                productPromoAction.get("productPromoId"),
2313:                                "productPromoRuleId", productPromoAction
2314:                                        .get("productPromoRuleId"),
2315:                                "productPromoActionSeqId", productPromoAction
2316:                                        .get("productPromoActionSeqId")));
2317:
2318:                // if an orderAdjustmentTypeId was included, override the default
2319:                if (UtilValidate.isNotEmpty(productPromoAction
2320:                        .getString("orderAdjustmentTypeId"))) {
2321:                    orderAdjustment.set("orderAdjustmentTypeId",
2322:                            productPromoAction.get("orderAdjustmentTypeId"));
2323:                }
2324:
2325:                cartItem.addAdjustment(orderAdjustment);
2326:            }
2327:
2328:            public static void doOrderPromoAction(
2329:                    GenericValue productPromoAction, ShoppingCart cart,
2330:                    double amount, String amountField,
2331:                    GenericDelegator delegator) {
2332:                GenericValue orderAdjustment = delegator.makeValue(
2333:                        "OrderAdjustment", UtilMisc.toMap(
2334:                                "orderAdjustmentTypeId",
2335:                                "PROMOTION_ADJUSTMENT", amountField,
2336:                                new Double(amount), "productPromoId",
2337:                                productPromoAction.get("productPromoId"),
2338:                                "productPromoRuleId", productPromoAction
2339:                                        .get("productPromoRuleId"),
2340:                                "productPromoActionSeqId", productPromoAction
2341:                                        .get("productPromoActionSeqId")));
2342:
2343:                // if an orderAdjustmentTypeId was included, override the default
2344:                if (UtilValidate.isNotEmpty(productPromoAction
2345:                        .getString("orderAdjustmentTypeId"))) {
2346:                    orderAdjustment.set("orderAdjustmentTypeId",
2347:                            productPromoAction.get("orderAdjustmentTypeId"));
2348:                }
2349:
2350:                cart.addAdjustment(orderAdjustment);
2351:            }
2352:
2353:            protected static Integer findAdjustment(
2354:                    GenericValue productPromoAction, List adjustments) {
2355:                for (int i = 0; i < adjustments.size(); i++) {
2356:                    GenericValue checkOrderAdjustment = (GenericValue) adjustments
2357:                            .get(i);
2358:
2359:                    if (productPromoAction.getString("productPromoId").equals(
2360:                            checkOrderAdjustment.get("productPromoId"))
2361:                            && productPromoAction.getString(
2362:                                    "productPromoRuleId").equals(
2363:                                    checkOrderAdjustment
2364:                                            .get("productPromoRuleId"))
2365:                            && productPromoAction.getString(
2366:                                    "productPromoActionSeqId").equals(
2367:                                    checkOrderAdjustment
2368:                                            .get("productPromoActionSeqId"))) {
2369:                        return new Integer(i);
2370:                    }
2371:                }
2372:                return null;
2373:            }
2374:
2375:            public static Set getPromoRuleCondProductIds(
2376:                    GenericValue productPromoCond, GenericDelegator delegator,
2377:                    Timestamp nowTimestamp) throws GenericEntityException {
2378:                // get a cached list for the whole promo and filter it as needed, this for better efficiency in caching
2379:                List productPromoCategoriesAll = delegator.findByAndCache(
2380:                        "ProductPromoCategory", UtilMisc.toMap(
2381:                                "productPromoId", productPromoCond
2382:                                        .get("productPromoId")));
2383:                List productPromoCategories = EntityUtil.filterByAnd(
2384:                        productPromoCategoriesAll, UtilMisc.toMap(
2385:                                "productPromoRuleId", "_NA_",
2386:                                "productPromoCondSeqId", "_NA_"));
2387:                productPromoCategories.addAll(EntityUtil.filterByAnd(
2388:                        productPromoCategoriesAll, UtilMisc.toMap(
2389:                                "productPromoRuleId", productPromoCond
2390:                                        .get("productPromoRuleId"),
2391:                                "productPromoCondSeqId", productPromoCond
2392:                                        .get("productPromoCondSeqId"))));
2393:
2394:                List productPromoProductsAll = delegator.findByAndCache(
2395:                        "ProductPromoProduct", UtilMisc.toMap("productPromoId",
2396:                                productPromoCond.get("productPromoId")));
2397:                List productPromoProducts = EntityUtil.filterByAnd(
2398:                        productPromoProductsAll, UtilMisc.toMap(
2399:                                "productPromoRuleId", "_NA_",
2400:                                "productPromoCondSeqId", "_NA_"));
2401:                productPromoProducts.addAll(EntityUtil.filterByAnd(
2402:                        productPromoProductsAll, UtilMisc.toMap(
2403:                                "productPromoRuleId", productPromoCond
2404:                                        .get("productPromoRuleId"),
2405:                                "productPromoCondSeqId", productPromoCond
2406:                                        .get("productPromoCondSeqId"))));
2407:
2408:                Set productIds = new HashSet();
2409:                makeProductPromoIdSet(productIds, productPromoCategories,
2410:                        productPromoProducts, delegator, nowTimestamp, false);
2411:                return productIds;
2412:            }
2413:
2414:            public static Set getPromoRuleActionProductIds(
2415:                    GenericValue productPromoAction,
2416:                    GenericDelegator delegator, Timestamp nowTimestamp)
2417:                    throws GenericEntityException {
2418:                // get a cached list for the whole promo and filter it as needed, this for better efficiency in caching
2419:                List productPromoCategoriesAll = delegator.findByAndCache(
2420:                        "ProductPromoCategory", UtilMisc.toMap(
2421:                                "productPromoId", productPromoAction
2422:                                        .get("productPromoId")));
2423:                List productPromoCategories = EntityUtil.filterByAnd(
2424:                        productPromoCategoriesAll, UtilMisc.toMap(
2425:                                "productPromoRuleId", "_NA_",
2426:                                "productPromoActionSeqId", "_NA_"));
2427:                productPromoCategories.addAll(EntityUtil.filterByAnd(
2428:                        productPromoCategoriesAll, UtilMisc.toMap(
2429:                                "productPromoRuleId", productPromoAction
2430:                                        .get("productPromoRuleId"),
2431:                                "productPromoActionSeqId", productPromoAction
2432:                                        .get("productPromoActionSeqId"))));
2433:
2434:                List productPromoProductsAll = delegator.findByAndCache(
2435:                        "ProductPromoProduct", UtilMisc.toMap("productPromoId",
2436:                                productPromoAction.get("productPromoId")));
2437:                List productPromoProducts = EntityUtil.filterByAnd(
2438:                        productPromoProductsAll, UtilMisc.toMap(
2439:                                "productPromoRuleId", "_NA_",
2440:                                "productPromoActionSeqId", "_NA_"));
2441:                productPromoProducts.addAll(EntityUtil.filterByAnd(
2442:                        productPromoProductsAll, UtilMisc.toMap(
2443:                                "productPromoRuleId", productPromoAction
2444:                                        .get("productPromoRuleId"),
2445:                                "productPromoActionSeqId", productPromoAction
2446:                                        .get("productPromoActionSeqId"))));
2447:
2448:                Set productIds = new HashSet();
2449:                makeProductPromoIdSet(productIds, productPromoCategories,
2450:                        productPromoProducts, delegator, nowTimestamp, false);
2451:                return productIds;
2452:            }
2453:
2454:            public static void makeProductPromoIdSet(Set productIds,
2455:                    List productPromoCategories, List productPromoProducts,
2456:                    GenericDelegator delegator, Timestamp nowTimestamp,
2457:                    boolean filterOldProducts) throws GenericEntityException {
2458:                // do the includes
2459:                handleProductPromoCategories(productIds,
2460:                        productPromoCategories, "PPPA_INCLUDE", delegator,
2461:                        nowTimestamp);
2462:                handleProductPromoProducts(productIds, productPromoProducts,
2463:                        "PPPA_INCLUDE");
2464:
2465:                // do the excludes
2466:                handleProductPromoCategories(productIds,
2467:                        productPromoCategories, "PPPA_EXCLUDE", delegator,
2468:                        nowTimestamp);
2469:                handleProductPromoProducts(productIds, productPromoProducts,
2470:                        "PPPA_EXCLUDE");
2471:
2472:                // do the always includes
2473:                handleProductPromoCategories(productIds,
2474:                        productPromoCategories, "PPPA_ALWAYS", delegator,
2475:                        nowTimestamp);
2476:                handleProductPromoProducts(productIds, productPromoProducts,
2477:                        "PPPA_ALWAYS");
2478:            }
2479:
2480:            public static void makeProductPromoCondActionIdSets(
2481:                    String productPromoId, Set productIdsCond,
2482:                    Set productIdsAction, GenericDelegator delegator,
2483:                    Timestamp nowTimestamp) throws GenericEntityException {
2484:                makeProductPromoCondActionIdSets(productPromoId,
2485:                        productIdsCond, productIdsAction, delegator,
2486:                        nowTimestamp, false);
2487:            }
2488:
2489:            public static void makeProductPromoCondActionIdSets(
2490:                    String productPromoId, Set productIdsCond,
2491:                    Set productIdsAction, GenericDelegator delegator,
2492:                    Timestamp nowTimestamp, boolean filterOldProducts)
2493:                    throws GenericEntityException {
2494:                if (nowTimestamp == null) {
2495:                    nowTimestamp = UtilDateTime.nowTimestamp();
2496:                }
2497:
2498:                List productPromoCategoriesAll = delegator.findByAndCache(
2499:                        "ProductPromoCategory", UtilMisc.toMap(
2500:                                "productPromoId", productPromoId));
2501:                List productPromoProductsAll = delegator.findByAndCache(
2502:                        "ProductPromoProduct", UtilMisc.toMap("productPromoId",
2503:                                productPromoId));
2504:
2505:                List productPromoProductsCond = FastList.newInstance();
2506:                List productPromoCategoriesCond = FastList.newInstance();
2507:                List productPromoProductsAction = FastList.newInstance();
2508:                List productPromoCategoriesAction = FastList.newInstance();
2509:
2510:                Iterator productPromoProductsAllIter = productPromoProductsAll
2511:                        .iterator();
2512:                while (productPromoProductsAllIter.hasNext()) {
2513:                    GenericValue productPromoProduct = (GenericValue) productPromoProductsAllIter
2514:                            .next();
2515:                    // if the rule id is null then this is a global promo one, so always include
2516:                    if (!"_NA_".equals(productPromoProduct
2517:                            .getString("productPromoCondSeqId"))
2518:                            || "_NA_".equals(productPromoProduct
2519:                                    .getString("productPromoRuleId"))) {
2520:                        productPromoProductsCond.add(productPromoProduct);
2521:                    }
2522:                    if (!"_NA_".equals(productPromoProduct
2523:                            .getString("productPromoActionSeqId"))
2524:                            || "_NA_".equals(productPromoProduct
2525:                                    .getString("productPromoRuleId"))) {
2526:                        productPromoProductsAction.add(productPromoProduct);
2527:                    }
2528:                }
2529:                Iterator productPromoCategoriesAllIter = productPromoCategoriesAll
2530:                        .iterator();
2531:                while (productPromoCategoriesAllIter.hasNext()) {
2532:                    GenericValue productPromoCategory = (GenericValue) productPromoCategoriesAllIter
2533:                            .next();
2534:                    if (!"_NA_".equals(productPromoCategory
2535:                            .getString("productPromoCondSeqId"))
2536:                            || "_NA_".equals(productPromoCategory
2537:                                    .getString("productPromoRuleId"))) {
2538:                        productPromoCategoriesCond.add(productPromoCategory);
2539:                    }
2540:                    if (!"_NA_".equals(productPromoCategory
2541:                            .getString("productPromoActionSeqId"))
2542:                            || "_NA_".equals(productPromoCategory
2543:                                    .getString("productPromoRuleId"))) {
2544:                        productPromoCategoriesAction.add(productPromoCategory);
2545:                    }
2546:                }
2547:
2548:                makeProductPromoIdSet(productIdsCond,
2549:                        productPromoCategoriesCond, productPromoProductsCond,
2550:                        delegator, nowTimestamp, filterOldProducts);
2551:                makeProductPromoIdSet(productIdsAction,
2552:                        productPromoCategoriesAction,
2553:                        productPromoProductsAction, delegator, nowTimestamp,
2554:                        filterOldProducts);
2555:
2556:                // last of all filterOldProducts, done here to make sure no product gets looked up twice
2557:                if (filterOldProducts) {
2558:                    Iterator productIdsCondIter = productIdsCond.iterator();
2559:                    while (productIdsCondIter.hasNext()) {
2560:                        String productId = (String) productIdsCondIter.next();
2561:                        if (isProductOld(productId, delegator, nowTimestamp)) {
2562:                            productIdsCondIter.remove();
2563:                        }
2564:                    }
2565:                    Iterator productIdsActionIter = productIdsAction.iterator();
2566:                    while (productIdsActionIter.hasNext()) {
2567:                        String productId = (String) productIdsActionIter.next();
2568:                        if (isProductOld(productId, delegator, nowTimestamp)) {
2569:                            productIdsActionIter.remove();
2570:                        }
2571:                    }
2572:                }
2573:            }
2574:
2575:            protected static boolean isProductOld(String productId,
2576:                    GenericDelegator delegator, Timestamp nowTimestamp)
2577:                    throws GenericEntityException {
2578:                GenericValue product = delegator.findByPrimaryKeyCache(
2579:                        "Product", UtilMisc.toMap("productId", productId));
2580:                if (product != null) {
2581:                    Timestamp salesDiscontinuationDate = product
2582:                            .getTimestamp("salesDiscontinuationDate");
2583:                    if (salesDiscontinuationDate != null
2584:                            && salesDiscontinuationDate.before(nowTimestamp)) {
2585:                        return true;
2586:                    }
2587:                }
2588:                return false;
2589:            }
2590:
2591:            protected static void handleProductPromoCategories(Set productIds,
2592:                    List productPromoCategories, String productPromoApplEnumId,
2593:                    GenericDelegator delegator, Timestamp nowTimestamp)
2594:                    throws GenericEntityException {
2595:                boolean include = !"PPPA_EXCLUDE"
2596:                        .equals(productPromoApplEnumId);
2597:                Set productCategoryIds = new HashSet();
2598:                Map productCategoryGroupSetListMap = new HashMap();
2599:
2600:                Iterator productPromoCategoryIter = productPromoCategories
2601:                        .iterator();
2602:                while (productPromoCategoryIter.hasNext()) {
2603:                    GenericValue productPromoCategory = (GenericValue) productPromoCategoryIter
2604:                            .next();
2605:                    if (productPromoApplEnumId.equals(productPromoCategory
2606:                            .getString("productPromoApplEnumId"))) {
2607:                        Set tempCatIdSet = new HashSet();
2608:                        if ("Y".equals(productPromoCategory
2609:                                .getString("includeSubCategories"))) {
2610:                            ProductSearch.getAllSubCategoryIds(
2611:                                    productPromoCategory
2612:                                            .getString("productCategoryId"),
2613:                                    tempCatIdSet, delegator, nowTimestamp);
2614:                        } else {
2615:                            tempCatIdSet.add(productPromoCategory
2616:                                    .getString("productCategoryId"));
2617:                        }
2618:
2619:                        String andGroupId = productPromoCategory
2620:                                .getString("andGroupId");
2621:                        if ("_NA_".equals(andGroupId)) {
2622:                            productCategoryIds.addAll(tempCatIdSet);
2623:                        } else {
2624:                            List catIdSetList = (List) productCategoryGroupSetListMap
2625:                                    .get(andGroupId);
2626:                            if (catIdSetList == null) {
2627:                                catIdSetList = FastList.newInstance();
2628:                            }
2629:                            catIdSetList.add(tempCatIdSet);
2630:                        }
2631:                    }
2632:                }
2633:
2634:                // for the ones with andGroupIds, if there is only one category move it to the productCategoryIds Set
2635:                // also remove all empty SetLists and Sets
2636:                Iterator pcgslmeIter = productCategoryGroupSetListMap
2637:                        .entrySet().iterator();
2638:                while (pcgslmeIter.hasNext()) {
2639:                    Map.Entry entry = (Map.Entry) pcgslmeIter.next();
2640:                    List catIdSetList = (List) entry.getValue();
2641:                    if (catIdSetList.size() == 0) {
2642:                        pcgslmeIter.remove();
2643:                    } else if (catIdSetList.size() == 1) {
2644:                        Set catIdSet = (Set) catIdSetList.iterator().next();
2645:                        if (catIdSet.size() == 0) {
2646:                            pcgslmeIter.remove();
2647:                        } else {
2648:                            // if there is only one set in the list since the set will be or'ed anyway, just add them all to the productCategoryIds Set
2649:                            productCategoryIds.addAll(catIdSet);
2650:                            pcgslmeIter.remove();
2651:                        }
2652:                    }
2653:                }
2654:
2655:                // now that the category Set and Map are setup, take care of the productCategoryIds Set first
2656:                getAllProductIds(productCategoryIds, productIds, delegator,
2657:                        nowTimestamp, include);
2658:
2659:                // now handle the productCategoryGroupSetListMap
2660:                // if a set has more than one category (because of an include sub-cats) then do an or
2661:                // all lists will have more than category because of the pre-pass that was done, so and them together
2662:                Iterator pcgslmIter = productCategoryGroupSetListMap.entrySet()
2663:                        .iterator();
2664:                while (pcgslmIter.hasNext()) {
2665:                    Map.Entry entry = (Map.Entry) pcgslmIter.next();
2666:                    List catIdSetList = (List) entry.getValue();
2667:                    // get all productIds for this catIdSetList
2668:                    List productIdSetList = FastList.newInstance();
2669:
2670:                    Iterator cidslIter = catIdSetList.iterator();
2671:                    while (cidslIter.hasNext()) {
2672:                        // make a Set of productIds including all ids from all categories
2673:                        Set catIdSet = (Set) cidslIter.next();
2674:                        Set groupProductIdSet = new HashSet();
2675:                        getAllProductIds(catIdSet, groupProductIdSet,
2676:                                delegator, nowTimestamp, true);
2677:                        productIdSetList.add(groupProductIdSet);
2678:                    }
2679:
2680:                    // now go through all productId sets and only include IDs that are in all sets
2681:                    // by definition if each id must be in all categories, then it must be in the first, so go through the first and drop each one that is not in all others
2682:                    Set firstProductIdSet = (Set) productIdSetList.remove(0);
2683:                    Iterator productIdSetIter = productIdSetList.iterator();
2684:                    while (productIdSetIter.hasNext()) {
2685:                        Set productIdSet = (Set) productIdSetIter.next();
2686:                        firstProductIdSet.retainAll(productIdSet);
2687:                    }
2688:
2689:                    /* the old way of doing it, not as efficient, recoded above using the retainAll operation, pretty handy
2690:                    Iterator firstProductIdIter = firstProductIdSet.iterator();
2691:                    while (firstProductIdIter.hasNext()) {
2692:                        String curProductId = (String) firstProductIdIter.next();
2693:                        
2694:                        boolean allContainProductId = true;
2695:                        Iterator productIdSetIter = productIdSetList.iterator();
2696:                        while (productIdSetIter.hasNext()) {
2697:                            Set productIdSet = (Set) productIdSetIter.next();
2698:                            if (!productIdSet.contains(curProductId)) {
2699:                                allContainProductId = false;
2700:                                break;
2701:                            }
2702:                        }
2703:                        
2704:                        if (!allContainProductId) {
2705:                            firstProductIdIter.remove();
2706:                        }
2707:                    }
2708:                     */
2709:
2710:                    if (firstProductIdSet.size() >= 0) {
2711:                        if (include) {
2712:                            productIds.addAll(firstProductIdSet);
2713:                        } else {
2714:                            productIds.removeAll(firstProductIdSet);
2715:                        }
2716:                    }
2717:                }
2718:            }
2719:
2720:            protected static void getAllProductIds(Set productCategoryIdSet,
2721:                    Set productIdSet, GenericDelegator delegator,
2722:                    Timestamp nowTimestamp, boolean include)
2723:                    throws GenericEntityException {
2724:                Iterator productCategoryIdIter = productCategoryIdSet
2725:                        .iterator();
2726:                while (productCategoryIdIter.hasNext()) {
2727:                    String productCategoryId = (String) productCategoryIdIter
2728:                            .next();
2729:                    // get all product category memebers, filter by date
2730:                    List productCategoryMembers = delegator.findByAndCache(
2731:                            "ProductCategoryMember", UtilMisc.toMap(
2732:                                    "productCategoryId", productCategoryId));
2733:                    productCategoryMembers = EntityUtil.filterByDate(
2734:                            productCategoryMembers, nowTimestamp);
2735:                    Iterator productCategoryMemberIter = productCategoryMembers
2736:                            .iterator();
2737:                    while (productCategoryMemberIter.hasNext()) {
2738:                        GenericValue productCategoryMember = (GenericValue) productCategoryMemberIter
2739:                                .next();
2740:                        String productId = productCategoryMember
2741:                                .getString("productId");
2742:                        if (include) {
2743:                            productIdSet.add(productId);
2744:                        } else {
2745:                            productIdSet.remove(productId);
2746:                        }
2747:                    }
2748:                }
2749:            }
2750:
2751:            protected static void handleProductPromoProducts(Set productIds,
2752:                    List productPromoProducts, String productPromoApplEnumId)
2753:                    throws GenericEntityException {
2754:                boolean include = !"PPPA_EXCLUDE"
2755:                        .equals(productPromoApplEnumId);
2756:                Iterator productPromoProductIter = productPromoProducts
2757:                        .iterator();
2758:                while (productPromoProductIter.hasNext()) {
2759:                    GenericValue productPromoProduct = (GenericValue) productPromoProductIter
2760:                            .next();
2761:                    if (productPromoApplEnumId.equals(productPromoProduct
2762:                            .getString("productPromoApplEnumId"))) {
2763:                        String productId = productPromoProduct
2764:                                .getString("productId");
2765:                        if (include) {
2766:                            productIds.add(productId);
2767:                        } else {
2768:                            productIds.remove(productId);
2769:                        }
2770:                    }
2771:                }
2772:            }
2773:
2774:            protected static class UseLimitException extends Exception {
2775:                public UseLimitException(String str) {
2776:                    super(str);
2777:                }
2778:            }
2779:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.