001: /*
002: * $Id: ShoppingCartHelper.java,v 1.11 2004/03/02 20:02:17 ajzeneski Exp $
003: *
004: * Copyright (c) 2001, 2002 The Open For Business Project - www.ofbiz.org
005: *
006: * Permission is hereby granted, free of charge, to any person obtaining a
007: * copy of this software and associated documentation files (the "Software"),
008: * to deal in the Software without restriction, including without limitation
009: * the rights to use, copy, modify, merge, publish, distribute, sublicense,
010: * and/or sell copies of the Software, and to permit persons to whom the
011: * Software is furnished to do so, subject to the following conditions:
012: *
013: * The above copyright notice and this permission notice shall be included
014: * in all copies or substantial portions of the Software.
015: *
016: * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
017: * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
018: * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
019: * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
020: * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
021: * OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
022: * THE USE OR OTHER DEALINGS IN THE SOFTWARE.
023: */
024: package org.ofbiz.order.shoppingcart;
025:
026: import java.text.NumberFormat;
027: import java.text.ParseException;
028: import java.util.*;
029:
030: import org.ofbiz.base.util.Debug;
031: import org.ofbiz.base.util.UtilMisc;
032: import org.ofbiz.base.util.UtilProperties;
033: import org.ofbiz.base.util.UtilFormatOut;
034: import org.ofbiz.entity.GenericDelegator;
035: import org.ofbiz.entity.GenericEntityException;
036: import org.ofbiz.entity.GenericValue;
037: import org.ofbiz.entity.util.EntityUtil;
038: import org.ofbiz.security.Security;
039: import org.ofbiz.service.LocalDispatcher;
040: import org.ofbiz.service.ModelService;
041: import org.ofbiz.service.ServiceUtil;
042:
043: /**
044: * A facade over the
045: * {@link org.ofbiz.order.shoppingcart.ShoppingCart ShoppingCart}
046: * providing catalog and product services to simplify the interaction
047: * with the cart directly.
048: *
049: * @author <a href="mailto:tristana@twibble.org">Tristan Austin</a>
050: * @author <a href="mailto:jaz@ofbiz.org">Andy Zeneski</a>
051: * @version $Revision: 1.11 $
052: * @since 2.0
053: */
054: public class ShoppingCartHelper {
055:
056: public static final String resource = "OrderUiLabels";
057: public static String module = ShoppingCartHelper.class.getName();
058:
059: // The shopping cart to manipulate
060: private ShoppingCart cart = null;
061:
062: // The entity engine delegator
063: private GenericDelegator delegator = null;
064:
065: // The service invoker
066: private LocalDispatcher dispatcher = null;
067:
068: /**
069: * Changes will be made to the cart directly, as opposed
070: * to a copy of the cart provided.
071: *
072: * @param cart The cart to manipulate
073: */
074: public ShoppingCartHelper(GenericDelegator delegator,
075: LocalDispatcher dispatcher, ShoppingCart cart) {
076: this .dispatcher = dispatcher;
077: this .delegator = delegator;
078: this .cart = cart;
079:
080: if (delegator == null) {
081: this .delegator = dispatcher.getDelegator();
082: }
083: if (dispatcher == null) {
084: throw new IllegalArgumentException(
085: "Dispatcher argument is null");
086: }
087: if (cart == null) {
088: throw new IllegalArgumentException(
089: "ShoppingCart argument is null");
090: }
091: }
092:
093: /** Event to add an item to the shopping cart. */
094: public Map addToCart(String catalogId, String shoppingListId,
095: String shoppingListItemSeqId, String productId,
096: String productCategoryId, String itemType,
097: String itemDescription, double price, double amount,
098: double quantity, Map context) {
099: Map result;
100: Map attributes = null;
101: String errMsg = null;
102:
103: // price sanity check
104: if (productId == null && price < 0) {
105: errMsg = UtilProperties.getMessage(resource,
106: "cart.price_not_positive_number", this .cart
107: .getLocale());
108: result = ServiceUtil.returnError(errMsg);
109: return result;
110: }
111:
112: // quantity sanity check
113: if (quantity < 1) {
114: errMsg = UtilProperties.getMessage(resource,
115: "cart.quantity_not_positive_number", this .cart
116: .getLocale());
117: result = ServiceUtil.returnError(errMsg);
118: return result;
119: }
120:
121: // amount sanity check
122: if (amount < 0) {
123: amount = 0;
124: }
125:
126: // Create a HashMap of product attributes - From ShoppingCartItem.attributeNames[]
127: for (int namesIdx = 0; namesIdx < ShoppingCartItem.attributeNames.length; namesIdx++) {
128: if (attributes == null)
129: attributes = new HashMap();
130: if (context
131: .containsKey(ShoppingCartItem.attributeNames[namesIdx])) {
132: attributes
133: .put(
134: ShoppingCartItem.attributeNames[namesIdx],
135: context
136: .get(ShoppingCartItem.attributeNames[namesIdx]));
137: }
138: }
139:
140: // check for required amount flag; if amount and no flag set to 0
141: GenericValue product = null;
142: if (productId != null) {
143: try {
144: product = delegator.findByPrimaryKeyCache("Product",
145: UtilMisc.toMap("productId", productId));
146: } catch (GenericEntityException e) {
147: Debug.logError(e, "Unable to lookup product : "
148: + productId, module);
149: }
150: if (product == null || product.get("requireAmount") == null
151: || "N".equals(product.getString("requireAmount"))) {
152: amount = 0;
153: }
154: }
155:
156: // Retrieve the catalog ID
157: try {
158: int itemId = -1;
159: if (productId != null) {
160: itemId = cart.addOrIncreaseItem(productId, amount,
161: quantity, null, attributes, catalogId,
162: dispatcher);
163: } else {
164: itemId = cart.addNonProductItem(itemType,
165: itemDescription, productCategoryId, price,
166: quantity, attributes, catalogId, dispatcher);
167: }
168:
169: // set the shopping list info
170: if (itemId > -1 && shoppingListId != null
171: && shoppingListItemSeqId != null) {
172: ShoppingCartItem item = cart.findCartItem(itemId);
173: item.setShoppingList(shoppingListId,
174: shoppingListItemSeqId);
175: }
176: } catch (CartItemModifyException cartException) {
177: result = ServiceUtil
178: .returnError(cartException.getMessage());
179: return result;
180: }
181:
182: //Indicate there were no critical errors
183: result = ServiceUtil.returnSuccess();
184: return result;
185: }
186:
187: public Map addToCartFromOrder(String catalogId, String orderId,
188: String[] itemIds, boolean addAll) {
189: ArrayList errorMsgs = new ArrayList();
190: Map result;
191: String errMsg = null;
192:
193: if (orderId == null || orderId.length() <= 0) {
194: errMsg = UtilProperties.getMessage(resource,
195: "cart.order_not_specified_to_add_from", this .cart
196: .getLocale());
197: result = ServiceUtil.returnError(errMsg);
198: return result;
199: }
200:
201: boolean noItems = true;
202:
203: if (addAll) {
204: Iterator itemIter = null;
205:
206: try {
207: itemIter = UtilMisc.toIterator(delegator.findByAnd(
208: "OrderItem",
209: UtilMisc.toMap("orderId", orderId), null));
210: } catch (GenericEntityException e) {
211: Debug.logWarning(e.getMessage(), module);
212: itemIter = null;
213: }
214:
215: if (itemIter != null && itemIter.hasNext()) {
216: while (itemIter.hasNext()) {
217: GenericValue orderItem = (GenericValue) itemIter
218: .next();
219: // never read: int itemId = -1;
220: if (orderItem.get("productId") != null
221: && orderItem.get("quantity") != null) {
222: double amount = 0.00;
223: if (orderItem.get("selectedAmount") != null) {
224: amount = orderItem.getDouble(
225: "selectedAmount").doubleValue();
226: }
227: try {
228: this .cart.addOrIncreaseItem(orderItem
229: .getString("productId"), amount,
230: orderItem.getDouble("quantity")
231: .doubleValue(), null, null,
232: catalogId, dispatcher);
233: noItems = false;
234: } catch (CartItemModifyException e) {
235: errorMsgs.add(e.getMessage());
236: }
237: }
238: }
239: if (errorMsgs.size() > 0) {
240: result = ServiceUtil.returnError(errorMsgs);
241: result.put(ModelService.RESPONSE_MESSAGE,
242: ModelService.RESPOND_SUCCESS);
243: return result; // don't return error because this is a non-critical error and should go back to the same page
244: }
245: } else {
246: noItems = true;
247: }
248: } else {
249: noItems = true;
250: if (itemIds != null) {
251:
252: for (int i = 0; i < itemIds.length; i++) {
253: String orderItemSeqId = itemIds[i];
254: GenericValue orderItem = null;
255:
256: try {
257: orderItem = delegator.findByPrimaryKey(
258: "OrderItem", UtilMisc.toMap("orderId",
259: orderId, "orderItemSeqId",
260: orderItemSeqId));
261: } catch (GenericEntityException e) {
262: Debug.logWarning(e.getMessage(), module);
263: errorMsgs.add("Order line \"" + orderItemSeqId
264: + "\" not found, so not added.");
265: continue;
266: }
267: if (orderItem != null) {
268: if (orderItem.get("productId") != null
269: && orderItem.get("quantity") != null) {
270: double amount = 0.00;
271: if (orderItem.get("selectedAmount") != null) {
272: amount = orderItem.getDouble(
273: "selectedAmount").doubleValue();
274: }
275: try {
276: this .cart.addOrIncreaseItem(orderItem
277: .getString("productId"),
278: amount, orderItem.getDouble(
279: "quantity")
280: .doubleValue(), null,
281: null, catalogId, dispatcher);
282: noItems = false;
283: } catch (CartItemModifyException e) {
284: errorMsgs.add(e.getMessage());
285: }
286: }
287: }
288: }
289: if (errorMsgs.size() > 0) {
290: result = ServiceUtil.returnError(errorMsgs);
291: result.put(ModelService.RESPONSE_MESSAGE,
292: ModelService.RESPOND_SUCCESS);
293: return result; // don't return error because this is a non-critical error and should go back to the same page
294: }
295: } // else no items
296: }
297:
298: if (noItems) {
299: result = ServiceUtil.returnSuccess();
300: result.put("_ERROR_MESSAGE_", "No items found to add.");
301: return result; // don't return error because this is a non-critical error and should go back to the same page
302: }
303:
304: result = ServiceUtil.returnSuccess();
305: return result;
306: }
307:
308: /**
309: * Adds all products in a category according to quantity request parameter
310: * for each; if no parameter for a certain product in the category, or if
311: * quantity is 0, do not add
312: */
313: public Map addToCartBulk(String catalogId, String categoryId,
314: Map context) {
315: Map result = null;
316: String errMsg = null;
317:
318: if (categoryId == null || categoryId.length() <= 0) {
319: errMsg = UtilProperties.getMessage(resource,
320: "cart.category_not_specified_to_add_from",
321: this .cart.getLocale());
322: result = ServiceUtil.returnError(errMsg);
323: return result;
324: }
325:
326: Collection prodCatMemberCol = null;
327:
328: try {
329: prodCatMemberCol = delegator.findByAndCache(
330: "ProductCategoryMember", UtilMisc.toMap(
331: "productCategoryId", categoryId));
332: } catch (GenericEntityException e) {
333: Map messageMap = UtilMisc.toMap("categoryId", categoryId);
334: messageMap.put("message", e.getMessage());
335: Debug.logWarning(e.getMessage(), module);
336: errMsg = UtilProperties.getMessage(resource,
337: "cart.could_not_get_products_in_category_cart",
338: messageMap, this .cart.getLocale());
339: result = ServiceUtil.returnError(errMsg);
340: return result;
341: }
342:
343: if (prodCatMemberCol == null) {
344: Map messageMap = UtilMisc.toMap("categoryId", categoryId);
345: errMsg = UtilProperties.getMessage(resource,
346: "cart.could_not_get_products_in_category",
347: messageMap, this .cart.getLocale());
348: result = ServiceUtil.returnError(errMsg);
349: return result;
350: }
351:
352: // never read: String errMsg = "";
353: Iterator pcmIter = prodCatMemberCol.iterator();
354:
355: while (pcmIter.hasNext()) {
356: GenericValue productCategoryMember = (GenericValue) pcmIter
357: .next();
358: String quantStr = (String) context.get("quantity_"
359: + productCategoryMember.getString("productId"));
360:
361: if (quantStr != null && quantStr.length() > 0) {
362: double quantity = 0;
363:
364: try {
365: quantity = Double.parseDouble(quantStr);
366: } catch (NumberFormatException nfe) {
367: quantity = 0;
368: }
369: if (quantity > 0.0) {
370: try {
371: this .cart.addOrIncreaseItem(
372: productCategoryMember
373: .getString("productId"), 0.00,
374: quantity, null, null, catalogId,
375: dispatcher);
376: } catch (CartItemModifyException cartException) {
377: result = ServiceUtil.returnError(cartException
378: .getMessage());
379: return result;
380: }
381: }
382: }
383: }
384:
385: //Indicate there were no non critical errors
386: result = ServiceUtil.returnSuccess();
387: return result;
388: }
389:
390: /**
391: * Adds all products in a category according to default quantity on ProductCategoryMember
392: * for each; if no default for a certain product in the category, or if
393: * quantity is 0, do not add
394: */
395: public Map addCategoryDefaults(String catalogId, String categoryId) {
396: ArrayList errorMsgs = new ArrayList();
397: Map result = null;
398: String errMsg = null;
399:
400: if (categoryId == null || categoryId.length() <= 0) {
401: errMsg = UtilProperties.getMessage(resource,
402: "cart.category_not_specified_to_add_from",
403: this .cart.getLocale());
404: result = ServiceUtil.returnError(errMsg);
405: // result = ServiceUtil.returnError("No category specified to add from.");
406: return result;
407: }
408:
409: Collection prodCatMemberCol = null;
410:
411: try {
412: prodCatMemberCol = delegator.findByAndCache(
413: "ProductCategoryMember", UtilMisc.toMap(
414: "productCategoryId", categoryId));
415: } catch (GenericEntityException e) {
416: Debug.logWarning(e.toString(), module);
417: Map messageMap = UtilMisc.toMap("categoryId", categoryId);
418: messageMap.put("message", e.getMessage());
419: errMsg = UtilProperties.getMessage(resource,
420: "cart.could_not_get_products_in_category_cart",
421: messageMap, this .cart.getLocale());
422: result = ServiceUtil.returnError(errMsg);
423: return result;
424: }
425:
426: if (prodCatMemberCol == null) {
427: Map messageMap = UtilMisc.toMap("categoryId", categoryId);
428: errMsg = UtilProperties.getMessage(resource,
429: "cart.could_not_get_products_in_category",
430: messageMap, this .cart.getLocale());
431: result = ServiceUtil.returnError(errMsg);
432: return result;
433: }
434:
435: double totalQuantity = 0;
436: Iterator pcmIter = prodCatMemberCol.iterator();
437:
438: while (pcmIter.hasNext()) {
439: GenericValue productCategoryMember = (GenericValue) pcmIter
440: .next();
441: Double quantity = productCategoryMember
442: .getDouble("quantity");
443:
444: if (quantity != null && quantity.doubleValue() > 0.0) {
445: try {
446: this .cart.addOrIncreaseItem(productCategoryMember
447: .getString("productId"), 0.00, quantity
448: .doubleValue(), null, null, catalogId,
449: dispatcher);
450: totalQuantity += quantity.doubleValue();
451: } catch (CartItemModifyException e) {
452: errorMsgs.add(e.getMessage());
453: }
454: }
455: }
456: if (errorMsgs.size() > 0) {
457: result = ServiceUtil.returnError(errorMsgs);
458: result.put(ModelService.RESPONSE_MESSAGE,
459: ModelService.RESPOND_SUCCESS);
460: return result; // don't return error because this is a non-critical error and should go back to the same page
461: }
462:
463: result = ServiceUtil.returnSuccess();
464: result.put("totalQuantity", new Double(totalQuantity));
465: return result;
466: }
467:
468: /** Delete an item from the shopping cart. */
469: public Map deleteFromCart(Map context) {
470: Map result = null;
471: Set names = context.keySet();
472: Iterator i = names.iterator();
473: ArrayList errorMsgs = new ArrayList();
474:
475: while (i.hasNext()) {
476: String o = (String) i.next();
477:
478: if (o.toUpperCase().startsWith("DELETE")) {
479: try {
480: String indexStr = o
481: .substring(o.lastIndexOf('_') + 1);
482: int index = Integer.parseInt(indexStr);
483:
484: try {
485: this .cart.removeCartItem(index, dispatcher);
486: } catch (CartItemModifyException e) {
487: errorMsgs.add(e.getMessage());
488: }
489: } catch (NumberFormatException nfe) {
490: }
491: }
492: }
493:
494: if (errorMsgs.size() > 0) {
495: result = ServiceUtil.returnError(errorMsgs);
496: result.put(ModelService.RESPONSE_MESSAGE,
497: ModelService.RESPOND_SUCCESS);
498: return result; // don't return error because this is a non-critical error and should go back to the same page
499: }
500:
501: result = ServiceUtil.returnSuccess();
502: return result;
503: }
504:
505: /** Update the items in the shopping cart. */
506: public Map modifyCart(Security security, GenericValue userLogin,
507: Map context, boolean removeSelected, String[] selectedItems) {
508: Map result = null;
509:
510: ArrayList deleteList = new ArrayList();
511: ArrayList errorMsgs = new ArrayList();
512:
513: Set names = context.keySet();
514: Iterator i = names.iterator();
515:
516: while (i.hasNext()) {
517: String o = (String) i.next();
518: int underscorePos = o.lastIndexOf('_');
519:
520: if (underscorePos >= 0) {
521: try {
522: String indexStr = o.substring(underscorePos + 1);
523: int index = Integer.parseInt(indexStr);
524: String quantString = (String) context.get(o);
525: double quantity = -1;
526:
527: // get the cart item
528: ShoppingCartItem item = this .cart
529: .findCartItem(index);
530:
531: if (o.toUpperCase().startsWith("OPTION")) {
532: if (quantString.toUpperCase().startsWith("NO^")) {
533: if (quantString.length() > 2) { // the length of the prefix
534: String featureTypeId = this
535: .getRemoveFeatureTypeId(o);
536: if (featureTypeId != null) {
537: item
538: .removeAdditionalProductFeatureAndAppl(featureTypeId);
539: }
540: }
541: } else {
542: GenericValue featureAppl = this
543: .getFeatureAppl(
544: item.getProductId(), o,
545: quantString);
546: if (featureAppl != null) {
547: item
548: .putAdditionalProductFeatureAndAppl(featureAppl);
549: }
550: }
551: } else {
552: quantity = NumberFormat.getNumberInstance()
553: .parse(quantString).doubleValue();
554: if (quantity < 0) {
555: throw new CartItemModifyException(
556: "Quantity must be a positive number.");
557: }
558: }
559:
560: if (o.toUpperCase().startsWith("UPDATE")) {
561: if (quantity == 0.0) {
562: deleteList.add(item);
563: } else {
564: if (item != null) {
565: try {
566: item.setQuantity(quantity,
567: dispatcher, this .cart);
568: } catch (CartItemModifyException e) {
569: errorMsgs.add(e.getMessage());
570: }
571: }
572: }
573: }
574:
575: if (o.toUpperCase().startsWith("PRICE")) {
576: if (security.hasEntityPermission("ORDERMGR",
577: "_CREATE", userLogin)) {
578: if (item != null) {
579: item.setBasePrice(quantity); // this is quanity because the parsed number variable is the same as quantity
580: }
581: }
582: }
583:
584: if (o.toUpperCase().startsWith("DELETE")) {
585: deleteList.add(this .cart.findCartItem(index));
586: }
587: } catch (NumberFormatException nfe) {
588: Debug
589: .logWarning(
590: nfe,
591: "Caught number format exception on cart update.",
592: module);
593: } catch (ParseException pe) {
594: Debug.logWarning(pe,
595: "Caught parse exception on cart update.",
596: module);
597: } catch (Exception e) {
598: Debug.logWarning(e,
599: "Caught exception on cart update.", module);
600: }
601: } // else not a parameter we need
602: }
603:
604: // get a list of the items to delete
605: if (removeSelected) {
606: for (int si = 0; si < selectedItems.length; si++) {
607: String indexStr = selectedItems[si];
608: ShoppingCartItem item = null;
609: try {
610: int index = Integer.parseInt(indexStr);
611: item = this .cart.findCartItem(index);
612: } catch (Exception e) {
613: Debug.logWarning(e,
614: "Problems getting the cart item by index",
615: module);
616: }
617: if (item != null) {
618: deleteList.add(item);
619: }
620: }
621: }
622:
623: Iterator di = deleteList.iterator();
624:
625: while (di.hasNext()) {
626: ShoppingCartItem item = (ShoppingCartItem) di.next();
627: int itemIndex = this .cart.getItemIndex(item);
628:
629: if (Debug.infoOn())
630: Debug.logInfo("Removing item index: " + itemIndex,
631: module);
632: try {
633: this .cart.removeCartItem(itemIndex, dispatcher);
634: } catch (CartItemModifyException e) {
635: ServiceUtil.returnError(new Vector());
636: errorMsgs.add(e.getMessage());
637: }
638: }
639:
640: if (context.containsKey("alwaysShowcart")) {
641: this .cart.setViewCartOnAdd(true);
642: } else {
643: this .cart.setViewCartOnAdd(false);
644: }
645:
646: if (errorMsgs.size() > 0) {
647: result = ServiceUtil.returnError(errorMsgs);
648: return result;
649: }
650:
651: result = ServiceUtil.returnSuccess();
652: return result;
653: }
654:
655: /** Empty the shopping cart. */
656: public boolean clearCart() {
657: this .cart.clear();
658: return true;
659: }
660:
661: /** Returns the shopping cart this helper is wrapping. */
662: public ShoppingCart getCartObject() {
663: return this .cart;
664: }
665:
666: public GenericValue getFeatureAppl(String productId,
667: String optionField, String featureId) {
668: if (delegator == null) {
669: throw new IllegalArgumentException(
670: "No delegator available to lookup ProductFeature");
671: }
672:
673: Map fields = UtilMisc.toMap("productId", productId,
674: "productFeatureId", featureId);
675: if (optionField != null) {
676: int featureTypeStartIndex = optionField.indexOf('^') + 1;
677: int featureTypeEndIndex = optionField.lastIndexOf('_');
678: if (featureTypeStartIndex > 0 && featureTypeEndIndex > 0) {
679: fields.put("productFeatureTypeId", optionField
680: .substring(featureTypeStartIndex,
681: featureTypeEndIndex));
682: }
683: }
684:
685: GenericValue productFeatureAppl = null;
686: List features = null;
687: try {
688: features = delegator.findByAnd("ProductFeatureAndAppl",
689: fields, UtilMisc.toList("-fromDate"));
690: } catch (GenericEntityException e) {
691: Debug.logError(e, module);
692: return null;
693: }
694:
695: if (features != null) {
696: if (features.size() > 1) {
697: features = EntityUtil.filterByDate(features);
698: }
699: productFeatureAppl = EntityUtil.getFirst(features);
700: }
701:
702: return productFeatureAppl;
703: }
704:
705: public String getRemoveFeatureTypeId(String optionField) {
706: if (optionField != null) {
707: int featureTypeStartIndex = optionField.indexOf('^') + 1;
708: int featureTypeEndIndex = optionField.lastIndexOf('_');
709: if (featureTypeStartIndex > 0 && featureTypeEndIndex > 0) {
710: return optionField.substring(featureTypeStartIndex,
711: featureTypeEndIndex);
712: }
713: }
714: return null;
715: }
716: }
|