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;
0019:
0020: import java.sql.Timestamp;
0021: import java.text.NumberFormat;
0022: import java.text.ParseException;
0023: import java.util.*;
0024:
0025: import org.ofbiz.base.util.Debug;
0026: import org.ofbiz.base.util.UtilHttp;
0027: import org.ofbiz.base.util.UtilMisc;
0028: import org.ofbiz.base.util.UtilProperties;
0029: import org.ofbiz.base.util.UtilValidate;
0030: import org.ofbiz.entity.GenericDelegator;
0031: import org.ofbiz.entity.GenericEntityException;
0032: import org.ofbiz.entity.GenericValue;
0033: import org.ofbiz.entity.util.EntityUtil;
0034: import org.ofbiz.order.shoppingcart.product.ProductPromoWorker;
0035: import org.ofbiz.product.config.ProductConfigWrapper;
0036: import org.ofbiz.security.Security;
0037: import org.ofbiz.service.GenericServiceException;
0038: import org.ofbiz.service.LocalDispatcher;
0039: import org.ofbiz.service.ModelService;
0040: import org.ofbiz.service.ServiceUtil;
0041:
0042: /**
0043: * A facade over the
0044: * {@link org.ofbiz.order.shoppingcart.ShoppingCart ShoppingCart}
0045: * providing catalog and product services to simplify the interaction
0046: * with the cart directly.
0047: */
0048: public class ShoppingCartHelper {
0049:
0050: public static final String resource = "OrderUiLabels";
0051: public static String module = ShoppingCartHelper.class.getName();
0052: public static final String resource_error = "OrderErrorUiLabels";
0053:
0054: // The shopping cart to manipulate
0055: private ShoppingCart cart = null;
0056:
0057: // The entity engine delegator
0058: private GenericDelegator delegator = null;
0059:
0060: // The service invoker
0061: private LocalDispatcher dispatcher = null;
0062:
0063: /**
0064: * Changes will be made to the cart directly, as opposed
0065: * to a copy of the cart provided.
0066: *
0067: * @param cart The cart to manipulate
0068: */
0069: public ShoppingCartHelper(GenericDelegator delegator,
0070: LocalDispatcher dispatcher, ShoppingCart cart) {
0071: this .dispatcher = dispatcher;
0072: this .delegator = delegator;
0073: this .cart = cart;
0074:
0075: if (delegator == null) {
0076: this .delegator = dispatcher.getDelegator();
0077: }
0078: if (dispatcher == null) {
0079: throw new IllegalArgumentException(
0080: "Dispatcher argument is null");
0081: }
0082: if (cart == null) {
0083: throw new IllegalArgumentException(
0084: "ShoppingCart argument is null");
0085: }
0086: }
0087:
0088: /** Event to add an item to the shopping cart. */
0089: public Map addToCart(String catalogId, String shoppingListId,
0090: String shoppingListItemSeqId, String productId,
0091: String productCategoryId, String itemType,
0092: String itemDescription, Double price, Double amount,
0093: double quantity, java.sql.Timestamp reservStart,
0094: Double reservLength, Double reservPersons,
0095: java.sql.Timestamp shipBeforeDate,
0096: java.sql.Timestamp shipAfterDate,
0097: ProductConfigWrapper configWrapper, String itemGroupNumber,
0098: Map context, String parentProductId) {
0099: Map result = null;
0100: Map attributes = null;
0101: String pProductId = null;
0102: pProductId = parentProductId;
0103: // price sanity check
0104: if (productId == null && price != null
0105: && price.doubleValue() < 0) {
0106: String errMsg = UtilProperties.getMessage(resource,
0107: "cart.price_not_positive_number", this .cart
0108: .getLocale());
0109: result = ServiceUtil.returnError(errMsg);
0110: return result;
0111: }
0112:
0113: // quantity sanity check
0114: if (quantity < 1) {
0115: String errMsg = UtilProperties.getMessage(resource,
0116: "cart.quantity_not_positive_number", this .cart
0117: .getLocale());
0118: result = ServiceUtil.returnError(errMsg);
0119: return result;
0120: }
0121:
0122: // amount sanity check
0123: if (amount != null && amount.doubleValue() < 0) {
0124: amount = null;
0125: }
0126:
0127: // check desiredDeliveryDate syntax and remove if empty
0128: String ddDate = (String) context.get("itemDesiredDeliveryDate");
0129: if (!UtilValidate.isEmpty(ddDate)) {
0130: try {
0131: java.sql.Timestamp.valueOf((String) context
0132: .get("itemDesiredDeliveryDate"));
0133: } catch (IllegalArgumentException e) {
0134: return ServiceUtil
0135: .returnError(UtilProperties
0136: .getMessage(
0137: resource_error,
0138: "OrderInvalidDesiredDeliveryDateSyntaxError",
0139: this .cart.getLocale()));
0140: }
0141: } else {
0142: context.remove("itemDesiredDeliveryDate");
0143: }
0144:
0145: // remove an empty comment
0146: String comment = (String) context.get("itemComment");
0147: if (UtilValidate.isEmpty(comment)) {
0148: context.remove("itemComment");
0149: }
0150:
0151: // stores the default desired delivery date in the cart if need
0152: if (!UtilValidate.isEmpty((String) context
0153: .get("useAsDefaultDesiredDeliveryDate"))) {
0154: cart.setDefaultItemDeliveryDate((String) context
0155: .get("itemDesiredDeliveryDate"));
0156: } else {
0157: // do we really want to clear this if it isn't checked?
0158: cart.setDefaultItemDeliveryDate(null);
0159: }
0160:
0161: // stores the default comment in session if need
0162: if (!UtilValidate.isEmpty((String) context
0163: .get("useAsDefaultComment"))) {
0164: cart.setDefaultItemComment((String) context
0165: .get("itemComment"));
0166: } else {
0167: // do we really want to clear this if it isn't checked?
0168: cart.setDefaultItemComment(null);
0169: }
0170:
0171: // Create a HashMap of product attributes - From ShoppingCartItem.attributeNames[]
0172: for (int namesIdx = 0; namesIdx < ShoppingCartItem.attributeNames.length; namesIdx++) {
0173: if (attributes == null)
0174: attributes = new HashMap();
0175: if (context
0176: .containsKey(ShoppingCartItem.attributeNames[namesIdx])) {
0177: attributes
0178: .put(
0179: ShoppingCartItem.attributeNames[namesIdx],
0180: context
0181: .get(ShoppingCartItem.attributeNames[namesIdx]));
0182: }
0183: }
0184:
0185: // check for required amount flag; if amount and no flag set to 0
0186: GenericValue product = null;
0187: if (productId != null) {
0188: try {
0189: product = delegator.findByPrimaryKeyCache("Product",
0190: UtilMisc.toMap("productId", productId));
0191: } catch (GenericEntityException e) {
0192: Debug.logError(e, "Unable to lookup product : "
0193: + productId, module);
0194: }
0195: if (product == null || product.get("requireAmount") == null
0196: || "N".equals(product.getString("requireAmount"))) {
0197: amount = null;
0198: }
0199: Debug.logInfo("carthelper productid " + productId, module);
0200: Debug.logInfo("parent productid " + pProductId, module);
0201: //if (product != null && !"Y".equals(product.getString("isVariant")))
0202: // pProductId = null;
0203:
0204: }
0205:
0206: // add or increase the item to the cart
0207: try {
0208: int itemId = -1;
0209: if (productId != null) {
0210: itemId = cart.addOrIncreaseItem(productId, amount,
0211: quantity, reservStart, reservLength,
0212: reservPersons, shipBeforeDate, shipAfterDate,
0213: null, attributes, catalogId, configWrapper,
0214: itemType, itemGroupNumber, pProductId,
0215: dispatcher);
0216: } else {
0217: itemId = cart.addNonProductItem(itemType,
0218: itemDescription, productCategoryId, price,
0219: quantity, attributes, catalogId,
0220: itemGroupNumber, dispatcher);
0221: }
0222:
0223: // set the shopping list info
0224: if (itemId > -1 && shoppingListId != null
0225: && shoppingListItemSeqId != null) {
0226: ShoppingCartItem item = cart.findCartItem(itemId);
0227: item.setShoppingList(shoppingListId,
0228: shoppingListItemSeqId);
0229: }
0230: } catch (CartItemModifyException e) {
0231: if (cart.getOrderType().equals("PURCHASE_ORDER")) {
0232: String errMsg = UtilProperties.getMessage(resource,
0233: "cart.product_not_valid_for_supplier",
0234: this .cart.getLocale());
0235: errMsg = errMsg + " (" + e.getMessage() + ")";
0236: result = ServiceUtil.returnError(errMsg);
0237: } else {
0238: result = ServiceUtil.returnError(e.getMessage());
0239: }
0240: return result;
0241: } catch (ItemNotFoundException e) {
0242: result = ServiceUtil.returnError(e.getMessage());
0243: return result;
0244: }
0245:
0246: // Indicate there were no critical errors
0247: result = ServiceUtil.returnSuccess();
0248: return result;
0249: }
0250:
0251: public Map addToCartFromOrder(String catalogId, String orderId,
0252: String[] itemIds, boolean addAll, String itemGroupNumber) {
0253: ArrayList errorMsgs = new ArrayList();
0254: Map result;
0255: String errMsg = null;
0256:
0257: if (orderId == null || orderId.length() <= 0) {
0258: errMsg = UtilProperties.getMessage(resource,
0259: "cart.order_not_specified_to_add_from", this .cart
0260: .getLocale());
0261: result = ServiceUtil.returnError(errMsg);
0262: return result;
0263: }
0264:
0265: boolean noItems = true;
0266:
0267: if (addAll) {
0268: Iterator itemIter = null;
0269:
0270: try {
0271: itemIter = UtilMisc.toIterator(delegator.findByAnd(
0272: "OrderItem",
0273: UtilMisc.toMap("orderId", orderId), null));
0274: } catch (GenericEntityException e) {
0275: Debug.logWarning(e.getMessage(), module);
0276: itemIter = null;
0277: }
0278:
0279: String orderItemTypeId = null;
0280: if (itemIter != null && itemIter.hasNext()) {
0281: while (itemIter.hasNext()) {
0282: GenericValue orderItem = (GenericValue) itemIter
0283: .next();
0284: orderItemTypeId = orderItem
0285: .getString("orderItemTypeId");
0286: // do not store rental items
0287: if (orderItemTypeId.equals("RENTAL_ORDER_ITEM"))
0288: continue;
0289: // never read: int itemId = -1;
0290: if (orderItem.get("productId") != null
0291: && orderItem.get("quantity") != null) {
0292: Double amount = orderItem
0293: .getDouble("selectedAmount");
0294: try {
0295: this .cart.addOrIncreaseItem(orderItem
0296: .getString("productId"), amount,
0297: orderItem.getDouble("quantity")
0298: .doubleValue(), null, null,
0299: null, null, null, null, null,
0300: catalogId, null, orderItemTypeId,
0301: itemGroupNumber, null, dispatcher);
0302: noItems = false;
0303: } catch (CartItemModifyException e) {
0304: errorMsgs.add(e.getMessage());
0305: } catch (ItemNotFoundException e) {
0306: errorMsgs.add(e.getMessage());
0307: }
0308: }
0309: }
0310: if (errorMsgs.size() > 0) {
0311: result = ServiceUtil.returnError(errorMsgs);
0312: result.put(ModelService.RESPONSE_MESSAGE,
0313: ModelService.RESPOND_SUCCESS);
0314: return result; // don't return error because this is a non-critical error and should go back to the same page
0315: }
0316: } else {
0317: noItems = true;
0318: }
0319: } else {
0320: noItems = true;
0321: if (itemIds != null) {
0322:
0323: for (int i = 0; i < itemIds.length; i++) {
0324: String orderItemSeqId = itemIds[i];
0325: GenericValue orderItem = null;
0326:
0327: try {
0328: orderItem = delegator.findByPrimaryKey(
0329: "OrderItem", UtilMisc.toMap("orderId",
0330: orderId, "orderItemSeqId",
0331: orderItemSeqId));
0332: } catch (GenericEntityException e) {
0333: Debug.logWarning(e.getMessage(), module);
0334: errorMsgs.add("Order line \"" + orderItemSeqId
0335: + "\" not found, so not added.");
0336: continue;
0337: }
0338: if (orderItem != null) {
0339: if (orderItem.get("productId") != null
0340: && orderItem.get("quantity") != null) {
0341: Double amount = orderItem
0342: .getDouble("selectedAmount");
0343: try {
0344: this .cart
0345: .addOrIncreaseItem(
0346: orderItem
0347: .getString("productId"),
0348: amount,
0349: orderItem.getDouble(
0350: "quantity")
0351: .doubleValue(),
0352: null,
0353: null,
0354: null,
0355: null,
0356: null,
0357: null,
0358: null,
0359: catalogId,
0360: null,
0361: orderItem
0362: .getString("orderItemTypeId"),
0363: itemGroupNumber, null,
0364: dispatcher);
0365: noItems = false;
0366: } catch (CartItemModifyException e) {
0367: errorMsgs.add(e.getMessage());
0368: } catch (ItemNotFoundException e) {
0369: errorMsgs.add(e.getMessage());
0370: }
0371: }
0372: }
0373: }
0374: if (errorMsgs.size() > 0) {
0375: result = ServiceUtil.returnError(errorMsgs);
0376: result.put(ModelService.RESPONSE_MESSAGE,
0377: ModelService.RESPOND_SUCCESS);
0378: return result; // don't return error because this is a non-critical error and should go back to the same page
0379: }
0380: } // else no items
0381: }
0382:
0383: if (noItems) {
0384: result = ServiceUtil.returnSuccess();
0385: result.put("_ERROR_MESSAGE_", UtilProperties.getMessage(
0386: resource_error, "OrderNoItemsFoundToAdd", this .cart
0387: .getLocale()));
0388: return result; // don't return error because this is a non-critical error and should go back to the same page
0389: }
0390:
0391: result = ServiceUtil.returnSuccess();
0392: return result;
0393: }
0394:
0395: /**
0396: * Adds all products in a category according to quantity request parameter
0397: * for each; if no parameter for a certain product in the category, or if
0398: * quantity is 0, do not add.
0399: * If a _ign_${itemGroupNumber} is appended to the name it will be put in that group instead of the default in the request parameter in itemGroupNumber
0400: *
0401: * There are 2 options for the syntax:
0402: * - name="quantity_${productId}" value="${quantity}
0403: * - name="product_${whatever}" value="${productId}" (note: quantity is always 1)
0404: */
0405: public Map addToCartBulk(String catalogId, String categoryId,
0406: Map context) {
0407: String itemGroupNumber = (String) context
0408: .get("itemGroupNumber");
0409: // use this prefix for the main structure such as a checkbox or a text input where name="quantity_${productId}" value="${quantity}"
0410: String keyPrefix = "quantity_";
0411: // use this prefix for a different structure, useful for radio buttons; can have any suffix, name="product_${whatever}" value="${productId}" and quantity is always 1
0412: String productQuantityKeyPrefix = "product_";
0413:
0414: // If a _ign_${itemGroupNumber} is appended to the name it will be put in that group instead of the default in the request parameter in itemGroupNumber
0415: String ignSeparator = "_ign_";
0416:
0417: // iterate through the context and find all keys that start with "quantity_"
0418: Iterator entryIter = context.entrySet().iterator();
0419: while (entryIter.hasNext()) {
0420: Map.Entry entry = (Map.Entry) entryIter.next();
0421: String productId = null;
0422: String quantStr = null;
0423: String itemGroupNumberToUse = itemGroupNumber;
0424: if (entry.getKey() instanceof String) {
0425: String key = (String) entry.getKey();
0426: //Debug.logInfo("Bulk Key: " + key, module);
0427:
0428: int ignIndex = key.indexOf(ignSeparator);
0429: if (ignIndex > 0) {
0430: itemGroupNumberToUse = key.substring(ignIndex
0431: + ignSeparator.length());
0432: key = key.substring(0, ignIndex);
0433: }
0434:
0435: if (key.startsWith(keyPrefix)) {
0436: productId = key.substring(keyPrefix.length());
0437: quantStr = (String) entry.getValue();
0438: } else if (key.startsWith(productQuantityKeyPrefix)) {
0439: productId = (String) entry.getValue();
0440: quantStr = "1";
0441: } else {
0442: continue;
0443: }
0444: } else {
0445: continue;
0446: }
0447:
0448: if (quantStr != null && quantStr.length() > 0) {
0449: double quantity = 0;
0450:
0451: try {
0452: quantity = Double.parseDouble(quantStr);
0453: } catch (NumberFormatException nfe) {
0454: quantity = 0;
0455: }
0456: if (quantity > 0.0) {
0457: try {
0458: if (Debug.verboseOn())
0459: Debug.logVerbose("Bulk Adding to cart ["
0460: + quantity + "] of [" + productId
0461: + "] in Item Group ["
0462: + itemGroupNumber + "]", module);
0463: this .cart.addOrIncreaseItem(productId, null,
0464: quantity, null, null, null, null, null,
0465: null, null, catalogId, null, null,
0466: itemGroupNumberToUse, null, dispatcher);
0467: } catch (CartItemModifyException e) {
0468: return ServiceUtil.returnError(e.getMessage());
0469: } catch (ItemNotFoundException e) {
0470: return ServiceUtil.returnError(e.getMessage());
0471: }
0472: }
0473: }
0474: }
0475:
0476: //Indicate there were no non critical errors
0477: return ServiceUtil.returnSuccess();
0478: }
0479:
0480: /**
0481: * Adds a set of requirements to the cart.
0482: */
0483: public Map addToCartBulkRequirements(String catalogId, Map context) {
0484: NumberFormat nf = NumberFormat.getNumberInstance(this .cart
0485: .getLocale());
0486: String itemGroupNumber = (String) context
0487: .get("itemGroupNumber");
0488: // check if we are using per row submit
0489: boolean useRowSubmit = (!context.containsKey("_useRowSubmit")) ? false
0490: : "Y".equalsIgnoreCase((String) context
0491: .get("_useRowSubmit"));
0492:
0493: // check if we are to also look in a global scope (no delimiter)
0494: //boolean checkGlobalScope = (!context.containsKey("_checkGlobalScope"))? false :
0495: // "Y".equalsIgnoreCase((String)context.get("_checkGlobalScope"));
0496:
0497: int rowCount = 0; // parsed int value
0498: try {
0499: if (context.containsKey("_rowCount")) {
0500: rowCount = Integer.parseInt((String) context
0501: .get("_rowCount"));
0502: }
0503: } catch (NumberFormatException e) {
0504: //throw new EventHandlerException("Invalid value for _rowCount");
0505: }
0506:
0507: // assume that the facility is the same for all requirements
0508: String facilityId = (String) context.get("facilityId_o_0");
0509: if (UtilValidate.isNotEmpty(facilityId)) {
0510: cart.setFacilityId(facilityId);
0511: }
0512:
0513: // now loop throw the rows and prepare/invoke the service for each
0514: for (int i = 0; i < rowCount; i++) {
0515: String productId = null;
0516: String quantStr = null;
0517: String requirementId = null;
0518: String this Suffix = UtilHttp.MULTI_ROW_DELIMITER + i;
0519: boolean rowSelected = (!context.containsKey("_rowSubmit"
0520: + this Suffix)) ? false : "Y"
0521: .equalsIgnoreCase((String) context.get("_rowSubmit"
0522: + this Suffix));
0523:
0524: // make sure we are to process this row
0525: if (useRowSubmit && !rowSelected) {
0526: continue;
0527: }
0528:
0529: // build the context
0530: if (context.containsKey("productId" + this Suffix)) {
0531: productId = (String) context.get("productId"
0532: + this Suffix);
0533: quantStr = (String) context
0534: .get("quantity" + this Suffix);
0535: requirementId = (String) context.get("requirementId"
0536: + this Suffix);
0537: GenericValue requirement = null;
0538: try {
0539: requirement = delegator.findByPrimaryKey(
0540: "Requirement", UtilMisc.toMap(
0541: "requirementId", requirementId));
0542: } catch (GenericEntityException gee) {
0543: }
0544: if (requirement == null) {
0545: return ServiceUtil
0546: .returnError("Requirement with id ["
0547: + requirementId
0548: + "] doesn't exist.");
0549: }
0550:
0551: if (quantStr != null && quantStr.length() > 0) {
0552: double quantity = 0;
0553: try {
0554: quantity = nf.parse(quantStr).doubleValue();
0555: } catch (ParseException nfe) {
0556: quantity = 0;
0557: }
0558: if (quantity > 0.0) {
0559: Iterator items = this .cart.iterator();
0560: boolean requirementAlreadyInCart = false;
0561: while (items.hasNext()
0562: && !requirementAlreadyInCart) {
0563: ShoppingCartItem sci = (ShoppingCartItem) items
0564: .next();
0565: if (sci.getRequirementId() != null
0566: && sci.getRequirementId().equals(
0567: requirementId)) {
0568: requirementAlreadyInCart = true;
0569: continue;
0570: }
0571: }
0572: if (requirementAlreadyInCart) {
0573: if (Debug.warningOn())
0574: Debug
0575: .logWarning(
0576: UtilProperties
0577: .getMessage(
0578: resource_error,
0579: "OrderTheRequirementIsAlreadyInTheCartNotAdding",
0580: UtilMisc
0581: .toMap(
0582: "requirementId",
0583: requirementId),
0584: cart
0585: .getLocale()),
0586: module);
0587: continue;
0588: }
0589: try {
0590: if (Debug.verboseOn())
0591: Debug.logVerbose(
0592: "Bulk Adding to cart requirement ["
0593: + quantity + "] of ["
0594: + productId + "]",
0595: module);
0596: int index = this .cart
0597: .addOrIncreaseItem(
0598: productId,
0599: null,
0600: quantity,
0601: null,
0602: null,
0603: null,
0604: requirement
0605: .getTimestamp("requiredByDate"),
0606: null, null, null,
0607: catalogId, null, null,
0608: itemGroupNumber, null,
0609: dispatcher);
0610: ShoppingCartItem sci = (ShoppingCartItem) this .cart
0611: .items().get(index);
0612: sci.setRequirementId(requirementId);
0613: } catch (CartItemModifyException e) {
0614: return ServiceUtil.returnError(e
0615: .getMessage());
0616: } catch (ItemNotFoundException e) {
0617: return ServiceUtil.returnError(e
0618: .getMessage());
0619: }
0620: }
0621: }
0622: }
0623: }
0624: //Indicate there were no non critical errors
0625: return ServiceUtil.returnSuccess();
0626: }
0627:
0628: /**
0629: * Adds all products in a category according to default quantity on ProductCategoryMember
0630: * for each; if no default for a certain product in the category, or if
0631: * quantity is 0, do not add
0632: */
0633: public Map addCategoryDefaults(String catalogId, String categoryId,
0634: String itemGroupNumber) {
0635: ArrayList errorMsgs = new ArrayList();
0636: Map result = null;
0637: String errMsg = null;
0638:
0639: if (categoryId == null || categoryId.length() <= 0) {
0640: errMsg = UtilProperties.getMessage(resource,
0641: "cart.category_not_specified_to_add_from",
0642: this .cart.getLocale());
0643: result = ServiceUtil.returnError(errMsg);
0644: // result = ServiceUtil.returnError(UtilProperties.getMessage(resource_error,"OrderNoCategorySpecifiedToAddFrom.",this.cart.getLocale()));
0645: return result;
0646: }
0647:
0648: Collection prodCatMemberCol = null;
0649:
0650: try {
0651: prodCatMemberCol = delegator.findByAndCache(
0652: "ProductCategoryMember", UtilMisc.toMap(
0653: "productCategoryId", categoryId));
0654: } catch (GenericEntityException e) {
0655: Debug.logWarning(e.toString(), module);
0656: Map messageMap = UtilMisc.toMap("categoryId", categoryId);
0657: messageMap.put("message", e.getMessage());
0658: errMsg = UtilProperties.getMessage(resource,
0659: "cart.could_not_get_products_in_category_cart",
0660: messageMap, this .cart.getLocale());
0661: result = ServiceUtil.returnError(errMsg);
0662: return result;
0663: }
0664:
0665: if (prodCatMemberCol == null) {
0666: Map messageMap = UtilMisc.toMap("categoryId", categoryId);
0667: errMsg = UtilProperties.getMessage(resource,
0668: "cart.could_not_get_products_in_category",
0669: messageMap, this .cart.getLocale());
0670: result = ServiceUtil.returnError(errMsg);
0671: return result;
0672: }
0673:
0674: double totalQuantity = 0;
0675: Iterator pcmIter = prodCatMemberCol.iterator();
0676:
0677: while (pcmIter.hasNext()) {
0678: GenericValue productCategoryMember = (GenericValue) pcmIter
0679: .next();
0680: Double quantity = productCategoryMember
0681: .getDouble("quantity");
0682:
0683: if (quantity != null && quantity.doubleValue() > 0.0) {
0684: try {
0685: this .cart.addOrIncreaseItem(productCategoryMember
0686: .getString("productId"), null, quantity
0687: .doubleValue(), null, null, null, null,
0688: null, null, null, catalogId, null, null,
0689: itemGroupNumber, null, dispatcher);
0690: totalQuantity += quantity.doubleValue();
0691: } catch (CartItemModifyException e) {
0692: errorMsgs.add(e.getMessage());
0693: } catch (ItemNotFoundException e) {
0694: errorMsgs.add(e.getMessage());
0695: }
0696: }
0697: }
0698: if (errorMsgs.size() > 0) {
0699: result = ServiceUtil.returnError(errorMsgs);
0700: result.put(ModelService.RESPONSE_MESSAGE,
0701: ModelService.RESPOND_SUCCESS);
0702: return result; // don't return error because this is a non-critical error and should go back to the same page
0703: }
0704:
0705: result = ServiceUtil.returnSuccess();
0706: result.put("totalQuantity", new Double(totalQuantity));
0707: return result;
0708: }
0709:
0710: /** Delete an item from the shopping cart. */
0711: public Map deleteFromCart(Map context) {
0712: Map result = null;
0713: Set names = context.keySet();
0714: Iterator i = names.iterator();
0715: ArrayList errorMsgs = new ArrayList();
0716:
0717: while (i.hasNext()) {
0718: String o = (String) i.next();
0719:
0720: if (o.toUpperCase().startsWith("DELETE")) {
0721: try {
0722: String indexStr = o
0723: .substring(o.lastIndexOf('_') + 1);
0724: int index = Integer.parseInt(indexStr);
0725:
0726: try {
0727: this .cart.removeCartItem(index, dispatcher);
0728: } catch (CartItemModifyException e) {
0729: errorMsgs.add(e.getMessage());
0730: }
0731: } catch (NumberFormatException nfe) {
0732: }
0733: }
0734: }
0735:
0736: if (errorMsgs.size() > 0) {
0737: result = ServiceUtil.returnError(errorMsgs);
0738: result.put(ModelService.RESPONSE_MESSAGE,
0739: ModelService.RESPOND_SUCCESS);
0740: return result; // don't return error because this is a non-critical error and should go back to the same page
0741: }
0742:
0743: result = ServiceUtil.returnSuccess();
0744: return result;
0745: }
0746:
0747: /** Update the items in the shopping cart. */
0748: public Map modifyCart(Security security, GenericValue userLogin,
0749: Map context, boolean removeSelected,
0750: String[] selectedItems, Locale locale) {
0751: Map result = null;
0752: if (locale == null) {
0753: locale = this .cart.getLocale();
0754: }
0755: NumberFormat nf = NumberFormat.getNumberInstance(locale);
0756:
0757: ArrayList deleteList = new ArrayList();
0758: ArrayList errorMsgs = new ArrayList();
0759:
0760: Set parameterNames = context.keySet();
0761: Iterator parameterNameIter = parameterNames.iterator();
0762:
0763: double oldQuantity = -1;
0764: String oldDescription = "";
0765: double oldPrice = -1;
0766:
0767: if (this .cart.isReadOnlyCart()) {
0768: String errMsg = UtilProperties.getMessage(resource,
0769: "cart.cart_is_in_read_only_mode", this .cart
0770: .getLocale());
0771: errorMsgs.add(errMsg);
0772: result = ServiceUtil.returnError(errorMsgs);
0773: return result;
0774: }
0775:
0776: // TODO: This should be refactored to use UtilHttp.parseMultiFormData(parameters)
0777: while (parameterNameIter.hasNext()) {
0778: String parameterName = (String) parameterNameIter.next();
0779: int underscorePos = parameterName.lastIndexOf('_');
0780:
0781: if (underscorePos >= 0) {
0782: try {
0783: String indexStr = parameterName
0784: .substring(underscorePos + 1);
0785: int index = Integer.parseInt(indexStr);
0786: String quantString = (String) context
0787: .get(parameterName);
0788: double quantity = -1;
0789: String itemDescription = "";
0790: if (quantString != null)
0791: quantString = quantString.trim();
0792:
0793: // get the cart item
0794: ShoppingCartItem item = this .cart
0795: .findCartItem(index);
0796: if (parameterName.toUpperCase()
0797: .startsWith("OPTION")) {
0798: if (quantString.toUpperCase().startsWith("NO^")) {
0799: if (quantString.length() > 2) { // the length of the prefix
0800: String featureTypeId = this
0801: .getRemoveFeatureTypeId(parameterName);
0802: if (featureTypeId != null) {
0803: item
0804: .removeAdditionalProductFeatureAndAppl(featureTypeId);
0805: }
0806: }
0807: } else {
0808: GenericValue featureAppl = this
0809: .getFeatureAppl(
0810: item.getProductId(),
0811: parameterName, quantString);
0812: if (featureAppl != null) {
0813: item
0814: .putAdditionalProductFeatureAndAppl(featureAppl);
0815: }
0816: }
0817: } else if (parameterName.toUpperCase().startsWith(
0818: "DESCRIPTION")) {
0819: itemDescription = quantString; // the quantString is actually the description if the field name starts with DESCRIPTION
0820: } else if (parameterName.startsWith("reservStart")) {
0821: // should have format: yyyy-mm-dd hh:mm:ss.fffffffff
0822: quantString += " 00:00:00.000000000";
0823: if (item != null) {
0824: Timestamp reservStart = Timestamp
0825: .valueOf(quantString);
0826: item.setReservStart(reservStart);
0827: }
0828: } else if (parameterName.startsWith("reservLength")) {
0829: if (item != null) {
0830: double reservLength = nf.parse(quantString)
0831: .doubleValue();
0832: item.setReservLength(reservLength);
0833: }
0834: } else if (parameterName
0835: .startsWith("reservPersons")) {
0836: if (item != null) {
0837: double reservPersons = nf
0838: .parse(quantString).doubleValue();
0839: item.setReservPersons(reservPersons);
0840: }
0841: } else if (parameterName
0842: .startsWith("shipBeforeDate")) {
0843: if (item != null && quantString.length() > 0) {
0844: // input is either yyyy-mm-dd or a full timestamp
0845: if (quantString.length() == 10)
0846: quantString += " 00:00:00.000";
0847: item.setShipBeforeDate(Timestamp
0848: .valueOf(quantString));
0849: }
0850: } else if (parameterName
0851: .startsWith("shipAfterDate")) {
0852: if (item != null && quantString.length() > 0) {
0853: // input is either yyyy-mm-dd or a full timestamp
0854: if (quantString.length() == 10)
0855: quantString += " 00:00:00.000";
0856: item.setShipAfterDate(Timestamp
0857: .valueOf(quantString));
0858: }
0859: } else if (parameterName.startsWith("itemType")) {
0860: if (item != null && quantString.length() > 0) {
0861: item.setItemType(quantString);
0862: }
0863: } else {
0864: quantity = nf.parse(quantString).doubleValue();
0865: if (quantity < 0) {
0866: throw new CartItemModifyException(
0867: "Quantity must be a positive number.");
0868: }
0869: }
0870:
0871: // perhaps we need to reset the ship groups' before and after dates based on new dates for the items
0872: if (parameterName.startsWith("shipAfterDate")
0873: || parameterName
0874: .startsWith("shipBeforeDate")) {
0875: this .cart.setShipGroupShipDatesFromItem(item);
0876: }
0877:
0878: if (parameterName.toUpperCase()
0879: .startsWith("UPDATE")) {
0880: if (quantity == 0.0) {
0881: deleteList.add(item);
0882: } else {
0883: if (item != null) {
0884: try {
0885: // if, on a purchase order, the quantity has changed, get the new SupplierProduct entity for this quantity level.
0886: if (cart.getOrderType().equals(
0887: "PURCHASE_ORDER")) {
0888: oldQuantity = item
0889: .getQuantity();
0890: if (oldQuantity != quantity) {
0891: // save the old description and price, in case the user wants to change those as well
0892: oldDescription = item
0893: .getName();
0894: oldPrice = item
0895: .getBasePrice();
0896:
0897: GenericValue productSupplier = this
0898: .getProductSupplier(
0899: item
0900: .getProductId(),
0901: new Double(
0902: quantity),
0903: cart
0904: .getCurrency());
0905:
0906: if (productSupplier == null) {
0907: if ("_NA_".equals(cart
0908: .getPartyId())) {
0909: // no supplier does not require the supplier product
0910: item.setQuantity(
0911: quantity,
0912: dispatcher,
0913: this .cart);
0914: item
0915: .setName(item
0916: .getProduct()
0917: .getString(
0918: "internalName"));
0919: } else {
0920: // in this case, the user wanted to purchase a quantity which is not available (probably below minimum)
0921: String errMsg = UtilProperties
0922: .getMessage(
0923: resource,
0924: "cart.product_not_valid_for_supplier",
0925: this .cart
0926: .getLocale());
0927: errMsg = errMsg
0928: + " ("
0929: + item
0930: .getProductId()
0931: + ", "
0932: + quantity
0933: + ", "
0934: + cart
0935: .getCurrency()
0936: + ")";
0937: errorMsgs
0938: .add(errMsg);
0939: }
0940: } else {
0941: item.setQuantity(
0942: quantity,
0943: dispatcher,
0944: this .cart);
0945: item
0946: .setBasePrice(productSupplier
0947: .getDouble(
0948: "lastPrice")
0949: .doubleValue());
0950: item
0951: .setName(ShoppingCartItem
0952: .getPurchaseOrderItemDescription(
0953: item
0954: .getProduct(),
0955: productSupplier,
0956: cart
0957: .getLocale()));
0958: }
0959: }
0960: } else {
0961: item.setQuantity(quantity,
0962: dispatcher, this .cart);
0963: }
0964: } catch (CartItemModifyException e) {
0965: errorMsgs.add(e.getMessage());
0966: }
0967: }
0968: }
0969: }
0970:
0971: if (parameterName.toUpperCase().startsWith(
0972: "DESCRIPTION")) {
0973: if (!oldDescription.equals(itemDescription)) {
0974: if (security.hasEntityPermission(
0975: "ORDERMGR", "_CREATE", userLogin)) {
0976: if (item != null) {
0977: item.setName(itemDescription);
0978: }
0979: }
0980: }
0981: }
0982:
0983: if (parameterName.toUpperCase().startsWith("PRICE")) {
0984: NumberFormat pf = NumberFormat
0985: .getCurrencyInstance(locale);
0986: String tmpQuantity = pf.format(quantity);
0987: String tmpOldPrice = pf.format(oldPrice);
0988: if (!tmpOldPrice.equals(tmpQuantity)) {
0989: if (security.hasEntityPermission(
0990: "ORDERMGR", "_CREATE", userLogin)) {
0991: if (item != null) {
0992: item.setBasePrice(quantity); // this is quantity because the parsed number variable is the same as quantity
0993: item.setDisplayPrice(quantity); // or the amount shown the cart items page won't be right
0994: item.setIsModifiedPrice(true); // flag as a modified price
0995: }
0996: }
0997: }
0998: }
0999:
1000: if (parameterName.toUpperCase()
1001: .startsWith("DELETE")) {
1002: deleteList.add(this .cart.findCartItem(index));
1003: }
1004: } catch (NumberFormatException nfe) {
1005: Debug
1006: .logWarning(
1007: nfe,
1008: UtilProperties
1009: .getMessage(
1010: resource_error,
1011: "OrderCaughtNumberFormatExceptionOnCartUpdate",
1012: cart.getLocale()));
1013: } catch (ParseException pe) {
1014: Debug.logWarning(pe, UtilProperties.getMessage(
1015: resource_error,
1016: "OrderCaughtParseExceptionOnCartUpdate",
1017: cart.getLocale()));
1018: } catch (Exception e) {
1019: Debug.logWarning(e, UtilProperties.getMessage(
1020: resource_error,
1021: "OrderCaughtExceptionOnCartUpdate", cart
1022: .getLocale()));
1023: }
1024: } // else not a parameter we need
1025: }
1026:
1027: // get a list of the items to delete
1028: if (removeSelected) {
1029: for (int si = 0; si < selectedItems.length; si++) {
1030: String indexStr = selectedItems[si];
1031: ShoppingCartItem item = null;
1032: try {
1033: int index = Integer.parseInt(indexStr);
1034: item = this .cart.findCartItem(index);
1035: } catch (Exception e) {
1036: Debug.logWarning(e, UtilProperties.getMessage(
1037: resource_error,
1038: "OrderProblemsGettingTheCartItemByIndex",
1039: cart.getLocale()));
1040: }
1041: if (item != null) {
1042: deleteList.add(item);
1043: }
1044: }
1045: }
1046:
1047: Iterator di = deleteList.iterator();
1048:
1049: while (di.hasNext()) {
1050: ShoppingCartItem item = (ShoppingCartItem) di.next();
1051: int itemIndex = this .cart.getItemIndex(item);
1052:
1053: if (Debug.infoOn())
1054: Debug.logInfo("Removing item index: " + itemIndex,
1055: module);
1056: try {
1057: this .cart.removeCartItem(itemIndex, dispatcher);
1058: } catch (CartItemModifyException e) {
1059: result = ServiceUtil.returnError(new Vector());
1060: errorMsgs.add(e.getMessage());
1061: }
1062: }
1063:
1064: if (context.containsKey("alwaysShowcart")) {
1065: this .cart.setViewCartOnAdd(true);
1066: } else {
1067: this .cart.setViewCartOnAdd(false);
1068: }
1069:
1070: // Promotions are run again.
1071: ProductPromoWorker.doPromotions(this .cart, dispatcher);
1072:
1073: if (errorMsgs.size() > 0) {
1074: result = ServiceUtil.returnError(errorMsgs);
1075: return result;
1076: }
1077:
1078: result = ServiceUtil.returnSuccess();
1079: return result;
1080: }
1081:
1082: /** Empty the shopping cart. */
1083: public boolean clearCart() {
1084: this .cart.clear();
1085: return true;
1086: }
1087:
1088: /** Returns the shopping cart this helper is wrapping. */
1089: public ShoppingCart getCartObject() {
1090: return this .cart;
1091: }
1092:
1093: public GenericValue getFeatureAppl(String productId,
1094: String optionField, String featureId) {
1095: if (delegator == null) {
1096: throw new IllegalArgumentException(
1097: "No delegator available to lookup ProductFeature");
1098: }
1099:
1100: Map fields = UtilMisc.toMap("productId", productId,
1101: "productFeatureId", featureId);
1102: if (optionField != null) {
1103: int featureTypeStartIndex = optionField.indexOf('^') + 1;
1104: int featureTypeEndIndex = optionField.lastIndexOf('_');
1105: if (featureTypeStartIndex > 0 && featureTypeEndIndex > 0) {
1106: fields.put("productFeatureTypeId", optionField
1107: .substring(featureTypeStartIndex,
1108: featureTypeEndIndex));
1109: }
1110: }
1111:
1112: GenericValue productFeatureAppl = null;
1113: List features = null;
1114: try {
1115: features = delegator.findByAnd("ProductFeatureAndAppl",
1116: fields, UtilMisc.toList("-fromDate"));
1117: } catch (GenericEntityException e) {
1118: Debug.logError(e, module);
1119: return null;
1120: }
1121:
1122: if (features != null) {
1123: if (features.size() > 1) {
1124: features = EntityUtil.filterByDate(features);
1125: }
1126: productFeatureAppl = EntityUtil.getFirst(features);
1127: }
1128:
1129: return productFeatureAppl;
1130: }
1131:
1132: public String getRemoveFeatureTypeId(String optionField) {
1133: if (optionField != null) {
1134: int featureTypeStartIndex = optionField.indexOf('^') + 1;
1135: int featureTypeEndIndex = optionField.lastIndexOf('_');
1136: if (featureTypeStartIndex > 0 && featureTypeEndIndex > 0) {
1137: return optionField.substring(featureTypeStartIndex,
1138: featureTypeEndIndex);
1139: }
1140: }
1141: return null;
1142: }
1143:
1144: /**
1145: * Select an agreement
1146: *
1147: * @param agreementId
1148: */
1149: public Map selectAgreement(String agreementId) {
1150: Map result = null;
1151: GenericValue agreement = null;
1152:
1153: if ((this .delegator == null) || (this .dispatcher == null)
1154: || (this .cart == null)) {
1155: result = ServiceUtil.returnError(UtilProperties.getMessage(
1156: resource_error,
1157: "OrderDispatcherOrDelegatorOrCartArgumentIsNull",
1158: this .cart.getLocale()));
1159: return result;
1160: }
1161:
1162: if ((agreementId == null) || (agreementId.length() <= 0)) {
1163: result = ServiceUtil.returnError(UtilProperties.getMessage(
1164: resource_error, "OrderNoAgreementSpecified",
1165: this .cart.getLocale()));
1166: return result;
1167: }
1168:
1169: try {
1170: agreement = this .delegator.findByPrimaryKeyCache(
1171: "Agreement", UtilMisc.toMap("agreementId",
1172: agreementId));
1173: } catch (GenericEntityException e) {
1174: Debug.logWarning(e.toString(), module);
1175: result = ServiceUtil.returnError(UtilProperties.getMessage(
1176: resource_error, "OrderCouldNotGetAgreement",
1177: UtilMisc.toMap("agreementId", agreementId),
1178: this .cart.getLocale())
1179: + UtilProperties.getMessage(resource_error,
1180: "OrderError", this .cart.getLocale())
1181: + e.getMessage());
1182: return result;
1183: }
1184:
1185: if (agreement == null) {
1186: result = ServiceUtil.returnError(UtilProperties.getMessage(
1187: resource_error, "OrderCouldNotGetAgreement",
1188: UtilMisc.toMap("agreementId", agreementId),
1189: this .cart.getLocale()));
1190: } else {
1191: // set the agreement id in the cart
1192: cart.setAgreementId(agreementId);
1193: try {
1194: // set the currency based on the pricing agreement
1195: List agreementItems = agreement.getRelated(
1196: "AgreementItem", UtilMisc.toMap(
1197: "agreementItemTypeId",
1198: "AGREEMENT_PRICING_PR"), null);
1199: if (agreementItems.size() > 0) {
1200: GenericValue agreementItem = (GenericValue) agreementItems
1201: .get(0);
1202: String currencyUomId = (String) agreementItem
1203: .get("currencyUomId");
1204: try {
1205: cart.setCurrency(dispatcher, currencyUomId);
1206: } catch (CartItemModifyException ex) {
1207: result = ServiceUtil.returnError(UtilProperties
1208: .getMessage(resource_error,
1209: "OrderSetCurrencyError",
1210: this .cart.getLocale())
1211: + ex.getMessage());
1212: return result;
1213: }
1214: }
1215: } catch (GenericEntityException e) {
1216: Debug.logWarning(e.toString(), module);
1217: result = ServiceUtil
1218: .returnError(UtilProperties
1219: .getMessage(
1220: resource_error,
1221: "OrderCouldNotGetAgreementItemsThrough",
1222: UtilMisc.toMap("agreementId",
1223: agreementId), this .cart
1224: .getLocale())
1225: + UtilProperties.getMessage(
1226: resource_error, "OrderError",
1227: this .cart.getLocale())
1228: + e.getMessage());
1229: return result;
1230: }
1231:
1232: try {
1233: // clear the existing order terms
1234: cart.removeOrderTerms();
1235: // set order terms based on agreement terms
1236: List agreementTerms = agreement
1237: .getRelated("AgreementTerm");
1238: if (agreementTerms.size() > 0) {
1239: for (int i = 0; agreementTerms.size() > i; i++) {
1240: GenericValue agreementTerm = (GenericValue) agreementTerms
1241: .get(i);
1242: String termTypeId = (String) agreementTerm
1243: .get("termTypeId");
1244: Double termValue = (Double) agreementTerm
1245: .get("termValue");
1246: Long termDays = (Long) agreementTerm
1247: .get("termDays");
1248: String description = agreementTerm
1249: .getString("description");
1250: cart.addOrderTerm(termTypeId, termValue,
1251: termDays, description);
1252: }
1253: }
1254: } catch (GenericEntityException e) {
1255: Debug.logWarning(e.toString(), module);
1256: result = ServiceUtil
1257: .returnError(UtilProperties
1258: .getMessage(
1259: resource_error,
1260: "OrderCouldNotGetAgreementTermsThrough",
1261: UtilMisc.toMap("agreementId",
1262: agreementId), this .cart
1263: .getLocale())
1264: + UtilProperties.getMessage(
1265: resource_error, "OrderError",
1266: this .cart.getLocale())
1267: + e.getMessage());
1268: return result;
1269: }
1270: }
1271: return result;
1272: }
1273:
1274: public Map setCurrency(String currencyUomId) {
1275: Map result = null;
1276:
1277: try {
1278: this .cart.setCurrency(this .dispatcher, currencyUomId);
1279: result = ServiceUtil.returnSuccess();
1280: } catch (CartItemModifyException ex) {
1281: result = ServiceUtil.returnError(UtilProperties.getMessage(
1282: resource_error, "Set currency error", this .cart
1283: .getLocale())
1284: + ex.getMessage());
1285: return result;
1286: }
1287: return result;
1288: }
1289:
1290: public Map addOrderTerm(String termTypeId, Double termValue,
1291: Long termDays) {
1292: return addOrderTerm(termTypeId, termValue, termDays, null);
1293: }
1294:
1295: public Map addOrderTerm(String termTypeId, Double termValue,
1296: Long termDays, String description) {
1297: Map result = null;
1298: this .cart.addOrderTerm(termTypeId, termValue, termDays,
1299: description);
1300: result = ServiceUtil.returnSuccess();
1301: return result;
1302: }
1303:
1304: public Map removeOrderTerm(int index) {
1305: Map result = null;
1306: this .cart.removeOrderTerm(index);
1307: result = ServiceUtil.returnSuccess();
1308: return result;
1309: }
1310:
1311: /** Get the first SupplierProduct record for productId with matching quantity and currency */
1312: public GenericValue getProductSupplier(String productId,
1313: Double quantity, String currencyUomId) {
1314: GenericValue productSupplier = null;
1315: Map params = UtilMisc.toMap("productId", productId, "partyId",
1316: cart.getPartyId(), "currencyUomId", currencyUomId,
1317: "quantity", quantity);
1318: try {
1319: Map result = dispatcher.runSync("getSuppliersForProduct",
1320: params);
1321: List productSuppliers = (List) result
1322: .get("supplierProducts");
1323: if ((productSuppliers != null)
1324: && (productSuppliers.size() > 0)) {
1325: productSupplier = (GenericValue) productSuppliers
1326: .get(0);
1327: }
1328: } catch (GenericServiceException e) {
1329: Debug.logWarning(UtilProperties.getMessage(resource_error,
1330: "OrderRunServiceGetSuppliersForProductError", cart
1331: .getLocale())
1332: + e.getMessage(), module);
1333: }
1334: return productSupplier;
1335: }
1336:
1337: }
|