applications/order/src/org/ofbiz/order/shoppingcart/ShoppingCart.java
/*******************************************************************************
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*******************************************************************************/
package org.ofbiz.order.shoppingcart;
import com.ilscipio.scipio.common.SolicitationTypeEnum;
import org.ofbiz.base.util.Debug;
import org.ofbiz.base.util.GeneralException;
import org.ofbiz.base.util.GeneralRuntimeException;
import org.ofbiz.base.util.StringUtil;
import org.ofbiz.base.util.UtilDateTime;
import org.ofbiz.base.util.UtilFormatOut;
import org.ofbiz.base.util.UtilGenerics;
import org.ofbiz.base.util.UtilMisc;
import org.ofbiz.base.util.UtilNumber;
import org.ofbiz.base.util.UtilProperties;
import org.ofbiz.base.util.UtilValidate;
import org.ofbiz.common.DataModelConstants;
import org.ofbiz.entity.Delegator;
import org.ofbiz.entity.DelegatorFactory;
import org.ofbiz.entity.GenericEntityException;
import org.ofbiz.entity.GenericPK;
import org.ofbiz.entity.GenericValue;
import org.ofbiz.entity.util.EntityQuery;
import org.ofbiz.entity.util.EntityUtil;
import org.ofbiz.entity.util.EntityUtilProperties;
import org.ofbiz.order.finaccount.FinAccountHelper;
import org.ofbiz.order.order.OrderReadHelper;
import org.ofbiz.order.shoppingcart.product.ProductPromoWorker;
import org.ofbiz.order.shoppingcart.shipping.ShippingEstimateWrapper;
import org.ofbiz.order.shoppinglist.ShoppingListCookieInfo;
import org.ofbiz.order.shoppinglist.ShoppingListEvents;
import org.ofbiz.order.shoppinglist.ShoppingListWorker;
import org.ofbiz.party.contact.ContactHelper;
import org.ofbiz.party.contact.ContactMechWorker;
import org.ofbiz.product.category.CategoryWorker;
import org.ofbiz.product.config.ProductConfigWrapper;
import org.ofbiz.product.product.ProductWorker;
import org.ofbiz.product.store.ProductStoreWorker;
import org.ofbiz.service.GenericServiceException;
import org.ofbiz.service.LocalDispatcher;
import org.ofbiz.service.ServiceUtil;
import java.io.Serializable;
import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
/**
* Shopping Cart Object
* <p>
* SCIPIO: IMPORTANT: 2018-11-22: Any event or code which modifies the main shopping cart stored in session ("shoppingCart")
* must now wrap its update code in a {@link CartUpdate#updateSection} or {@link CartSync#synchronizedSection} section.
* The main instance currently stored in session/request attribute is now considered immutable and can only be updated using
* an update section (which creates a modifiable copy that may be committed to replace the main session cart instance).
*
* @see CartUpdate
* @see CartSync
*/
@SuppressWarnings("serial")
public class ShoppingCart implements Iterable<ShoppingCartItem>, Serializable {
private static final Debug.OfbizLogger module = Debug.getOfbizLogger(java.lang.invoke.MethodHandles.lookup().lookupClass());
public static final String resource_error = "OrderErrorUiLabels";
// modes for getting OrderItemAttributes
public static final int ALL = 1;
public static final int EMPTY_ONLY = 2;
public static final int FILLED_ONLY = 3;
// scales and rounding modes for BigDecimal math
public static final int scale = UtilNumber.getBigDecimalScale("order.decimals");
public static final RoundingMode rounding = UtilNumber.getRoundingMode("order.rounding");
public static final int taxCalcScale = UtilNumber.getBigDecimalScale("salestax.calc.decimals");
public static final int taxFinalScale = UtilNumber.getBigDecimalScale("salestax.final.decimals");
public static final RoundingMode taxRounding = UtilNumber.getRoundingMode("salestax.rounding");
/**
* @deprecated SCIPIO: 2018-10-09: use {@link BigDecimal#ZERO}.
*/
@Deprecated
public static final BigDecimal ZERO = BigDecimal.ZERO;
public static final BigDecimal percentage = new BigDecimal("0.01");
public static final MathContext generalRounding = new MathContext(10);
public static final boolean DEBUG = UtilProperties.getPropertyAsBoolean("order", "shoppingcart.debug", false); // SCIPIO
static { // SCIPIO
if (ShoppingCart.verboseOn() && CartUpdate.isCartCopiesEnabled()) {
ShoppingCartChangeVerifier.register();
}
}
// SCIPIO: NOTE: 2018-11-22: Many default values have been moved to the constructors, or removed and defaults used (null/false)
protected String orderType = "SALES_ORDER"; // default orderType
protected String channel = "UNKNWN_SALES_CHANNEL"; // default channel enum
protected String poNumber;
protected String orderId;
protected String orderName;
protected String orderStatusId;
protected String orderStatusString;
protected String firstAttemptOrderId;
protected String externalId;
protected String internalCode;
protected String billingAccountId;
protected BigDecimal billingAccountAmt = BigDecimal.ZERO;
protected String agreementId;
protected String quoteId;
protected String workEffortId;
protected long nextItemSeq = 1;
protected String defaultItemDeliveryDate;
protected String defaultItemComment;
protected String orderAdditionalEmails;
protected boolean viewCartOnAdd; // = false;
protected boolean readOnlyCart; // = false;
protected Timestamp lastListRestore;
protected String autoSaveListId;
protected String autoSaveListAuthToken; // SCIPIO
protected Map<String, Object> autoSaveListParams = Collections.emptyMap(); // SCIPIO
/** Holds value of order adjustments. */
protected List<GenericValue> adjustments = new ArrayList<>();
// OrderTerms
protected boolean orderTermSet; // = false;
protected List<GenericValue> orderTerms = new ArrayList<>();
protected List<ShoppingCartItem> cartLines = new ArrayList<>();
protected Map<String, ShoppingCartItemGroup> itemGroupByNumberMap = new HashMap<>();
protected long nextGroupNumber = 1;
protected List<CartPaymentInfo> paymentInfo = new ArrayList<>();
protected List<CartShipInfo> shipInfo = new ArrayList<>();
protected Map<String, String> contactMechIdsMap = new HashMap<>();
protected Map<String, String> orderAttributes = new HashMap<>();
protected Map<String, Object> attributes = new HashMap<>(); // user defined attributes
// Lists of internal/public notes: when the order is stored they are transformed into OrderHeaderNotes
protected List<String> internalOrderNotes = new ArrayList<>(); // internal notes
protected List<String> orderNotes = new ArrayList<>(); // public notes (printed on documents etc.)
/** contains a list of partyId for each roleTypeId (key) */
protected Map<String, List<String>> additionalPartyRole = new HashMap<>();
/** these are defaults for all ship groups */
protected Timestamp defaultShipAfterDate;
protected Timestamp defaultShipBeforeDate;
/** Contains a List for each productPromoId (key) containing a productPromoCodeId (or empty string for no code) for each use of the productPromoId */
protected List<ProductPromoUseInfo> productPromoUseInfoList = new ArrayList<>();
/** Contains the promo codes entered */
protected Set<String> productPromoCodes = new HashSet<>();
protected List<GenericValue> freeShippingProductPromoActions = new ArrayList<>();
/** Note that even though this is promotion info, it should NOT be cleared when the promos are cleared, it is a preference that will be used in the next promo calculation */
protected Map<GenericPK, String> desiredAlternateGiftByAction = new HashMap<>();
protected Timestamp cartCreatedTs = UtilDateTime.nowTimestamp();
protected transient Delegator delegator;
protected String delegatorName;
protected String productStoreId;
protected boolean doPromotions = true;
protected String transactionId;
protected String facilityId;
protected String webSiteId;
protected String terminalId;
protected String autoOrderShoppingListId;
/** General partyId for the Order, all other IDs default to this one if not specified explicitly */
protected String orderPartyId;
// sales order parties
protected String placingCustomerPartyId;
protected String billToCustomerPartyId;
protected String shipToCustomerPartyId;
protected String endUserCustomerPartyId;
// purchase order parties
protected String billFromVendorPartyId;
protected String shipFromVendorPartyId;
protected String supplierAgentPartyId;
protected GenericValue userLogin;
protected GenericValue autoUserLogin;
protected Locale locale; // holds the locale from the user session
protected String currencyUom;
protected boolean holdOrder; // = false;
protected Timestamp orderDate;
protected Timestamp cancelBackOrderDate;
// SCIPIO: Cart item subscriptions
/* 2018-11-22: This cache is problematic due to both thread safety and because it risks going out of
* sync with some cart operations, so we simply omit it for now... the lookups will rely on entity cache instead.
* NOTE: ShoppingCart cannot be treated like OrderReadHelper (global vs local).
* DEV NOTE: If a transient cache is ever really needed on ShoppingCart, this should go on
* ShoppingCartItem instead as a List<GenericValue> field.
protected Map<String, List<GenericValue>> cartSubscriptionItems;
*/
protected boolean allowMissingShipEstimates; // = false; // SCIPIO: see WebShoppingCart for implementation
// SCIPIO:
protected List<SolicitationTypeEnum> allowedSolicitations = UtilMisc.newList();
/** don't allow empty constructor */
protected ShoppingCart() {}
/** Creates a new cloned ShoppingCart Object.
* SCIPIO: NOTE: This is legacy copy constructor with exactCopy==false. */
public ShoppingCart(ShoppingCart cart) {
this(cart, false);
}
/** Creates a new cloned ShoppingCart Object.
* SCIPIO: Added exactCopy flag, toggles between legacy (partial) and full/exact cloning the whole cart. */
public ShoppingCart(ShoppingCart cart, boolean exactCopy) {
if (exactCopy) {
// Exact/full instance copy (SCIPIO)
this.delegator = cart.delegator;
this.delegatorName = cart.delegatorName;
this.productStoreId = cart.productStoreId;
this.doPromotions = cart.doPromotions;
this.poNumber = cart.poNumber;
this.orderId = cart.orderId;
this.orderName = cart.orderName;
this.workEffortId = cart.workEffortId;
this.firstAttemptOrderId = cart.firstAttemptOrderId;
this.billingAccountId = cart.billingAccountId;
this.agreementId = cart.agreementId;
this.quoteId = cart.quoteId;
this.orderAdditionalEmails = cart.orderAdditionalEmails;
// SCIPIO: Replace it
//this.adjustments.addAll(cart.getAdjustments());
this.adjustments = new ArrayList<>(cart.adjustments);
this.contactMechIdsMap = new HashMap<>(cart.contactMechIdsMap);
this.freeShippingProductPromoActions = new ArrayList<>(cart.freeShippingProductPromoActions);
this.desiredAlternateGiftByAction = cart.getAllDesiredAlternateGiftByActionCopy();
// clone the groups
this.itemGroupByNumberMap = copyItemGroupByNumberMap(exactCopy, cart.itemGroupByNumberMap);
// clone the items
Map<ShoppingCartItem, ShoppingCartItem> oldToNewItemMap = new HashMap<>();
List<ShoppingCartItem> cartLines = new ArrayList<>(); // SCIPIO: Use local var
for (ShoppingCartItem item : cart.items()) {
ShoppingCartItem newItem = item.copy(exactCopy, itemGroupByNumberMap);
cartLines.add(newItem);
oldToNewItemMap.put(item, newItem);
}
this.cartLines = cartLines;
// SCIPIO: Replace it
//this.productPromoUseInfoList.addAll(cart.productPromoUseInfoList);
List<ProductPromoUseInfo> productPromoUseInfoList = new ArrayList<>(cart.productPromoUseInfoList.size());
for(ProductPromoUseInfo ppui : cart.productPromoUseInfoList) {
productPromoUseInfoList.add(ppui.copy(exactCopy, oldToNewItemMap));
}
this.productPromoUseInfoList = productPromoUseInfoList;
this.productPromoCodes = new HashSet<>(cart.productPromoCodes);
this.locale = cart.locale;
this.currencyUom = cart.currencyUom;
this.externalId = cart.externalId;
this.internalCode = cart.internalCode;
this.viewCartOnAdd = cart.viewCartOnAdd;
this.defaultShipAfterDate = cart.defaultShipAfterDate;
this.defaultShipBeforeDate = cart.defaultShipBeforeDate;
this.cancelBackOrderDate = cart.cancelBackOrderDate;
this.terminalId = cart.terminalId;
this.transactionId = cart.transactionId;
this.autoOrderShoppingListId = cart.autoOrderShoppingListId;
// SCIPIO: Stock fields not covered by legacy copy constructor
this.orderType = cart.orderType;
this.channel = cart.channel;
this.orderStatusId = cart.orderStatusId;
this.orderStatusString = cart.orderStatusString;
this.billingAccountAmt = cart.billingAccountAmt;
this.nextItemSeq = cart.nextItemSeq;
this.defaultItemDeliveryDate = cart.defaultItemDeliveryDate;
this.defaultItemComment = cart.defaultItemComment;
this.readOnlyCart = cart.readOnlyCart;
this.lastListRestore = cart.lastListRestore;
this.autoSaveListId = cart.autoSaveListId;
this.autoSaveListAuthToken = cart.autoSaveListAuthToken;
this.autoSaveListParams = cart.autoSaveListParams;
this.orderTermSet = cart.orderTermSet;
this.orderTerms = new ArrayList<>(cart.orderTerms);
this.nextGroupNumber = cart.nextGroupNumber;
List<CartPaymentInfo> paymentInfo = new ArrayList<>();
for(CartPaymentInfo cpi : cart.paymentInfo) {
paymentInfo.add(cpi.copy(exactCopy));
}
this.paymentInfo = paymentInfo;
List<CartShipInfo> shipInfo = new ArrayList<>();
for(CartShipInfo csi : cart.shipInfo) {
shipInfo.add(csi.copy(exactCopy, oldToNewItemMap));
}
this.shipInfo = shipInfo;
this.orderAttributes = new HashMap<>(cart.orderAttributes);
this.attributes = new HashMap<>(cart.attributes);
this.internalOrderNotes = new ArrayList<>(cart.internalOrderNotes);
this.orderNotes = new ArrayList<>(cart.orderNotes);
this.cartCreatedTs = cart.cartCreatedTs;
this.orderPartyId = cart.orderPartyId;
this.placingCustomerPartyId = cart.placingCustomerPartyId;
this.billToCustomerPartyId = cart.billToCustomerPartyId;
this.shipToCustomerPartyId = cart.shipToCustomerPartyId;
this.endUserCustomerPartyId = cart.endUserCustomerPartyId;
this.billFromVendorPartyId = cart.billFromVendorPartyId;
this.shipFromVendorPartyId = cart.shipFromVendorPartyId;
this.supplierAgentPartyId = cart.supplierAgentPartyId;
this.userLogin = cart.userLogin;
this.autoUserLogin = cart.autoUserLogin;
this.holdOrder = cart.holdOrder;
this.orderDate = cart.orderDate;
// SCIPIO: new fields
/* 2018-11-22: Removed: cartSubscriptionItems cache is counter-productive
// SCIPIO
Map<String, List<GenericValue>> cartSubscriptionItems = null;
if (cart.cartSubscriptionItems != null) {
cartSubscriptionItems = new HashMap<>();
for(Map.Entry<String, List<GenericValue>> entry : cart.cartSubscriptionItems.entrySet()) {
cartSubscriptionItems.put(entry.getKey(), (entry.getValue() != null) ? new ArrayList<>(entry.getValue()) : null);
}
}
this.cartSubscriptionItems = cartSubscriptionItems;
*/
this.allowedSolicitations = cart.allowedSolicitations;
} else {
// Partial/high-level instance copy (legacy)
this.delegator = cart.getDelegator();
this.delegatorName = delegator.getDelegatorName();
this.productStoreId = cart.getProductStoreId();
this.doPromotions = cart.getDoPromotions();
this.poNumber = cart.getPoNumber();
this.orderId = cart.getOrderId();
this.orderName = "Copy of " + cart.getOrderName();
this.workEffortId = cart.getWorkEffortId();
this.firstAttemptOrderId = cart.getFirstAttemptOrderId();
this.billingAccountId = cart.getBillingAccountId();
this.agreementId = cart.getAgreementId();
this.quoteId = cart.getQuoteId();
this.orderAdditionalEmails = cart.getOrderAdditionalEmails();
// SCIPIO: Replace it
//this.adjustments.addAll(cart.getAdjustments());
this.adjustments = new ArrayList<>(cart.getAdjustments());
this.contactMechIdsMap = new HashMap<>(cart.getOrderContactMechIds());
this.freeShippingProductPromoActions = new ArrayList<>(cart.getFreeShippingProductPromoActions());
this.desiredAlternateGiftByAction = cart.getAllDesiredAlternateGiftByActionCopy();
// SCIPIO: Replace it
//this.productPromoUseInfoList.addAll(cart.productPromoUseInfoList);
this.productPromoUseInfoList = new ArrayList<>(cart.productPromoUseInfoList);
this.productPromoCodes = new HashSet<>(cart.productPromoCodes);
this.locale = cart.getLocale();
this.currencyUom = cart.getCurrency();
this.externalId = cart.getExternalId();
this.internalCode = cart.getInternalCode();
this.viewCartOnAdd = cart.viewCartOnAdd();
this.defaultShipAfterDate = cart.getDefaultShipAfterDate();
this.defaultShipBeforeDate = cart.getDefaultShipBeforeDate();
this.cancelBackOrderDate = cart.getCancelBackOrderDate();
this.terminalId = cart.getTerminalId();
this.transactionId = cart.getTransactionId();
this.autoOrderShoppingListId = cart.getAutoOrderShoppingListId();
// clone the groups
/* SCIPIO: This is wrong; there is nothing guarantees parents will be iterated before children in a HashMap
for (ShoppingCartItemGroup itemGroup : cart.itemGroupByNumberMap.values()) {
// get the new parent group by number from the existing set; as before the parent must come before all children to work...
ShoppingCartItemGroup parentGroup = null;
if (itemGroup.getParentGroup() != null) {
parentGroup = this.getItemGroupByNumber(itemGroup.getParentGroup().getGroupNumber());
}
ShoppingCartItemGroup newGroup = new ShoppingCartItemGroup(itemGroup, parentGroup);
itemGroupByNumberMap.put(newGroup.getGroupNumber(), newGroup);
}
*/
this.itemGroupByNumberMap = copyItemGroupByNumberMap(false, cart.itemGroupByNumberMap);
// clone the items
List<ShoppingCartItem> cartLines = new ArrayList<>(); // SCIPIO: Use local var
for (ShoppingCartItem item : cart.items()) {
cartLines.add(item.copy(exactCopy));
}
this.cartLines = cartLines;
this.allowedSolicitations = cart.allowedSolicitations;
}
// clone the additionalPartyRoleMap
// SCIPIO: Use local var
//this.additionalPartyRole = new HashMap<>();
Map<String, List<String>> additionalPartyRole = new HashMap<>();
for (Map.Entry<String, List<String>> me : cart.additionalPartyRole.entrySet()) {
additionalPartyRole.put(me.getKey(), new ArrayList<>(me.getValue()));
}
this.additionalPartyRole = additionalPartyRole;
this.facilityId = cart.facilityId;
this.webSiteId = cart.webSiteId;
this.allowMissingShipEstimates = cart.allowMissingShipEstimates; // SCIPIO
}
/**
* SCIPIO: Deep copy of itemGroupByNumberMap (fixes flawed stock logic) with parent references resolved.
*/
private Map<String, ShoppingCartItemGroup> copyItemGroupByNumberMap(boolean exactCopy, Map<String, ShoppingCartItemGroup> itemGroupByNumberMap) {
Map<String, ShoppingCartItemGroup> newMap = new HashMap<>(); // SCIPIO: Use local var
for (ShoppingCartItemGroup itemGroup : itemGroupByNumberMap.values()) {
if (!newMap.containsKey(itemGroup.getGroupNumber())) { // Parent may be already created
copyItemGroupParentsFirst(itemGroup, exactCopy, newMap);
}
}
return newMap;
}
/**
* SCIPIO: Recursively registers the group, depth-first starting with parent.
*/
private ShoppingCartItemGroup copyItemGroupParentsFirst(ShoppingCartItemGroup itemGroup, boolean exactCopy, Map<String, ShoppingCartItemGroup> newMap) {
ShoppingCartItemGroup newParentGroup = null;
if (itemGroup.getParentGroup() != null) {
newParentGroup = newMap.get(itemGroup.getParentGroup().getGroupNumber());
if (newParentGroup == null) {
newParentGroup = copyItemGroupParentsFirst(itemGroup.getParentGroup(), exactCopy, newMap);
}
}
ShoppingCartItemGroup newGroup = itemGroup.copy(exactCopy, newParentGroup);
newMap.put(newGroup.getGroupNumber(), newGroup);
return newGroup;
}
/** Creates new empty ShoppingCart object. */
public ShoppingCart(Delegator delegator, String productStoreId, String webSiteId, Locale locale, String currencyUom, String billToCustomerPartyId, String billFromVendorPartyId) {
this.delegator = delegator;
this.delegatorName = delegator.getDelegatorName();
this.productStoreId = productStoreId;
this.webSiteId = webSiteId;
this.locale = (locale != null) ? locale : Locale.getDefault();
this.currencyUom = (currencyUom != null) ? currencyUom : EntityUtilProperties.getPropertyValue("general", "currency.uom.id.default", "USD", delegator);
this.billToCustomerPartyId = billToCustomerPartyId;
this.billFromVendorPartyId = billFromVendorPartyId;
if (productStoreId != null) {
// set the default view cart on add for this store
GenericValue productStore = ProductStoreWorker.getProductStore(productStoreId, delegator);
if (productStore == null) {
throw new IllegalArgumentException("Unable to locate ProductStore by ID [" + productStoreId + "]");
}
String storeViewCartOnAdd = productStore.getString("viewCartOnAdd");
if (storeViewCartOnAdd != null && "Y".equalsIgnoreCase(storeViewCartOnAdd)) {
this.viewCartOnAdd = true;
}
if (billFromVendorPartyId == null) {
// since default cart is of type SALES_ORDER, set to store's payToPartyId
this.billFromVendorPartyId = productStore.getString("payToPartyId");
}
this.facilityId = productStore.getString("inventoryFacilityId");
}
}
/** Creates new empty ShoppingCart object. */
public ShoppingCart(Delegator delegator, String productStoreId, String webSiteId, Locale locale, String currencyUom) {
this(delegator, productStoreId, webSiteId, locale, currencyUom, null, null);
}
/** Creates a new empty ShoppingCart object. */
public ShoppingCart(Delegator delegator, String productStoreId, Locale locale, String currencyUom) {
this(delegator, productStoreId, null, locale, currencyUom);
}
/** SCIPIO: Performs a copy of the instance. Added 2019-09-05. */
public ShoppingCart copy(boolean exactCopy) {
return new ShoppingCart(this, exactCopy);
}
/** SCIPIO: Performs an exact, deep copy of the cart. Added 2018-11-16.
* @deprecated 2019-09-05: use {@link #copy(boolean)} instead.
*/
@Deprecated
public ShoppingCart exactCopy() {
return copy(true);
}
/**
* SCIPIO: Tests to ensure the cart is an exact copy of the other; used to verify {@link #copy}.
* NOTE: This is NOT the same as a logical Object equals override! This is mainly for testing.
*/
protected void ensureExactEquals(ShoppingCart other, List<String> errorMessages) {
ShoppingCart.ensureExactEquals(this.orderType, other.orderType, "ShoppingCart.orderType", errorMessages);
ShoppingCart.ensureExactEquals(this.channel, other.channel, "ShoppingCart.channel", errorMessages);
ShoppingCart.ensureExactEquals(this.poNumber, other.poNumber, "ShoppingCart.poNumber", errorMessages);
ShoppingCart.ensureExactEquals(this.orderId, other.orderId, "ShoppingCart.orderId", errorMessages);
ShoppingCart.ensureExactEquals(this.orderName, other.orderName, "ShoppingCart.orderName", errorMessages);
ShoppingCart.ensureExactEquals(this.orderStatusId, other.orderStatusId, "ShoppingCart.orderStatusId", errorMessages);
ShoppingCart.ensureExactEquals(this.orderStatusString, other.orderStatusString, "ShoppingCart.orderStatusString", errorMessages);
ShoppingCart.ensureExactEquals(this.firstAttemptOrderId, other.firstAttemptOrderId, "ShoppingCart.firstAttemptOrderId", errorMessages);
ShoppingCart.ensureExactEquals(this.externalId, other.externalId, "ShoppingCart.externalId", errorMessages);
ShoppingCart.ensureExactEquals(this.internalCode, other.internalCode, "ShoppingCart.internalCode", errorMessages);
ShoppingCart.ensureExactEquals(this.billingAccountId, other.billingAccountId, "ShoppingCart.billingAccountId", errorMessages);
ShoppingCart.ensureExactEquals(this.billingAccountAmt, other.billingAccountAmt, "ShoppingCart.billingAccountAmt", errorMessages);
ShoppingCart.ensureExactEquals(this.agreementId, other.agreementId, "ShoppingCart.agreementId", errorMessages);
ShoppingCart.ensureExactEquals(this.quoteId, other.quoteId, "ShoppingCart.quoteId", errorMessages);
ShoppingCart.ensureExactEquals(this.workEffortId, other.workEffortId, "ShoppingCart.workEffortId", errorMessages);
ShoppingCart.ensureExactEquals(this.nextItemSeq, other.nextItemSeq, "ShoppingCart.nextItemSeq", errorMessages);
ShoppingCart.ensureExactEquals(this.defaultItemDeliveryDate, other.defaultItemDeliveryDate, "ShoppingCart.defaultItemDeliveryDate", errorMessages);
ShoppingCart.ensureExactEquals(this.defaultItemComment, other.defaultItemComment, "ShoppingCart.defaultItemComment", errorMessages);
ShoppingCart.ensureExactEquals(this.orderAdditionalEmails, other.orderAdditionalEmails, "ShoppingCart.orderAdditionalEmails", errorMessages);
ShoppingCart.ensureExactEquals(this.viewCartOnAdd, other.viewCartOnAdd, "ShoppingCart.viewCartOnAdd", errorMessages);
ShoppingCart.ensureExactEquals(this.readOnlyCart, other.readOnlyCart, "ShoppingCart.readOnlyCart", errorMessages);
ShoppingCart.ensureExactEquals(this.lastListRestore, other.lastListRestore, "ShoppingCart.lastListRestore", errorMessages);
ShoppingCart.ensureExactEquals(this.autoSaveListId, other.autoSaveListId, "ShoppingCart.autoSaveListId", errorMessages);
ShoppingCart.ensureExactEquals(this.autoSaveListAuthToken, other.autoSaveListAuthToken, "ShoppingCart.autoSaveListAuthToken", errorMessages);
ShoppingCart.ensureExactEquals(this.autoSaveListParams, other.autoSaveListParams, "ShoppingCart.autoSaveListParams", errorMessages);
ShoppingCart.ensureExactEquals(this.adjustments, other.adjustments, "ShoppingCart.adjustments", errorMessages);
ShoppingCart.ensureExactEquals(this.orderTermSet, other.orderTermSet, "ShoppingCart.orderTermSet", errorMessages);
ShoppingCart.ensureExactEquals(this.orderTerms, other.orderTerms, "ShoppingCart.orderTerms", errorMessages);
ShoppingCart.ensureExactEquals(this.cartLines, other.cartLines, "ShoppingCart.cartLines", errorMessages);
ShoppingCart.ensureExactEquals(this.itemGroupByNumberMap, other.itemGroupByNumberMap, "ShoppingCart.itemGroupByNumberMap", errorMessages);
ShoppingCart.ensureExactEquals(this.nextGroupNumber, other.nextGroupNumber, "ShoppingCart.nextGroupNumber", errorMessages);
ShoppingCart.ensureExactEquals(this.paymentInfo, other.paymentInfo, "ShoppingCart.paymentInfo", errorMessages);
ShoppingCart.ensureExactEquals(this.shipInfo, other.shipInfo, "ShoppingCart.shipInfo", errorMessages);
ShoppingCart.ensureExactEquals(this.contactMechIdsMap, other.contactMechIdsMap, "ShoppingCart.contactMechIdsMap", errorMessages);
ShoppingCart.ensureExactEquals(this.orderAttributes, other.orderAttributes, "ShoppingCart.orderAttributes", errorMessages);
ShoppingCart.ensureExactEquals(this.attributes, other.attributes, "ShoppingCart.attributes", errorMessages);
ShoppingCart.ensureExactEquals(this.internalOrderNotes, other.internalOrderNotes, "ShoppingCart.internalOrderNotes", errorMessages);
ShoppingCart.ensureExactEquals(this.orderNotes, other.orderNotes, "ShoppingCart.orderNotes", errorMessages);
ShoppingCart.ensureExactEquals(this.additionalPartyRole, other.additionalPartyRole, "ShoppingCart.additionalPartyRole", errorMessages);
ShoppingCart.ensureExactEquals(this.defaultShipAfterDate, other.defaultShipAfterDate, "ShoppingCart.defaultShipAfterDate", errorMessages);
ShoppingCart.ensureExactEquals(this.defaultShipBeforeDate, other.defaultShipBeforeDate, "ShoppingCart.defaultShipBeforeDate", errorMessages);
ShoppingCart.ensureExactEquals(this.productPromoUseInfoList, other.productPromoUseInfoList, "ShoppingCart.productPromoUseInfoList", errorMessages);
ShoppingCart.ensureExactEquals(this.productPromoCodes, other.productPromoCodes, "ShoppingCart.productPromoCodes", errorMessages);
ShoppingCart.ensureExactEquals(this.freeShippingProductPromoActions, other.freeShippingProductPromoActions, "ShoppingCart.freeShippingProductPromoActions", errorMessages);
ShoppingCart.ensureExactEquals(this.desiredAlternateGiftByAction, other.desiredAlternateGiftByAction, "ShoppingCart.desiredAlternateGiftByAction", errorMessages);
ShoppingCart.ensureExactEquals(this.cartCreatedTs, other.cartCreatedTs, "ShoppingCart.cartCreatedTs", errorMessages);
// transient
//ShoppingCart.ensureExactEquals(this.delegator, other.delegator, "ShoppingCart.delegator", errorMessages);
ShoppingCart.ensureExactEquals(this.delegatorName, other.delegatorName, "ShoppingCart.delegatorName", errorMessages);
ShoppingCart.ensureExactEquals(this.productStoreId, other.productStoreId, "ShoppingCart.productStoreId", errorMessages);
ShoppingCart.ensureExactEquals(this.doPromotions, other.doPromotions, "ShoppingCart.doPromotions", errorMessages);
ShoppingCart.ensureExactEquals(this.transactionId, other.transactionId, "ShoppingCart.transactionId", errorMessages);
ShoppingCart.ensureExactEquals(this.facilityId, other.facilityId, "ShoppingCart.facilityId", errorMessages);
ShoppingCart.ensureExactEquals(this.webSiteId, other.webSiteId, "ShoppingCart.webSiteId", errorMessages);
ShoppingCart.ensureExactEquals(this.terminalId, other.terminalId, "ShoppingCart.terminalId", errorMessages);
ShoppingCart.ensureExactEquals(this.autoOrderShoppingListId, other.autoOrderShoppingListId, "ShoppingCart.autoOrderShoppingListId", errorMessages);
ShoppingCart.ensureExactEquals(this.orderPartyId, other.orderPartyId, "ShoppingCart.orderPartyId", errorMessages);
ShoppingCart.ensureExactEquals(this.placingCustomerPartyId, other.placingCustomerPartyId, "ShoppingCart.placingCustomerPartyId", errorMessages);
ShoppingCart.ensureExactEquals(this.billToCustomerPartyId, other.billToCustomerPartyId, "ShoppingCart.billToCustomerPartyId", errorMessages);
ShoppingCart.ensureExactEquals(this.shipToCustomerPartyId, other.shipToCustomerPartyId, "ShoppingCart.shipToCustomerPartyId", errorMessages);
ShoppingCart.ensureExactEquals(this.endUserCustomerPartyId, other.endUserCustomerPartyId, "ShoppingCart.endUserCustomerPartyId", errorMessages);
ShoppingCart.ensureExactEquals(this.billFromVendorPartyId, other.billFromVendorPartyId, "ShoppingCart.billFromVendorPartyId", errorMessages);
ShoppingCart.ensureExactEquals(this.shipFromVendorPartyId, other.shipFromVendorPartyId, "ShoppingCart.shipFromVendorPartyId", errorMessages);
ShoppingCart.ensureExactEquals(this.supplierAgentPartyId, other.supplierAgentPartyId, "ShoppingCart.supplierAgentPartyId", errorMessages);
ShoppingCart.ensureExactEquals(this.userLogin, other.userLogin, "ShoppingCart.userLogin", errorMessages);
ShoppingCart.ensureExactEquals(this.autoUserLogin, other.autoUserLogin, "ShoppingCart.autoUserLogin", errorMessages);
ShoppingCart.ensureExactEquals(this.locale, other.locale, "ShoppingCart.locale", errorMessages);
ShoppingCart.ensureExactEquals(this.currencyUom, other.currencyUom, "ShoppingCart.currencyUom", errorMessages);
ShoppingCart.ensureExactEquals(this.holdOrder, other.holdOrder, "ShoppingCart.holdOrder", errorMessages);
ShoppingCart.ensureExactEquals(this.orderDate, other.orderDate, "ShoppingCart.orderDate", errorMessages);
ShoppingCart.ensureExactEquals(this.cancelBackOrderDate, other.cancelBackOrderDate, "ShoppingCart.cancelBackOrderDate", errorMessages);
ShoppingCart.ensureExactEquals(this.allowMissingShipEstimates, other.allowMissingShipEstimates, "ShoppingCart.allowMissingShipEstimates", errorMessages);
}
protected static void ensureExactEquals(Object first, Object second, String fieldName, List<String> errorMessages) { // WARN: subject to change without notice
if (first == null) {
if (second != null) {
errorMessages.add("values not equal for " + fieldName + ": " + first + ", " + second);
return;
} else {
return;
}
} else if (second == null) {
errorMessages.add("values not equal for " + fieldName + ": " + first + ", " + second);
return;
}
if (!first.getClass().equals(second.getClass())) {
errorMessages.add("values not equal for " + fieldName + ": " + first + " (" + first.getClass() + "), "
+ second + " (" + second.getClass() + ")");
return;
}
if (first instanceof ShoppingCartItem) {
((ShoppingCartItem) first).ensureExactEquals((ShoppingCartItem) second, errorMessages);
} else if (first instanceof ShoppingCartItemGroup) {
((ShoppingCartItemGroup) first).ensureExactEquals((ShoppingCartItemGroup) second, errorMessages);
} else if (first instanceof CartPaymentInfo) {
((CartPaymentInfo) first).ensureExactEquals((CartPaymentInfo) second, errorMessages);
} else if (first instanceof CartShipInfo) {
((CartShipInfo) first).ensureExactEquals((CartShipInfo) second, errorMessages);
} else if (first instanceof CartShipInfo.CartShipItemInfo) {
((CartShipInfo.CartShipItemInfo) first).ensureExactEquals((CartShipInfo.CartShipItemInfo) second, errorMessages);
} else if (first instanceof ProductPromoUseInfo) {
((ProductPromoUseInfo) first).ensureExactEquals((ProductPromoUseInfo) second, errorMessages);
} else if (first instanceof ProductConfigWrapper) {
((ProductConfigWrapper) first).ensureExactEquals((ProductConfigWrapper) second, errorMessages);
} else if (first instanceof GenericValue) {
if (!first.equals(second)) {
errorMessages.add("GenericValues not equal for " + fieldName + ": " + first + ", " + second);
}
} else if (first instanceof Map) {
Map<?, ?> firstMap = (Map<?, ?>) first;
Map<?, ?> secondMap = (Map<?, ?>) second;
if (firstMap.size() != secondMap.size()) {
errorMessages.add("Maps not equal for " + fieldName + ": " + first + ", " + second);
return;
}
boolean comparable = true;
for(Map.Entry<?, ?> entry : firstMap.entrySet()) {
if (entry.getKey() instanceof ShoppingCartItem) {
// FIXME: can't verify this type of key because ShoppingCartItem is missing formal equals/hashcode
comparable = false;
continue;
}
ensureExactEquals(entry.getValue(), secondMap.get(entry.getKey()), fieldName, errorMessages);
}
if (comparable) {
if (!first.equals(second)) { // WARN: This may break on new fields
errorMessages.add("Maps not equal for " + fieldName + ": " + first + ", " + second);
}
}
} else if (first instanceof List) {
List<?> firstList = (List<?>) first;
List<?> secondList = (List<?>) second;
if (firstList.size() != secondList.size()) {
errorMessages.add("Lists not equal for " + fieldName + ": " + first + ", " + second);
return;
}
for(int i=0; i<firstList.size(); i++) {
ensureExactEquals(firstList.get(i), secondList.get(i), fieldName, errorMessages);
}
} else if (first.getClass().isArray()) {
if (!Arrays.equals((Object[]) first, (Object[]) second)) {
errorMessages.add("Arrays not equal for " + fieldName + ": " + first + ", " + second);
}
} else {
if (!first.equals(second)) {
errorMessages.add("Values not equal for " + fieldName + ": " + first + ", " + second);
}
}
}
public Delegator getDelegator() {
if (delegator == null) {
delegator = DelegatorFactory.getDelegator(delegatorName);
}
return delegator;
}
public String getProductStoreId() {
return this.productStoreId;
}
public boolean getDoPromotions() {
return this.doPromotions;
}
public void setDoPromotions(boolean doPromotions) {
this.doPromotions = doPromotions;
}
/**
* This is somewhat of a dangerous method, changing the productStoreId changes a lot of stuff including:
* - some items in the cart may not be valid in any catalog in the new store
* - promotions need to be recalculated for the products that remain
* - what else? lots of settings on the ProductStore...
*
* So for now this can only be called if the cart is empty... otherwise it wil throw an exception
*
*/
public void setProductStoreId(String productStoreId) {
if ((productStoreId == null && this.productStoreId == null) || (productStoreId != null && productStoreId.equals(this.productStoreId))) {
return;
}
if (this.size() == 0) {
this.productStoreId = productStoreId;
} else {
throw new IllegalArgumentException("Cannot set productStoreId when the cart is not empty; cart size is " + this.size());
}
}
public String getTransactionId() {
return this.transactionId;
}
public void setTransactionId(String transactionId) {
this.transactionId = transactionId;
}
public String getTerminalId() {
return this.terminalId;
}
public void setTerminalId(String terminalId) {
this.terminalId = terminalId;
}
public String getAutoOrderShoppingListId() {
return this.autoOrderShoppingListId;
}
public void setAutoOrderShoppingListId(String autoOrderShoppingListId) {
this.autoOrderShoppingListId = autoOrderShoppingListId;
}
public String getFacilityId() {
return this.facilityId;
}
public void setFacilityId(String facilityId) {
this.facilityId = facilityId;
}
public Locale getLocale() {
return locale;
}
public void setLocale(Locale locale) {
this.locale = locale;
for (ShoppingCartItem cartItem : cartLines) {
cartItem.setLocale(locale);
}
}
public void setOrderName(String orderName) {
this.orderName = orderName;
}
public String getOrderName() {
return orderName;
}
public void setWorkEffortId(String workEffortId) {
this.workEffortId = workEffortId;
}
public String getWorkEffortId() {
return workEffortId;
}
public void setAttribute(String name, Object value) {
this.attributes.put(name, value);
}
public void removeAttribute(String name) {
this.attributes.remove(name);
}
@SuppressWarnings("unchecked")
public <T> T getAttribute(String name) {
return (T) this.attributes.get(name);
}
public void removeOrderAttribute(String name) {
this.orderAttributes.remove(name);
}
public void setOrderAttribute(String name, String value) {
this.orderAttributes.put(name, value);
}
public String getOrderAttribute(String name) {
return this.orderAttributes.get(name);
}
public void setHoldOrder(boolean b) {
this.holdOrder = b;
}
public boolean getHoldOrder() {
return this.holdOrder;
}
public void setOrderDate(Timestamp t) {
this.orderDate = t;
}
public Timestamp getOrderDate() {
return this.orderDate;
}
/** Sets the currency for the cart. */
public void setCurrency(LocalDispatcher dispatcher, String currencyUom) throws CartItemModifyException {
if (isReadOnlyCart()) {
throw new CartItemModifyException("Cart items cannot be changed");
}
String previousCurrency = this.currencyUom;
this.currencyUom = currencyUom;
if (!previousCurrency.equals(this.currencyUom)) {
for (ShoppingCartItem item : this) {
item.updatePrice(dispatcher, this);
}
}
}
/** Get the current currency setting. */
public String getCurrency() {
if (this.currencyUom != null) {
return this.currencyUom;
}
// uh oh, not good, should always be passed in on init, we can't really do
// anything without it, so throw an exception
throw new IllegalStateException(
"The Currency UOM is not set in the shopping cart, this is not a valid state, it should always be passed in when the cart is created.");
}
public Timestamp getCartCreatedTime() {
return this.cartCreatedTs;
}
/**
* SCIPIO: reads back the default supplier ID (currently in attributes), or null if none/empty.
*/
public String getSupplierPartyId() {
String supplierPartyId = getAttribute("supplierPartyId");
if (UtilValidate.isEmpty(supplierPartyId)) {
return null;
} else {
return supplierPartyId;
}
}
/**
* SCIPIO: set the default supplier ID (currently in attributes)
*/
public void setSupplierPartyId(String supplierPartyId) {
setAttribute("supplierPartyId", (UtilValidate.isEmpty(supplierPartyId)) ? null : supplierPartyId);
}
public GenericValue getSupplierProduct(String productId, BigDecimal quantity, LocalDispatcher dispatcher) {
GenericValue supplierProduct = null;
Map<String, Object> params = UtilMisc.<String, Object>toMap("productId", productId,
"partyId", this.getSupplierPartyId(), // SCIPIO: this was wrong: this.getPartyId()
"currencyUomId", this.getCurrency(),
"quantity", quantity);
try {
Map<String, Object> result = dispatcher.runSync("getSuppliersForProduct", params);
if (ServiceUtil.isError(result)) {
Debug.logError(ServiceUtil.getErrorMessage(result), module);
return null;
}
List<GenericValue> productSuppliers = UtilGenerics.checkList(result.get("supplierProducts"));
if ((productSuppliers != null) && (productSuppliers.size() > 0)) {
supplierProduct = productSuppliers.get(0);
}
} catch (GenericServiceException e) {
Debug.logWarning(UtilProperties.getMessage(resource_error,"OrderRunServiceGetSuppliersForProductError", Debug.getLogLocale()) + e.getMessage(), module); // SCIPIO: log locale
} catch (Exception e) {
Debug.logWarning(UtilProperties.getMessage(resource_error,"OrderRunServiceGetSuppliersForProductError", Debug.getLogLocale()) + e.getMessage(), module); // SCIPIO: log locale
}
return supplierProduct;
}
// =======================================================================
// Methods for cart items
// =======================================================================
/** Add an item to the shopping cart, or if already there, increase the quantity.
* @return the new/increased item index
* @throws CartItemModifyException
*/
public int addOrIncreaseItem(String productId, BigDecimal selectedAmount, BigDecimal quantity, Timestamp reservStart, BigDecimal reservLength, BigDecimal reservPersons,
Timestamp shipBeforeDate, Timestamp shipAfterDate, Map<String, GenericValue> features, Map<String, Object> attributes, String prodCatalogId,
ProductConfigWrapper configWrapper, String itemType, String itemGroupNumber, String parentProductId, LocalDispatcher dispatcher) throws CartItemModifyException, ItemNotFoundException {
return addOrIncreaseItem(productId,selectedAmount,quantity,reservStart,reservLength,reservPersons, null,null,shipBeforeDate,shipAfterDate,features,attributes,prodCatalogId,
configWrapper,itemType,itemGroupNumber,parentProductId,dispatcher);
}
/** add rental (with accommodation) item to cart */
public int addOrIncreaseItem(String productId, BigDecimal selectedAmount, BigDecimal quantity, Timestamp reservStart, BigDecimal reservLength, BigDecimal reservPersons,
String accommodationMapId, String accommodationSpotId, Timestamp shipBeforeDate, Timestamp shipAfterDate, Map<String, GenericValue> features, Map<String, Object> attributes,
String prodCatalogId, ProductConfigWrapper configWrapper, String itemType, String itemGroupNumber, String parentProductId, LocalDispatcher dispatcher) throws CartItemModifyException, ItemNotFoundException {
return addOrIncreaseItem(productId, selectedAmount, quantity, reservStart, reservLength, reservPersons, accommodationMapId, accommodationSpotId, shipBeforeDate, shipAfterDate, features, attributes, null, prodCatalogId, configWrapper, itemType, itemGroupNumber, parentProductId, dispatcher);
}
/** add rental (with accommodation) item to cart and order item attributes*/
public int addOrIncreaseItem(String productId, BigDecimal selectedAmount, BigDecimal quantity, Timestamp reservStart, BigDecimal reservLength, BigDecimal reservPersons,
String accommodationMapId, String accommodationSpotId, Timestamp shipBeforeDate, Timestamp shipAfterDate, Map<String, GenericValue> features, Map<String,Object> attributes,
Map<String, String> orderItemAttributes, String prodCatalogId, ProductConfigWrapper configWrapper, String itemType, String itemGroupNumber,
String parentProductId, LocalDispatcher dispatcher) throws CartItemModifyException, ItemNotFoundException {
if (isReadOnlyCart()) {
throw new CartItemModifyException("Cart items cannot be changed");
}
selectedAmount = selectedAmount == null ? BigDecimal.ZERO : selectedAmount;
reservLength = reservLength == null ? BigDecimal.ZERO : reservLength;
reservPersons = reservPersons == null ? BigDecimal.ZERO : reservPersons;
ShoppingCart.ShoppingCartItemGroup itemGroup = this.getItemGroupByNumber(itemGroupNumber);
GenericValue supplierProduct = null;
// Check for existing cart item.
for (int i = 0; i < this.cartLines.size(); i++) {
ShoppingCartItem sci = cartLines.get(i);
if (sci.equals(productId, reservStart, reservLength, reservPersons, accommodationMapId, accommodationSpotId, features, attributes, orderItemAttributes, prodCatalogId,selectedAmount, configWrapper, itemType, itemGroup, false)) {
BigDecimal newQuantity = sci.getQuantity().add(quantity);
try {
BigDecimal minQuantity = getMinimumOrderQuantity(getDelegator(),sci.getBasePrice(), productId);
if(newQuantity.compareTo(minQuantity) < 0) {
newQuantity = minQuantity;
}
} catch (GenericEntityException e) {
Debug.logError(e, module);
}
if ("RENTAL_ORDER_ITEM".equals(sci.getItemType())) {
// check to see if the related fixed asset is available for the new quantity
String isAvailable = ShoppingCartItem.checkAvailability(productId, newQuantity, reservStart, reservLength, this);
if (isAvailable.compareTo("OK") != 0) {
Map<String, Object> messageMap = UtilMisc.<String, Object>toMap("productId", productId, "availableMessage", isAvailable);
String excMsg = UtilProperties.getMessage(ShoppingCartItem.resource, "item.product_not_available", messageMap, this.getLocale());
Debug.logInfo(excMsg, module);
throw new CartItemModifyException(isAvailable);
}
}
if (Debug.verboseOn()) {
Debug.logVerbose("Found a match for id " + productId + " on line " + i + ", updating quantity to " + newQuantity, module);
}
sci.setQuantity(newQuantity, dispatcher, this);
if ("PURCHASE_ORDER".equals(getOrderType())) {
supplierProduct = getSupplierProduct(productId, newQuantity, dispatcher);
if (supplierProduct != null && supplierProduct.getBigDecimal("lastPrice") != null) {
sci.setSupplierProductId(supplierProduct.getString("supplierProductId"));
sci.setBasePrice(supplierProduct.getBigDecimal("lastPrice"));
sci.setName(ShoppingCartItem.getPurchaseOrderItemDescription(sci.getProduct(), supplierProduct, this.getLocale()));
} else {
throw new CartItemModifyException("SupplierProduct not found");
}
}
return i;
}
}
// Add the new item to the shopping cart if it wasn't found.
ShoppingCartItem item = null;
if ("PURCHASE_ORDER".equals(getOrderType())) {
supplierProduct = getSupplierProduct(productId, quantity, dispatcher);
if (supplierProduct != null || "_NA_".equals(this.getPartyId())) {
item = ShoppingCartItem.makePurchaseOrderItem(0, productId, selectedAmount, quantity, features, attributes, prodCatalogId, configWrapper, itemType, itemGroup, dispatcher, this, supplierProduct, shipBeforeDate, shipAfterDate, cancelBackOrderDate,
new ShoppingCartItem.ExtraPurchaseOrderInitArgs(orderItemAttributes)); // SCIPIO: 2018-07-17: orderItemAttributes here for early assignment in item
} else {
throw new CartItemModifyException("SupplierProduct not found");
}
} else {
try {
BigDecimal minQuantity = getMinimumOrderQuantity(getDelegator(),null, productId);
if(quantity.compareTo(minQuantity) < 0) {
quantity = minQuantity;
}
} catch (GenericEntityException e) {
Debug.logError(e, module);
}
item = ShoppingCartItem.makeItem(0, productId, selectedAmount, quantity, null,
reservStart, reservLength, reservPersons, accommodationMapId, accommodationSpotId, shipBeforeDate, shipAfterDate,
features, attributes, prodCatalogId, configWrapper, itemType, itemGroup, dispatcher,
this, Boolean.TRUE, Boolean.TRUE, parentProductId, Boolean.FALSE, Boolean.FALSE,
new ShoppingCartItem.ExtraInitArgs(orderItemAttributes)); // SCIPIO: 2018-07-17: orderItemAttributes here for early assignment in item
}
/* SCIPIO: 2018-07-17: This code is now in ShoppingCartItem.setOrderItemAttributes
* and is called by the makeItem and makePurchaseItem methods, earlier in the item creation
// add order item attributes
if (UtilValidate.isNotEmpty(orderItemAttributes)) {
for (Entry<String, String> entry : orderItemAttributes.entrySet()) {
item.setOrderItemAttribute(entry.getKey(), entry.getValue());
}
}
*/
return this.addItem(0, item);
}
/** Add a non-product item to the shopping cart.
* @return the new item index
* @throws CartItemModifyException
*/
public int addNonProductItem(String itemType, String description, String categoryId, BigDecimal price, BigDecimal quantity,
Map<String, Object> attributes, String prodCatalogId, String itemGroupNumber, LocalDispatcher dispatcher) throws CartItemModifyException {
ShoppingCart.ShoppingCartItemGroup itemGroup = this.getItemGroupByNumber(itemGroupNumber);
return this.addItem(0, ShoppingCartItem.makeItem(0, itemType, description, categoryId, price, null, quantity, attributes, prodCatalogId, itemGroup, dispatcher, this, Boolean.TRUE));
}
/** Add an item to the shopping cart. */
public int addItem(int index, ShoppingCartItem item) throws CartItemModifyException {
if (isReadOnlyCart()) {
throw new CartItemModifyException("Cart items cannot be changed");
}
if (!cartLines.contains(item)) {
// If the billing address is already set, verify if the new product
// is available in the address' geo
GenericValue product = item.getProduct();
if (product != null && isSalesOrder()) {
GenericValue billingAddress = this.getBillingAddress();
if (billingAddress != null) {
if (!ProductWorker.isBillableToAddress(product, billingAddress)) {
throw new CartItemModifyException("The billing address is not compatible with ProductGeos rules of this product.");
}
}
}
cartLines.add(index, item);
return index;
}
return this.getItemIndex(item);
}
/** Add an item to the shopping cart. */
public int addItemToEnd(String productId, BigDecimal amount, BigDecimal quantity, BigDecimal unitPrice, HashMap<String, GenericValue> features, HashMap<String, Object> attributes, String prodCatalogId, String itemType, ProductConfigWrapper configWrapper, LocalDispatcher dispatcher, Boolean triggerExternalOps, Boolean triggerPriceRules) throws CartItemModifyException, ItemNotFoundException {
return addItemToEnd(ShoppingCartItem.makeItem(null, productId, amount, quantity, unitPrice, null, null, null, null, null, features, attributes, prodCatalogId, configWrapper, itemType, null, dispatcher, this, triggerExternalOps, triggerPriceRules, null, Boolean.FALSE, Boolean.FALSE));
}
/** Add an item to the shopping cart. */
public int addItemToEnd(String productId, BigDecimal amount, BigDecimal quantity, BigDecimal unitPrice, HashMap<String, GenericValue> features, HashMap<String, Object> attributes, String prodCatalogId, String itemType, LocalDispatcher dispatcher, Boolean triggerExternalOps, Boolean triggerPriceRules) throws CartItemModifyException, ItemNotFoundException {
return addItemToEnd(productId, amount, quantity, unitPrice, features, attributes, prodCatalogId, itemType, dispatcher, triggerExternalOps, triggerPriceRules, Boolean.FALSE, Boolean.FALSE);
}
/** Add an (rental)item to the shopping cart. */
public int addItemToEnd(String productId, BigDecimal amount, BigDecimal quantity, BigDecimal unitPrice, Timestamp reservStart, BigDecimal reservLength, BigDecimal reservPersons, HashMap<String, GenericValue> features, HashMap<String, Object> attributes, String prodCatalogId, String itemType, LocalDispatcher dispatcher, Boolean triggerExternalOps, Boolean triggerPriceRules) throws CartItemModifyException, ItemNotFoundException {
return addItemToEnd(ShoppingCartItem.makeItem(null, productId, amount, quantity, unitPrice, reservStart, reservLength, reservPersons, null, null, features, attributes, prodCatalogId, null, itemType, null, dispatcher, this, triggerExternalOps, triggerPriceRules, null, Boolean.FALSE, Boolean.FALSE));
}
/** Add an (rental)item to the shopping cart. */
public int addItemToEnd(String productId, BigDecimal amount, BigDecimal quantity, BigDecimal unitPrice, Timestamp reservStart, BigDecimal reservLength, BigDecimal reservPersons, HashMap<String, GenericValue> features, HashMap<String, Object> attributes, String prodCatalogId, String itemType, LocalDispatcher dispatcher, Boolean triggerExternalOps, Boolean triggerPriceRules, Boolean skipInventoryChecks, Boolean skipProductChecks) throws CartItemModifyException, ItemNotFoundException {
return addItemToEnd(ShoppingCartItem.makeItem(null, productId, amount, quantity, unitPrice, reservStart, reservLength, reservPersons, null, null, features, attributes, prodCatalogId, null, itemType, null, dispatcher, this, triggerExternalOps, triggerPriceRules, null, skipInventoryChecks, skipProductChecks));
}
/** Add an (rental/aggregated)item to the shopping cart. */
public int addItemToEnd(String productId, BigDecimal amount, BigDecimal quantity, BigDecimal unitPrice, Timestamp reservStart, BigDecimal reservLength, BigDecimal reservPersons, HashMap<String, GenericValue> features, HashMap<String, Object> attributes, String prodCatalogId, ProductConfigWrapper configWrapper, String itemType, LocalDispatcher dispatcher, Boolean triggerExternalOps, Boolean triggerPriceRules, Boolean skipInventoryChecks, Boolean skipProductChecks) throws CartItemModifyException, ItemNotFoundException {
return addItemToEnd(ShoppingCartItem.makeItem(null, productId, amount, quantity, unitPrice, reservStart, reservLength, reservPersons, null, null, features, attributes, prodCatalogId, configWrapper, itemType, null, dispatcher, this, triggerExternalOps, triggerPriceRules, null, skipInventoryChecks, skipProductChecks));
}
/** Add an accommodation(rental)item to the shopping cart. */
public int addItemToEnd(String productId, BigDecimal amount, BigDecimal quantity, BigDecimal unitPrice, Timestamp reservStart, BigDecimal reservLength, BigDecimal reservPersons, String accommodationMapId, String accommodationSpotId, HashMap<String, GenericValue> features, HashMap<String, Object> attributes, String prodCatalogId, String itemType, LocalDispatcher dispatcher, Boolean triggerExternalOps, Boolean triggerPriceRules) throws CartItemModifyException, ItemNotFoundException {
return addItemToEnd(ShoppingCartItem.makeItem(null, productId, amount, quantity, unitPrice, reservStart, reservLength, reservPersons, accommodationMapId, accommodationSpotId, null, null, features, attributes, prodCatalogId, null, itemType, null, dispatcher, this, triggerExternalOps, triggerPriceRules, null, Boolean.FALSE, Boolean.FALSE));
}
/** Add an accommodation(rental)item to the shopping cart. */
public int addItemToEnd(String productId, BigDecimal amount, BigDecimal quantity, BigDecimal unitPrice, Timestamp reservStart, BigDecimal reservLength, BigDecimal reservPersons, String accommodationMapId, String accommodationSpotId, HashMap<String, GenericValue> features, HashMap<String, Object> attributes, String prodCatalogId, String itemType, LocalDispatcher dispatcher, Boolean triggerExternalOps, Boolean triggerPriceRules, Boolean skipInventoryChecks, Boolean skipProductChecks) throws CartItemModifyException, ItemNotFoundException {
return addItemToEnd(ShoppingCartItem.makeItem(null, productId, amount, quantity, unitPrice, reservStart, reservLength, reservPersons, accommodationMapId, accommodationSpotId, null, null, features, attributes, prodCatalogId, null, itemType, null, dispatcher, this, triggerExternalOps, triggerPriceRules, null, skipInventoryChecks, skipProductChecks));
}
/** Add an accommodation(rental/aggregated)item to the shopping cart. */
public int addItemToEnd(String productId, BigDecimal amount, BigDecimal quantity, BigDecimal unitPrice, Timestamp reservStart, BigDecimal reservLength, BigDecimal reservPersonsDbl,String accommodationMapId, String accommodationSpotId, HashMap<String, GenericValue> features, HashMap<String, Object> attributes, String prodCatalogId, ProductConfigWrapper configWrapper, String itemType, LocalDispatcher dispatcher, Boolean triggerExternalOps, Boolean triggerPriceRules, Boolean skipInventoryChecks, Boolean skipProductChecks) throws CartItemModifyException, ItemNotFoundException {
return addItemToEnd(ShoppingCartItem.makeItem(null, productId, amount, quantity, unitPrice, reservStart, reservLength, reservPersonsDbl, accommodationMapId, accommodationSpotId, null, null, features, attributes, prodCatalogId, configWrapper, itemType, null, dispatcher, this, triggerExternalOps, triggerPriceRules, null, skipInventoryChecks, skipProductChecks));
}
/** Add an item to the shopping cart. */
public int addItemToEnd(String productId, BigDecimal amount, BigDecimal quantity, BigDecimal unitPrice, HashMap<String, GenericValue> features, HashMap<String, Object> attributes, String prodCatalogId, String itemType, LocalDispatcher dispatcher, Boolean triggerExternalOps, Boolean triggerPriceRules, Boolean skipInventoryChecks, Boolean skipProductChecks) throws CartItemModifyException, ItemNotFoundException {
return addItemToEnd(ShoppingCartItem.makeItem(null, productId, amount, quantity, unitPrice, null, null, null, null, null, features, attributes, prodCatalogId, null, itemType, null, dispatcher, this, triggerExternalOps, triggerPriceRules, null, skipInventoryChecks, skipProductChecks));
}
/** Add an item to the shopping cart. */
public int addItemToEnd(ShoppingCartItem item) throws CartItemModifyException {
return addItem(cartLines.size(), item);
}
/** Get a ShoppingCartItem from the cart object. */
public ShoppingCartItem findCartItem(String productId, Map<String, GenericValue> features, Map<String, Object> attributes, String prodCatalogId, BigDecimal selectedAmount) {
// Check for existing cart item.
for (int i = 0; i < this.cartLines.size(); i++) {
ShoppingCartItem cartItem = cartLines.get(i);
if (cartItem.equals(productId, features, attributes, prodCatalogId, selectedAmount)) {
return cartItem;
}
}
return null;
}
/** Get all ShoppingCartItems from the cart object with the given productId. */
public List<ShoppingCartItem> findAllCartItems(String productId) {
return this.findAllCartItems(productId, null);
}
/** Get all ShoppingCartItems from the cart object with the given productId and optional groupNumber to limit it to a specific item group */
public List<ShoppingCartItem> findAllCartItems(String productId, String groupNumber) {
if (productId == null) {
return this.items();
}
List<ShoppingCartItem> itemsToReturn = new ArrayList<>();
// Check for existing cart item.
for (ShoppingCartItem cartItem : cartLines) {
if (UtilValidate.isNotEmpty(groupNumber) && !cartItem.isInItemGroup(groupNumber)) {
continue;
}
if (productId.equals(cartItem.getProductId())) {
itemsToReturn.add(cartItem);
}
}
return itemsToReturn;
}
/** Get all ShoppingCartItems from the cart object with the given productCategoryId and optional groupNumber to limit it to a specific item group */
public List<ShoppingCartItem> findAllCartItemsInCategory(String productCategoryId, String groupNumber) {
if (productCategoryId == null) {
return this.items();
}
Delegator delegator = this.getDelegator();
List<ShoppingCartItem> itemsToReturn = new ArrayList<>();
try {
// Check for existing cart item
for (ShoppingCartItem cartItem : cartLines) {
if (UtilValidate.isNotEmpty(groupNumber) && !cartItem.isInItemGroup(groupNumber)) {
continue;
}
if (CategoryWorker.isProductInCategory(delegator, cartItem.getProductId(), productCategoryId)) {
itemsToReturn.add(cartItem);
} else {
}
}
} catch (GenericEntityException e) {
Debug.logError(e, "Error getting cart items that are in a category: " + e.toString(), module);
}
return itemsToReturn;
}
/** Remove quantity 0 ShoppingCartItems from the cart object. */
public void removeEmptyCartItems() {
// Check for existing cart item.
for (int i = 0; i < this.cartLines.size();) {
ShoppingCartItem cartItem = cartLines.get(i);
if (cartItem.getQuantity().compareTo(BigDecimal.ZERO) == 0) {
this.clearItemShipInfo(cartItem);
cartLines.remove(i);
} else {
i++;
}
}
}
// =============== some misc utility methods, mostly for dealing with lists of items =================
public void removeExtraItems(List<ShoppingCartItem> multipleItems, LocalDispatcher dispatcher, int maxItems) throws CartItemModifyException {
// if 1 or 0 items, do nothing
if (multipleItems.size() <= maxItems) {
return;
}
// remove all except first <maxItems> in list from the cart, first because new cart items are added to the beginning...
List<ShoppingCartItem> localList = new ArrayList<>();
localList.addAll(multipleItems);
// the ones to keep...
for (int i=0; i<maxItems; i++) {
localList.remove(0);
}
for (ShoppingCartItem item : localList) {
this.removeCartItem(item, dispatcher);
}
}
public static BigDecimal getItemsTotalQuantity(List<ShoppingCartItem> cartItems) {
BigDecimal totalQuantity = BigDecimal.ZERO;
for (ShoppingCartItem item : cartItems) {
totalQuantity = totalQuantity.add(item.getQuantity());
}
return totalQuantity;
}
public static List<GenericValue> getItemsProducts(List<ShoppingCartItem> cartItems) {
List<GenericValue> productList = new ArrayList<>();
for (ShoppingCartItem item : cartItems) {
GenericValue product = item.getProduct();
if (product != null) {
productList.add(product);
}
}
return productList;
}
public void ensureItemsQuantity(List<ShoppingCartItem> cartItems, LocalDispatcher dispatcher, BigDecimal quantity) throws CartItemModifyException {
for (ShoppingCartItem item : cartItems) {
if (item.getQuantity() != quantity) {
item.setQuantity(quantity, dispatcher, this);
}
}
}
public BigDecimal ensureItemsTotalQuantity(List<ShoppingCartItem> cartItems, LocalDispatcher dispatcher, BigDecimal quantity) throws CartItemModifyException {
BigDecimal quantityRemoved = BigDecimal.ZERO;
// go through the items and reduce quantityToKeep by the item quantities until it is 0, then remove the remaining...
BigDecimal quantityToKeep = quantity;
for (ShoppingCartItem item : cartItems) {
if (quantityToKeep.compareTo(item.getQuantity()) >= 0) {
// quantityToKeep sufficient to keep it all... just reduce quantityToKeep and move on
quantityToKeep = quantityToKeep.subtract(item.getQuantity());
} else {
// there is more in this than we want to keep, so reduce the quantity, or remove altogether...
if (quantityToKeep.compareTo(BigDecimal.ZERO) == 0) {
// nothing left to keep, just remove it...
quantityRemoved = quantityRemoved.add(item.getQuantity());
this.removeCartItem(item, dispatcher);
} else {
// there is some to keep, so reduce quantity to quantityToKeep, at this point we know we'll take up all of the rest of the quantityToKeep
quantityRemoved = quantityRemoved.add(item.getQuantity().subtract(quantityToKeep));
item.setQuantity(quantityToKeep, dispatcher, this);
quantityToKeep = BigDecimal.ZERO;
}
}
}
return quantityRemoved;
}
// ============== WorkEffort related methods ===============
public boolean containAnyWorkEffortCartItems() {
// Check for existing cart item.
for (ShoppingCartItem cartItem : this.cartLines) {
if ("RENTAL_ORDER_ITEM".equals(cartItem.getItemType())) { // create workeffort items?
return true;
}
}
return false;
}
public boolean containAllWorkEffortCartItems() {
// Check for existing cart item.
for (ShoppingCartItem cartItem : this.cartLines) {
if (!"RENTAL_ORDER_ITEM".equals(cartItem.getItemType())) { // not a item to create workefforts?
return false;
}
}
return true;
}
/**
* Check to see if the cart contains only Digital Goods, ie no Finished Goods and no Finished/Digital Goods, et cetera.
* This is determined by making sure no Product has a type where ProductType.isPhysical!=N.
*/
public boolean containOnlyDigitalGoods() {
for (ShoppingCartItem cartItem : this.cartLines) {
GenericValue product = cartItem.getProduct();
try {
GenericValue productType = product.getRelatedOne("ProductType", true);
if (productType == null || !"N".equals(productType.getString("isPhysical"))) {
return false;
}
} catch (GenericEntityException e) {
Debug.logError(e, "Error looking up ProductType: " + e.toString(), module);
// consider this not a digital good if we don't have "proof"
return false;
}
}
return true;
}
/**
* Check to see if the ship group contains only Digital Goods, ie no Finished Goods and no Finished/Digital Goods, et cetera.
* This is determined by making sure no Product has a type where ProductType.isPhysical!=N.
*/
public boolean containOnlyDigitalGoods(int shipGroupIdx) {
CartShipInfo shipInfo = getShipInfo(shipGroupIdx);
for (ShoppingCartItem cartItem: shipInfo.getShipItems()) {
GenericValue product = cartItem.getProduct();
try {
GenericValue productType = product.getRelatedOne("ProductType", true);
if (productType == null || !"N".equals(productType.getString("isPhysical"))) {
return false;
}
} catch (GenericEntityException e) {
Debug.logError(e, "Error looking up ProductType: " + e.toString(), module);
// consider this not a digital good if we don't have "proof"
return false;
}
}
return true;
}
/** Returns this item's index. */
public int getItemIndex(ShoppingCartItem item) {
return cartLines.indexOf(item);
}
/** Get a ShoppingCartItem from the cart object. */
public ShoppingCartItem findCartItem(int index) {
if (cartLines.size() <= index) {
return null;
}
return cartLines.get(index);
}
public ShoppingCartItem findCartItem(String orderItemSeqId) {
if (orderItemSeqId != null) {
for (int i = 0; i < this.cartLines.size(); i++) {
ShoppingCartItem cartItem = cartLines.get(i);
String itemSeqId = cartItem.getOrderItemSeqId();
if (itemSeqId != null && orderItemSeqId.equals(itemSeqId)) {
return cartItem;
}
}
}
return null;
}
/**
* SCIPIO: Remove an item from the cart object.
* <p>
* SCIPIO: Modified to support triggerExternalOps bool.
*/
public void removeCartItem(ShoppingCartItem item, boolean triggerExternalOps, LocalDispatcher dispatcher) throws CartItemModifyException {
if (item == null) return;
this.removeCartItem(this.getItemIndex(item), triggerExternalOps, dispatcher);
}
public void removeCartItem(ShoppingCartItem item, LocalDispatcher dispatcher) throws CartItemModifyException {
if (item == null) {
return;
}
this.removeCartItem(this.getItemIndex(item), dispatcher);
}
/**
* Remove an item from the cart object.
* <p>
* SCIPIO: Modified to support triggerExternalOps bool.
*/
public void removeCartItem(int index, boolean triggerExternalOps, LocalDispatcher dispatcher) throws CartItemModifyException {
if (isReadOnlyCart()) {
throw new CartItemModifyException("Cart items cannot be changed");
}
if (index < 0) {
return;
}
if (cartLines.size() <= index) {
return;
}
ShoppingCartItem item;
/* 2018-11-22: Removed: cartSubscriptionItems cache is counter-productive
// SCIPIO: Removing cart item from subscriptions map, if exists
item = cartLines.get(index);
String prodId = item.getProductId();
if (UtilValidate.isNotEmpty(cartSubscriptionItems)
&& UtilValidate.isNotEmpty(prodId)
&& cartSubscriptionItems.containsKey(prodId)) {
cartSubscriptionItems.remove(prodId);
}
*/
// SCIPIO: Prevents setQuantity from working, do after + index
//item = cartLines.remove(index);
item = cartLines.get(index);
if (item == null) {
throw new CartItemModifyException("Invalid cart item index");
}
// set quantity to 0 to trigger necessary events, but skip price calc and inventory checks
item.setQuantity(BigDecimal.ZERO, dispatcher, this, triggerExternalOps, true, false, true);
item = cartLines.remove(index); // SCIPIO
}
/**
* Remove an item from the cart object.
* <p>
* SCIPIO: Implies triggerExternalOps true.
*/
public void removeCartItem(int index, LocalDispatcher dispatcher) throws CartItemModifyException {
removeCartItem(index, true, dispatcher);
}
/** Moves a line item to a different index. */
public void moveCartItem(int fromIndex, int toIndex) {
if (toIndex < fromIndex) {
cartLines.add(toIndex, cartLines.remove(fromIndex));
} else if (toIndex > fromIndex) {
cartLines.add(toIndex - 1, cartLines.remove(fromIndex));
}
}
/** Returns the number of items in the cart object. */
public int size() {
return cartLines.size();
}
/** Returns a Collection of items in the cart object. */
public List<ShoppingCartItem> items() {
List<ShoppingCartItem> result = new ArrayList<>();
result.addAll(cartLines);
return result;
}
/** Returns an iterator of cart items. */
@Override
public Iterator<ShoppingCartItem> iterator() {
return cartLines.iterator();
}
public ShoppingCart.ShoppingCartItemGroup getItemGroupByNumber(String groupNumber) {
if (UtilValidate.isEmpty(groupNumber)) {
return null;
}
return this.itemGroupByNumberMap.get(groupNumber);
}
/** Creates a new Item Group and returns the groupNumber that represents it */
public String addItemGroup(String groupName, String parentGroupNumber) {
ShoppingCart.ShoppingCartItemGroup parentGroup = this.getItemGroupByNumber(parentGroupNumber);
ShoppingCart.ShoppingCartItemGroup newGroup = newItemGroup(this.nextGroupNumber, groupName, parentGroup);
this.nextGroupNumber++;
this.itemGroupByNumberMap.put(newGroup.getGroupNumber(), newGroup);
return newGroup.getGroupNumber();
}
public ShoppingCartItemGroup addItemGroup(GenericValue itemGroupValue) throws GenericEntityException {
if (itemGroupValue == null) {
return null;
}
String itemGroupNumber = itemGroupValue.getString("orderItemGroupSeqId");
ShoppingCartItemGroup itemGroup = this.getItemGroupByNumber(itemGroupNumber);
if (itemGroup == null) {
ShoppingCartItemGroup parentGroup = addItemGroup(itemGroupValue.getRelatedOne("ParentOrderItemGroup", true));
itemGroup = newItemGroup(itemGroupNumber, itemGroupValue.getString("groupName"), parentGroup);
int parsedGroupNumber = Integer.parseInt(itemGroupNumber);
if (parsedGroupNumber > this.nextGroupNumber) {
this.nextGroupNumber = parsedGroupNumber + 1;
}
this.itemGroupByNumberMap.put(itemGroupNumber, itemGroup);
}
return itemGroup;
}
public List<ShoppingCartItem> getCartItemsInNoGroup() {
List<ShoppingCartItem> cartItemList = new ArrayList<>();
for (ShoppingCartItem cartItem : cartLines) {
if (cartItem.getItemGroup() == null) {
cartItemList.add(cartItem);
}
}
return cartItemList;
}
public List<ShoppingCartItem> getCartItemsInGroup(String groupNumber) {
List<ShoppingCartItem> cartItemList = new ArrayList<>();
ShoppingCart.ShoppingCartItemGroup itemGroup = this.getItemGroupByNumber(groupNumber);
if (itemGroup != null) {
for (ShoppingCartItem cartItem : cartLines) {
if (itemGroup.equals(cartItem.getItemGroup())) {
cartItemList.add(cartItem);
}
}
}
return cartItemList;
}
public void deleteItemGroup(String groupNumber) {
ShoppingCartItemGroup itemGroup = this.getItemGroupByNumber(groupNumber);
if (itemGroup != null) {
// go through all cart items and remove from group if they are in it
List<ShoppingCartItem> cartItemList = this.getCartItemsInGroup(groupNumber);
for (ShoppingCartItem cartItem : cartItemList) {
cartItem.setItemGroup(null);
}
// if this is a parent of any set them to this group's parent (or null)
for (ShoppingCartItemGroup otherItemGroup : this.itemGroupByNumberMap.values()) {
if (itemGroup.equals(otherItemGroup.getParentGroup())) {
otherItemGroup.inheritParentsParent();
}
}
// finally, remove the itemGroup...
this.itemGroupByNumberMap.remove(groupNumber);
}
}
//=======================================================
// Other General Info Maintenance Methods
//=======================================================
/** Gets the userLogin associated with the cart; may be null */
public GenericValue getUserLogin() {
return this.userLogin;
}
public void setUserLogin(GenericValue userLogin, LocalDispatcher dispatcher) throws CartItemModifyException {
this.userLogin = userLogin;
this.handleNewUser(dispatcher);
}
protected void setUserLogin(GenericValue userLogin) {
if (this.userLogin == null) {
this.userLogin = userLogin;
} else {
throw new IllegalArgumentException("Cannot change UserLogin object with this method");
}
}
public GenericValue getAutoUserLogin() {
return this.autoUserLogin;
}
public void setAutoUserLogin(GenericValue autoUserLogin, LocalDispatcher dispatcher) throws CartItemModifyException {
this.autoUserLogin = autoUserLogin;
if (getUserLogin() == null) {
this.handleNewUser(dispatcher);
}
}
protected void setAutoUserLogin(GenericValue autoUserLogin) {
if (this.autoUserLogin == null) {
this.autoUserLogin = autoUserLogin;
} else {
throw new IllegalArgumentException("Cannot change AutoUserLogin object with this method");
}
}
/**
* SCIPIO: Sets autoUserLogin even if null - for internal use only.
*/
protected void setAutoUserLoginAlways(GenericValue autoUserLogin) {
this.autoUserLogin = autoUserLogin;
}
public void handleNewUser(LocalDispatcher dispatcher) throws CartItemModifyException {
String partyId = this.getPartyId();
if (UtilValidate.isNotEmpty(partyId)) {
// recalculate all prices
for (ShoppingCartItem cartItem : this) {
cartItem.updatePrice(dispatcher, this);
}
// check all promo codes, remove on failed check
Iterator<String> promoCodeIter = this.productPromoCodes.iterator();
while (promoCodeIter.hasNext()) {
String promoCode = promoCodeIter.next();
String checkResult = ProductPromoWorker.checkCanUsePromoCode(promoCode, partyId, this.getDelegator(), locale);
if (checkResult != null) {
promoCodeIter.remove();
Debug.logWarning(UtilProperties.getMessage(resource_error,"OrderOnUserChangePromoCodeWasRemovedBecause", UtilMisc.toMap("checkResult",checkResult), Debug.getLogLocale()), module); // SCIPIO: log locale
}
}
// rerun promotions
ProductPromoWorker.doPromotions(this, dispatcher);
}
}
public String getExternalId() {
return this.externalId;
}
public void setExternalId(String externalId) {
this.externalId = externalId;
}
public String getInternalCode() {
return this.internalCode;
}
public void setInternalCode(String internalCode) {
this.internalCode = internalCode;
}
public String getWebSiteId() {
return this.webSiteId;
}
public void setWebSiteId(String webSiteId) {
this.webSiteId = webSiteId;
}
/**
* Set ship before date for a particular ship group
* @param idx
* @param shipBeforeDate
*/
public void setShipBeforeDate(int idx, Timestamp shipBeforeDate) {
CartShipInfo csi = this.getOrAddShipInfo(idx);
csi.shipBeforeDate = shipBeforeDate;
}
/**
* Set ship before date for ship group 0
* @param shipBeforeDate
*/
public void setShipBeforeDate(Timestamp shipBeforeDate) {
this.setShipBeforeDate(0, shipBeforeDate);
}
/**
* Get ship before date for a particular ship group
* @param idx the ship group number
* @return ship before date for a given ship group
*/
public Timestamp getShipBeforeDate(int idx) {
CartShipInfo csi = this.getShipInfo(idx);
return csi.shipBeforeDate;
}
/**
* Get ship before date for ship group 0
* @return ship before date for the first ship group
*/
public Timestamp getShipBeforeDate() {
return this.getShipBeforeDate(0);
}
/**
* Set ship after date for a particular ship group
* @param idx the ship group number
* @param shipAfterDate the ship after date to be set for the given ship group
*/
public void setShipAfterDate(int idx, Timestamp shipAfterDate) {
CartShipInfo csi = this.getOrAddShipInfo(idx);
csi.shipAfterDate = shipAfterDate;
}
/**
* Set ship after date for a particular ship group
* @param shipAfterDate the ship after date to be set for the first ship group
*/
public void setShipAfterDate(Timestamp shipAfterDate) {
this.setShipAfterDate(0, shipAfterDate);
}
/**
* Get ship after date for a particular ship group
* @param idx the ship group number
* @return return the ship after date for the given ship group
*/
public Timestamp getShipAfterDate(int idx) {
CartShipInfo csi = this.getShipInfo(idx);
return csi.shipAfterDate;
}
/**
* Get ship after date for ship group 0
* @return return the ship after date for the first ship group
*/
public Timestamp getShipAfterDate() {
return this.getShipAfterDate(0);
}
public void setDefaultShipBeforeDate(Timestamp defaultShipBeforeDate) {
this.defaultShipBeforeDate = defaultShipBeforeDate;
}
public Timestamp getDefaultShipBeforeDate() {
return this.defaultShipBeforeDate;
}
public void setDefaultShipAfterDate(Timestamp defaultShipAfterDate) {
this.defaultShipAfterDate = defaultShipAfterDate;
}
public void setCancelBackOrderDate(Timestamp cancelBackOrderDate) {
this.cancelBackOrderDate = cancelBackOrderDate;
}
public Timestamp getCancelBackOrderDate() {
return this.cancelBackOrderDate;
}
public Timestamp getDefaultShipAfterDate() {
return this.defaultShipAfterDate;
}
public String getOrderPartyId() {
return this.orderPartyId != null ? this.orderPartyId : this.getPartyId();
}
public void setOrderPartyId(String orderPartyId) {
this.orderPartyId = orderPartyId;
}
public String getPlacingCustomerPartyId() {
return this.placingCustomerPartyId != null ? this.placingCustomerPartyId : this.getPartyId();
}
public void setPlacingCustomerPartyId(String placingCustomerPartyId) {
this.placingCustomerPartyId = placingCustomerPartyId;
if (UtilValidate.isEmpty(this.orderPartyId)) {
this.orderPartyId = placingCustomerPartyId;
}
}
public String getBillToCustomerPartyId() {
return this.billToCustomerPartyId != null ? this.billToCustomerPartyId : this.getPartyId();
}
public void setBillToCustomerPartyId(String billToCustomerPartyId) {
this.billToCustomerPartyId = billToCustomerPartyId;
if ((UtilValidate.isEmpty(this.orderPartyId)) && !("PURCHASE_ORDER".equals(orderType))) {
this.orderPartyId = billToCustomerPartyId; // orderPartyId should be bill-to-customer when it is not a purchase order
}
}
public String getShipToCustomerPartyId() {
return this.shipToCustomerPartyId != null ? this.shipToCustomerPartyId : this.getPartyId();
}
public void setShipToCustomerPartyId(String shipToCustomerPartyId) {
this.shipToCustomerPartyId = shipToCustomerPartyId;
if (UtilValidate.isEmpty(this.orderPartyId)) {
this.orderPartyId = shipToCustomerPartyId;
}
}
public String getEndUserCustomerPartyId() {
return this.endUserCustomerPartyId != null ? this.endUserCustomerPartyId : this.getPartyId();
}
public void setEndUserCustomerPartyId(String endUserCustomerPartyId) {
this.endUserCustomerPartyId = endUserCustomerPartyId;
if (UtilValidate.isEmpty(this.orderPartyId)) {
this.orderPartyId = endUserCustomerPartyId;
}
}
public String getBillFromVendorPartyId() {
// SCIPIO (12-16-2019): it's really error prone to set billFromVendorPartyId=orderPartyId if billFromVendorPartyId is null,
// It's hard to see any possible scenario where the orderPartyId (likely the customer) is at the same time the one who bill.
//this.billFromVendorPartyId != null ? this.billFromVendorPartyId : this.getPartyId();
return this.billFromVendorPartyId;
}
public void setBillFromVendorPartyId(String billFromVendorPartyId) {
this.billFromVendorPartyId = billFromVendorPartyId;
if ((UtilValidate.isEmpty(this.orderPartyId)) && ("PURCHASE_ORDER".equals(orderType))) {
this.orderPartyId = billFromVendorPartyId; // orderPartyId should be bill-from-vendor when it is a purchase order
}
}
public String getShipFromVendorPartyId() {
return this.shipFromVendorPartyId != null ? this.shipFromVendorPartyId : this.getPartyId();
}
public void setShipFromVendorPartyId(String shipFromVendorPartyId) {
this.shipFromVendorPartyId = shipFromVendorPartyId;
if (UtilValidate.isEmpty(this.orderPartyId)) {
this.orderPartyId = shipFromVendorPartyId;
}
}
public String getSupplierAgentPartyId() {
return this.supplierAgentPartyId != null ? this.supplierAgentPartyId : this.getPartyId();
}
public void setSupplierAgentPartyId(String supplierAgentPartyId) {
this.supplierAgentPartyId = supplierAgentPartyId;
if (UtilValidate.isEmpty(this.orderPartyId)) {
this.orderPartyId = supplierAgentPartyId;
}
}
public String getPartyId() {
String partyId = this.orderPartyId;
if (partyId == null && getUserLogin() != null) {
partyId = getUserLogin().getString("partyId");
}
if (partyId == null && getAutoUserLogin() != null) {
partyId = getAutoUserLogin().getString("partyId");
}
return partyId;
}
public void setAutoSaveList(ShoppingListCookieInfo ci) {
setAutoSaveListId((ci != null) ? ci.getShoppingListId() : null);
setAutoSaveListAuthToken((ci != null) ? ci.getAuthToken() : null);
}
/**
* Sets the auto-save list ID.
* SCIPIO: WARNING: The system implicitly trusts this ID - caller is responsible for ensuring security - use {@link ShoppingListWorker#checkShoppingListSecurity}.
*/
public void setAutoSaveListId(String id) {
this.autoSaveListId = UtilValidate.nullIfEmpty(id);
}
public String getAutoSaveListId() {
return this.autoSaveListId;
}
/**
* Sets the auto-save list ID.
* SCIPIO: WARNING: The system implicitly trusts this ID - caller is responsible for ensuring security - use {@link ShoppingListWorker#checkShoppingListSecurity}.
*/
public void setAutoSaveListAuthToken(String authToken) {
this.autoSaveListAuthToken = UtilValidate.nullIfEmpty(authToken);
}
/**
* Gets autoSaveList auth token.
* <p>FIXME: Currently this is often null and mainly used by fillAutoSaveList to communite the token back to post-processor event.</p>
*/
public String getAutoSaveListAuthToken() {
return this.autoSaveListAuthToken;
}
public void setAutoSaveListParams(Map<String, Object> params) {
this.autoSaveListParams = UtilValidate.isNotEmpty(params) ? Collections.unmodifiableMap(new HashMap<>(params)) : Collections.emptyMap();
}
public void setAutoSaveListParams(Object... params) {
this.autoSaveListParams = UtilValidate.isNotEmpty(params) ? Collections.unmodifiableMap(UtilMisc.toMap(params)) : Collections.emptyMap();
}
public void addAutoSaveListParams(Map<String, Object> params) {
if (UtilValidate.isEmpty(params)) {
return;
}
Map<String, Object> autoSaveListParams = this.autoSaveListParams;
if (UtilValidate.isNotEmpty(autoSaveListParams)) {
autoSaveListParams = new HashMap<>(autoSaveListParams);
autoSaveListParams.putAll(params);
this.autoSaveListParams = Collections.unmodifiableMap(autoSaveListParams);
} else {
this.autoSaveListParams = Collections.unmodifiableMap(new HashMap<>(params));
}
}
public void addAutoSaveListParams(Object... params) {
if (UtilValidate.isEmpty(params)) {
return;
}
Map<String, Object> autoSaveListParams = this.autoSaveListParams;
autoSaveListParams = UtilValidate.isNotEmpty(autoSaveListParams) ? new HashMap<>(autoSaveListParams) : new HashMap<>();
this.autoSaveListParams = Collections.unmodifiableMap(UtilMisc.putAll(autoSaveListParams, params));
}
public Map<String, Object> getAutoSaveListParams() {
return this.autoSaveListParams;
}
public void setLastListRestore(Timestamp time) {
this.lastListRestore = time;
}
public Timestamp getLastListRestore() {
return this.lastListRestore;
}
public BigDecimal getPartyDaysSinceCreated(Timestamp nowTimestamp) {
String partyId = this.getPartyId();
if (UtilValidate.isEmpty(partyId)) {
return null;
}
try {
GenericValue party = this.getDelegator().findOne("Party", UtilMisc.toMap("partyId", partyId), true);
if (party == null) {
return null;
}
Timestamp createdDate = party.getTimestamp("createdDate");
if (createdDate == null) {
return null;
}
BigDecimal diffMillis = new BigDecimal(nowTimestamp.getTime() - createdDate.getTime());
// millis per day: 1000.0 * 60.0 * 60.0 * 24.0 = 86400000.0
return (diffMillis).divide(new BigDecimal("86400000"), generalRounding);
} catch (GenericEntityException e) {
Debug.logError(e, "Error looking up party when getting createdDate", module);
return null;
}
}
// =======================================================================
// Methods for cart fields
// =======================================================================
/** Clears out the cart. */
public void clear() {
this.poNumber = null;
this.orderId = null;
this.firstAttemptOrderId = null;
this.billingAccountId = null;
this.billingAccountAmt = BigDecimal.ZERO;
this.nextItemSeq = 1;
this.agreementId = null;
this.quoteId = null;
this.defaultItemDeliveryDate = null;
this.defaultItemComment = null;
this.orderAdditionalEmails = null;
this.readOnlyCart = false;
this.lastListRestore = null;
this.orderTermSet = false;
this.orderTerms.clear();
this.adjustments.clear();
this.expireSingleUsePayments();
this.cartLines.clear();
this.itemGroupByNumberMap.clear();
this.clearPayments();
this.shipInfo.clear();
this.contactMechIdsMap.clear();
this.internalOrderNotes.clear();
this.orderNotes.clear();
this.attributes.clear();
this.orderAttributes.clear();
// clear the additionalPartyRole Map
for (Map.Entry<String, List<String>> me : this.additionalPartyRole.entrySet()) {
((ArrayList<String>) me.getValue()).clear();
}
this.additionalPartyRole.clear();
this.freeShippingProductPromoActions.clear();
this.desiredAlternateGiftByAction.clear();
this.productPromoUseInfoList.clear();
this.productPromoCodes.clear();
/* 2018-11-22: Removed: cartSubscriptionItems cache is counter-productive
// SCIPIO: Clearing subscription items
if (this.cartSubscriptionItems != null) {
this.cartSubscriptionItems.clear();
}
*/
// clear the auto-save info
if (ProductStoreWorker.autoSaveCart(this.getDelegator(), this.getProductStoreId())) {
GenericValue ul = this.getUserLogin();
if (ul == null) {
ul = this.getAutoUserLogin();
}
// autoSaveListId shouldn't be set to null for anonymous user until the list is not cleared from the database
if (ul != null && !"anonymous".equals(ul.getString("userLoginId"))) {
this.autoSaveListId = null;
}
// load the auto-save list ID
if (autoSaveListId == null) {
try {
Map<String, Object> autoSaveListInfo = ShoppingListEvents.getAutoSaveListInfo(this.getDelegator(),
null, null, ul, this.getProductStoreId(), false, this.autoSaveListParams);
autoSaveListId = (autoSaveListInfo != null) ? (String) autoSaveListInfo.get("shoppingListId") : null;
} catch (GeneralException e) {
Debug.logError(e, module);
}
}
// clear the list
if (autoSaveListId != null) {
try {
org.ofbiz.order.shoppinglist.ShoppingListEvents.clearListInfo(this.getDelegator(), autoSaveListId);
} catch (GeneralException e) { // SCIPIO: Changed to GeneralException
Debug.logError(e, module);
}
}
this.lastListRestore = null;
this.autoSaveListId = null;
this.autoSaveListAuthToken = null;
// SCIPIO: TODO: REVIEW: Do not do this for now. These parameters can simply be overridden and due to
// relying on after-login events, if we clear here some of the info would be lost across cart clears
//this.autoSaveListParams = Collections.emptyMap();
}
// SCIPIO (2020-10-28): Added missing fields (used in mPOS addon)
this.transactionId = null;
this.orderPartyId = null;
this.terminalId = null;
}
/** Sets the order type. */
public void setOrderType(String orderType) {
this.orderType = orderType;
}
/** Returns the order type. */
public String getOrderType() {
return this.orderType;
}
public void setChannelType(String channelType) {
this.channel = channelType;
}
public String getChannelType() {
return this.channel;
}
public boolean isPurchaseOrder() {
return "PURCHASE_ORDER".equals(this.orderType);
}
public boolean isSalesOrder() {
return "SALES_ORDER".equals(this.orderType);
}
/** Sets the PO Number in the cart. */
public void setPoNumber(String poNumber) {
this.poNumber = poNumber;
}
/** Returns the po number. */
public String getPoNumber() {
return poNumber;
}
public void setDefaultItemDeliveryDate(String date) {
this.defaultItemDeliveryDate = date;
}
public String getDefaultItemDeliveryDate() {
return this.defaultItemDeliveryDate;
}
public void setDefaultItemComment(String comment) {
this.defaultItemComment = comment;
}
public String getDefaultItemComment() {
return this.defaultItemComment;
}
public void setAgreementId(String agreementId) {
this.agreementId = agreementId;
}
public String getAgreementId() {
return this.agreementId;
}
public void setQuoteId(String quoteId) {
this.quoteId = quoteId;
}
public String getQuoteId() {
return this.quoteId;
}
// =======================================================================
// Payment Method
// =======================================================================
public String getPaymentMethodTypeId(String paymentMethodId) {
try {
GenericValue pm = this.getDelegator().findOne("PaymentMethod", UtilMisc.toMap("paymentMethodId", paymentMethodId), false);
if (pm != null) {
return pm.getString("paymentMethodTypeId");
}
} catch (GenericEntityException e) {
Debug.logError(e, module);
}
return null;
}
/** Creates a CartPaymentInfo object */
public CartPaymentInfo makePaymentInfo(String id, String refNum, BigDecimal amount) {
CartPaymentInfo inf = new CartPaymentInfo(this.getPartyId()); // SCIPIO: 2018-07-19: billing address default: partyId
inf.refNum[0] = refNum;
inf.amount = amount;
inf.origAmount = amount; // SCIPIO: Save the original amount, that was specified upon creation
if (!isPaymentMethodType(id)) {
inf.paymentMethodTypeId = this.getPaymentMethodTypeId(id);
inf.paymentMethodId = id;
} else {
inf.paymentMethodTypeId = id;
}
return inf;
}
/** Creates a CartPaymentInfo object with a possible authCode (may be null) */
public CartPaymentInfo makePaymentInfo(String id, String refNum, String authCode, BigDecimal amount) {
CartPaymentInfo inf = new CartPaymentInfo(this.getPartyId()); // SCIPIO: 2018-07-19: billing address default: partyId
inf.refNum[0] = refNum;
inf.refNum[1] = authCode;
inf.amount = amount;
inf.origAmount = amount; // SCIPIO: Save the original amount, that was specified upon creation
if (!isPaymentMethodType(id)) {
inf.paymentMethodTypeId = this.getPaymentMethodTypeId(id);
inf.paymentMethodId = id;
} else {
inf.paymentMethodTypeId = id;
}
return inf;
}
/** Locates the index of an existing CartPaymentInfo object or -1 if none found */
public int getPaymentInfoIndex(String id, String refNum) {
CartPaymentInfo thisInf = this.makePaymentInfo(id, refNum, null);
for (int i = 0; i < paymentInfo.size(); i++) {
CartPaymentInfo inf = paymentInfo.get(i);
if (inf.compareTo(thisInf) == 0) {
return i;
}
}
return -1;
}
/** Returns the CartPaymentInfo objects which have matching fields */
public List<CartPaymentInfo> getPaymentInfos(boolean isPaymentMethod, boolean isPaymentMethodType, boolean hasRefNum) {
List<CartPaymentInfo> foundRecords = new ArrayList<>();
for (CartPaymentInfo inf : paymentInfo) {
if (isPaymentMethod && inf.paymentMethodId != null) {
if (hasRefNum && inf.refNum != null) {
foundRecords.add(inf);
} else if (!hasRefNum && inf.refNum == null) {
foundRecords.add(inf);
}
} else if (isPaymentMethodType && inf.paymentMethodTypeId != null) {
if (hasRefNum && inf.refNum != null) {
foundRecords.add(inf);
} else if (!hasRefNum && inf.refNum == null) {
foundRecords.add(inf);
}
}
}
return foundRecords;
}
/** SCIPIO: Returns all payment infos */
public List<CartPaymentInfo> getPaymentInfos() {
return paymentInfo;
}
/** Locates an existing CartPaymentInfo object by index */
public CartPaymentInfo getPaymentInfo(int index) {
return paymentInfo.get(index);
}
/** Locates an existing (or creates a new) CartPaymentInfo object */
public CartPaymentInfo getPaymentInfo(String id, String refNum, String authCode, BigDecimal amount, boolean update) {
CartPaymentInfo thisInf = this.makePaymentInfo(id, refNum, authCode, amount);
for (CartPaymentInfo inf : paymentInfo) {
if (inf.compareTo(thisInf) == 0) {
// update the info
if (update) {
inf.refNum[0] = refNum;
inf.refNum[1] = authCode;
inf.amount = amount;
}
Debug.logInfo("Returned existing PaymentInfo - " + inf.toString(), module);
return inf;
}
}
Debug.logInfo("Returned new PaymentInfo - " + thisInf.toString(), module);
return thisInf;
}
/** Locates an existing (or creates a new) CartPaymentInfo object */
public CartPaymentInfo getPaymentInfo(String id, String refNum, String authCode, BigDecimal amount) {
return this.getPaymentInfo(id, refNum, authCode, amount, false);
}
/** Locates an existing (or creates a new) CartPaymentInfo object */
public CartPaymentInfo getPaymentInfo(String id) {
return this.getPaymentInfo(id, null, null, null, false);
}
/** adds a payment method/payment method type */
public CartPaymentInfo addPaymentAmount(String id, BigDecimal amount, String refNum, String authCode, boolean isSingleUse, boolean isPresent, boolean replace) {
CartPaymentInfo inf = this.getPaymentInfo(id, refNum, authCode, amount, replace);
if (isSalesOrder()) {
GenericValue billingAddress = inf.getBillingAddress(this.getDelegator());
if (billingAddress != null) {
// this payment method will set the billing address for the order;
// before it is set we have to verify if the billing address is
// compatible with the ProductGeos
for (GenericValue product : ShoppingCart.getItemsProducts(this.cartLines)) {
if (!ProductWorker.isBillableToAddress(product, billingAddress)) {
throw new IllegalArgumentException("The billing address is not compatible with ProductGeos rules.");
}
}
}
}
inf.singleUse = isSingleUse;
inf.isPresent = isPresent;
if (replace) {
paymentInfo.remove(inf);
}
paymentInfo.add(inf);
return inf;
}
/** adds a payment method/payment method type */
public CartPaymentInfo addPaymentAmount(String id, BigDecimal amount, boolean isSingleUse) {
return this.addPaymentAmount(id, amount, null, null, isSingleUse, false, true);
}
/** adds a payment method/payment method type */
public CartPaymentInfo addPaymentAmount(String id, BigDecimal amount) {
return this.addPaymentAmount(id, amount, false);
}
/** adds a payment method/payment method type */
public CartPaymentInfo addPayment(String id) {
return this.addPaymentAmount(id, null, false);
}
/** returns the payment method/payment method type amount */
public BigDecimal getPaymentAmount(String id) {
return this.getPaymentInfo(id).amount;
}
/**
* SCIPIO: returns the original payment method/payment method type amount as specified
* upon payment meth info creation (usually by the user).
*/
public BigDecimal getPaymentOrigAmount(String id) {
return this.getPaymentInfo(id).origAmount;
}
/** SCIPIO: Returns all payment amounts */
public Map<String, BigDecimal> getPaymentAmountsByIdOrType() {
// BASED ON OrderReadHelper.getOrderPaymentPreferenceTotalsByIdOrType
// NOTE: Summing may be redundant but should not hurt...
Map<String, BigDecimal> totals = new HashMap<>();
for(CartPaymentInfo info : paymentInfo) {
if (info.amount == null) continue;
if (UtilValidate.isNotEmpty(info.paymentMethodId)) {
BigDecimal total = totals.get(info.paymentMethodId);
if (total == null) {
total = BigDecimal.ZERO;
}
total = total.add(info.amount).setScale(scale, rounding);
totals.put(info.paymentMethodId, total);
} else if (UtilValidate.isNotEmpty(info.paymentMethodTypeId)) {
BigDecimal total = totals.get(info.paymentMethodTypeId);
if (total == null) {
total = BigDecimal.ZERO;
}
total = total.add(info.amount).setScale(scale, rounding);
totals.put(info.paymentMethodTypeId, total);
}
}
return totals;
}
public void addPaymentRef(String id, String ref, String authCode) {
this.getPaymentInfo(id).refNum[0] = ref;
this.getPaymentInfo(id).refNum[1] = authCode;
}
public String getPaymentRef(String id) {
for (CartPaymentInfo inf : paymentInfo) {
if (inf.paymentMethodId.equals(id) || inf.paymentMethodTypeId.equals(id)) {
return inf.refNum[0];
}
}
return null;
}
/** returns the total payment amounts */
public BigDecimal getPaymentTotal() {
BigDecimal total = BigDecimal.ZERO;
for (CartPaymentInfo inf : paymentInfo) {
if (inf.amount != null) {
total = total.add(inf.amount);
}
}
return total;
}
/**
* SCIPIO: Verifies if current payment methods in cart are adequate enough to cover the current order, or in
* other words the cart payments in current state can effectively be used to pay for the order.
*/
public boolean isPaymentsAdequate() {
return CheckOutHelper.isPaymentsAdequate(this);
}
public int selectedPayments() {
return paymentInfo.size();
}
public boolean isPaymentSelected(String id) {
CartPaymentInfo inf = this.getPaymentInfo(id);
return paymentInfo.contains(inf);
}
/** removes a specific payment method/payment method type */
public void clearPayment(String id) {
CartPaymentInfo inf = this.getPaymentInfo(id);
paymentInfo.remove(inf);
}
/** removes a specific payment info from the list */
public void clearPayment(int index) {
paymentInfo.remove(index);
}
/** clears all payment method/payment method types */
public void clearPayments() {
this.expireSingleUsePayments();
paymentInfo.clear();
}
/** remove all the paymentMethods based on the paymentMethodIds */
public void clearPaymentMethodsById(List<String> paymentMethodIdsToRemove) {
if (UtilValidate.isEmpty(paymentMethodIdsToRemove)) {
return;
}
for (Iterator<CartPaymentInfo> iter = paymentInfo.iterator(); iter.hasNext();) {
CartPaymentInfo info = iter.next();
if (paymentMethodIdsToRemove.contains(info.paymentMethodId)) {
iter.remove();
}
}
}
/** remove declined payment methods for an order from cart. The idea is to call this after an attempted order is rejected */
public void clearDeclinedPaymentMethods(Delegator delegator) {
String orderId = this.getOrderId();
if (UtilValidate.isNotEmpty(orderId)) {
try {
List<GenericValue> declinedPaymentMethods = EntityQuery.use(delegator).from("OrderPaymentPreference").where("orderId", orderId, "statusId", "PAYMENT_DECLINED").queryList();
if (UtilValidate.isNotEmpty(declinedPaymentMethods)) {
List<String> paymentMethodIdsToRemove = new ArrayList<>();
for (GenericValue opp : declinedPaymentMethods) {
paymentMethodIdsToRemove.add(opp.getString("paymentMethodId"));
}
clearPaymentMethodsById(paymentMethodIdsToRemove);
}
} catch (GenericEntityException ex) {
Debug.logError("Unable to remove declined payment methods from cart due to " + ex.getMessage(), module);
return;
}
}
}
private void expireSingleUsePayments() {
Timestamp now = UtilDateTime.nowTimestamp();
for (CartPaymentInfo inf : paymentInfo) {
if (inf.paymentMethodId == null || !inf.singleUse) {
continue;
}
GenericValue paymentMethod = null;
try {
paymentMethod = this.getDelegator().findOne("PaymentMethod", UtilMisc.toMap("paymentMethodId", inf.paymentMethodId), false);
} catch (GenericEntityException e) {
Debug.logError(e, "ERROR: Unable to get payment method record to expire : " + inf.paymentMethodId, module);
}
if (paymentMethod != null) {
paymentMethod.set("thruDate", now);
try {
paymentMethod.store();
} catch (GenericEntityException e) {
Debug.logError(e, "Unable to store single use PaymentMethod record : " + paymentMethod, module);
}
} else {
Debug.logError("ERROR: Received back a null payment method record for expired ID : " + inf.paymentMethodId, module);
}
}
}
/** Returns the Payment Method Ids */
public List<String> getPaymentMethodIds() {
List<String> pmi = new ArrayList<>();
for (CartPaymentInfo inf : paymentInfo) {
if (inf.paymentMethodId != null) {
pmi.add(inf.paymentMethodId);
}
}
return pmi;
}
/** Returns the Payment Method Type Ids */
public List<String> getPaymentMethodTypeIds() {
List<String> pmt = new ArrayList<>();
for (CartPaymentInfo inf : paymentInfo) {
if (inf.paymentMethodTypeId != null) {
pmt.add(inf.paymentMethodTypeId);
}
}
return pmt;
}
/** SCIPIO: Returns the Payment Method Ids that have no paymentMethodIds */
public List<String> getPaymentMethodTypeIdsNoPaymentMethodIds() {
List<String> pmt = new ArrayList<>();
for (CartPaymentInfo inf : paymentInfo) {
if (inf.paymentMethodId == null && inf.paymentMethodTypeId != null) {
pmt.add(inf.paymentMethodTypeId);
}
}
return pmt;
}
/** Returns a list of PaymentMethod value objects selected in the cart */
public List<GenericValue> getPaymentMethods() {
List<GenericValue> methods = new ArrayList<>();
if (UtilValidate.isNotEmpty(paymentInfo)) {
for (String paymentMethodId : getPaymentMethodIds()) {
try {
GenericValue paymentMethod = this.getDelegator().findOne("PaymentMethod", UtilMisc.toMap("paymentMethodId", paymentMethodId), true);
if (paymentMethod != null) {
methods.add(paymentMethod);
} else {
Debug.logError("Error getting cart payment methods, the paymentMethodId [" + paymentMethodId +"] is not valid", module);
}
} catch (GenericEntityException e) {
Debug.logError(e, "Unable to get payment method from the database", module);
}
}
}
return methods;
}
/** Returns a list of PaymentMethodType value objects selected in the cart */
public List<GenericValue> getPaymentMethodTypes() {
List<GenericValue> types = new ArrayList<>();
if (UtilValidate.isNotEmpty(paymentInfo)) {
for (String id : getPaymentMethodTypeIds()) {
try {
types.add(this.getDelegator().findOne("PaymentMethodType", UtilMisc.toMap("paymentMethodTypeId", id), true));
} catch (GenericEntityException e) {
Debug.logError(e, "Unable to get payment method type from the database", module);
}
}
}
return types;
}
public List<GenericValue> getCreditCards() {
List<GenericValue> paymentMethods = this.getPaymentMethods();
List<GenericValue> creditCards = new ArrayList<>();
for (GenericValue pm : paymentMethods) {
if ("CREDIT_CARD".equals(pm.getString("paymentMethodTypeId"))) {
try {
GenericValue cc = pm.getRelatedOne("CreditCard", false);
creditCards.add(cc);
} catch (GenericEntityException e) {
Debug.logError(e, "Unable to get credit card record from payment method : " + pm, module);
}
}
}
return creditCards;
}
public List<GenericValue> getGiftCards() {
List<GenericValue> paymentMethods = this.getPaymentMethods();
List<GenericValue> giftCards = new ArrayList<>();
for (GenericValue pm : paymentMethods) {
if ("GIFT_CARD".equals(pm.getString("paymentMethodTypeId"))) {
try {
GenericValue gc = pm.getRelatedOne("GiftCard", false);
giftCards.add(gc);
} catch (GenericEntityException e) {
Debug.logError(e, "Unable to get gift card record from payment method : " + pm, module);
}
}
}
return giftCards;
}
/* determines if the id supplied is a payment method or not by searching in the entity engine */
public boolean isPaymentMethodType(String id) {
GenericValue paymentMethodType = null;
try {
paymentMethodType = this.getDelegator().findOne("PaymentMethodType", UtilMisc.toMap("paymentMethodTypeId", id), true);
} catch (GenericEntityException e) {
Debug.logInfo(e, "Problems getting PaymentMethodType", module);
}
if (paymentMethodType == null) {
return false;
}
return true;
}
public GenericValue getBillingAddress() {
GenericValue billingAddress = null;
for (CartPaymentInfo inf : paymentInfo) {
billingAddress = inf.getBillingAddress(this.getDelegator());
if (billingAddress != null) {
break;
}
}
return billingAddress;
}
/**
* Returns ProductStoreFinActSetting based on cart's productStoreId and FinAccountHelper's defined giftCertFinAcctTypeId
* @param delegator the delegator
* @return returns ProductStoreFinActSetting based on cart's productStoreId
* @throws GenericEntityException
*/
public GenericValue getGiftCertSettingFromStore(Delegator delegator) throws GenericEntityException {
return EntityQuery.use(delegator).from("ProductStoreFinActSetting").where("productStoreId", getProductStoreId(), "finAccountTypeId", FinAccountHelper.giftCertFinAccountTypeId).cache().queryOne();
}
/**
* Determines whether pin numbers are required for gift cards, based on ProductStoreFinActSetting. Default to true.
* @param delegator the delegator
* @return returns true whether pin numbers are required for gift card
*/
public boolean isPinRequiredForGC(Delegator delegator) {
try {
GenericValue giftCertSettings = getGiftCertSettingFromStore(delegator);
if (giftCertSettings != null) {
if ("Y".equals(giftCertSettings.getString("requirePinCode"))) {
return true;
}
return false;
}
Debug.logWarning("No product store gift certificate settings found for store [" + getProductStoreId() + "]",
module);
return true;
} catch (GenericEntityException ex) {
Debug.logError("Error checking if store requires pin number for GC: " + ex.getMessage(), module);
return true;
}
}
/**
* Returns whether the cart should validate gift cards against FinAccount (ie, internal gift certificates). Defaults to false.
* @param delegator the delegator
* @return returns true whether the cart should validate gift cards against FinAccount
*/
public boolean isValidateGCFinAccount(Delegator delegator) {
try {
GenericValue giftCertSettings = getGiftCertSettingFromStore(delegator);
if (giftCertSettings != null) {
if ("Y".equals(giftCertSettings.getString("validateGCFinAcct"))) {
return true;
}
return false;
}
Debug.logWarning("No product store gift certificate settings found for store [" + getProductStoreId() + "]",
module);
return false;
} catch (GenericEntityException ex) {
Debug.logError("Error checking if store requires pin number for GC: " + ex.getMessage(), module);
return false;
}
}
// =======================================================================
// Billing Accounts
// =======================================================================
/** Sets the billing account id string. */
public void setBillingAccount(String billingAccountId, BigDecimal amount) {
this.billingAccountId = billingAccountId;
this.billingAccountAmt = amount;
}
/** Returns the billing message string. */
public String getBillingAccountId() {
return this.billingAccountId;
}
/** Returns the amount to be billed to the billing account.*/
public BigDecimal getBillingAccountAmount() {
return this.billingAccountAmt;
}
// =======================================================================
// Shipping Charges
// =======================================================================
/** Returns the order level shipping amount */
public BigDecimal getOrderShipping() {
return OrderReadHelper.calcOrderAdjustments(this.getAdjustments(), this.getSubTotal(), false, false, true);
}
// ----------------------------------------
// Ship Group Methods
// ----------------------------------------
public int addShipInfo() {
CartShipInfo csi = newShipInfo();
csi.orderTypeId = getOrderType();
shipInfo.add(csi);
return (shipInfo.size() - 1);
}
public List<CartShipInfo> getShipGroups() {
return this.shipInfo;
}
public Map<Integer, BigDecimal> getShipGroups(ShoppingCartItem item) {
Map<Integer, BigDecimal> shipGroups = new LinkedHashMap<>();
if (item != null) {
for (int i = 0; i < this.shipInfo.size(); i++) {
CartShipInfo csi = shipInfo.get(i);
CartShipInfo.CartShipItemInfo csii = csi.shipItemInfo.get(item);
if (csii != null) {
// SCIPIO: DO NOT call this method in this getter because it causes implicit cart modifications
//if (this.checkShipItemInfo(csi, csii)) {
if (this.checkShipItemInfoReadOnly(csi, csii)) {
shipGroups.put(i, csii.quantity);
}
}
}
}
return shipGroups;
}
public Map<Integer, BigDecimal> getShipGroups(int itemIndex) {
return this.getShipGroups(this.findCartItem(itemIndex));
}
/**
* Returns the ship info at the given index.
* SCIPIO: <strong>WARN: DEPRECATION:</strong> Currently, this method adds a ship group if the index points to the end,
* but this behavior must soon be changed - please use {@link #getOrAddShipInfo(int)} instead if you required this behavior.
*/
public CartShipInfo getShipInfo(int idx) {
if (idx == -1) {
return null;
}
if (shipInfo.size() == idx) {
Debug.logWarning("getShipInfo: Deprecated cart modification triggered by ship info index '"
+ idx + "'; please use getOrAddShipInfo instead or prevent the index going of bounds", module);
CartShipInfo csi = newShipInfo();
csi.orderTypeId = getOrderType();
shipInfo.add(csi);
}
return shipInfo.get(idx);
}
/**
* Gets the ship info, or if the index is at the end of the ship groups, creates another (SCIPIO).
* NOTE: This method must be used instead of {@link #getShipInfo(int)}. The getShipInfo method will
* be modifi
*/
public CartShipInfo getOrAddShipInfo(int idx) {
if (idx == -1) {
return null;
}
if (shipInfo.size() == idx) {
CartShipInfo csi = newShipInfo();
csi.orderTypeId = getOrderType();
shipInfo.add(csi);
}
return shipInfo.get(idx);
}
public int getShipGroupSize() {
return this.shipInfo.size();
}
/** Returns the ShoppingCartItem (key) and quantity (value) associated with the ship group */
public Map<ShoppingCartItem, BigDecimal> getShipGroupItems(int idx) {
CartShipInfo csi = this.getShipInfo(idx);
Map<ShoppingCartItem, BigDecimal> qtyMap = new HashMap<>();
for (ShoppingCartItem item : csi.shipItemInfo.keySet()) {
CartShipInfo.CartShipItemInfo csii = csi.shipItemInfo.get(item);
qtyMap.put(item, csii.quantity);
}
return qtyMap;
}
public void clearItemShipInfo(ShoppingCartItem item) {
for (int i = 0; i < shipInfo.size(); i++) {
CartShipInfo csi = this.getShipInfo(i);
csi.shipItemInfo.remove(item);
}
// DEJ20100107: commenting this out because we do NOT want to clear out ship group info since there is information there that will be lost; good enough to clear the item/group association which can be restored later (though questionable, the whole processes using this should be rewritten to not destroy information!
// this.cleanUpShipGroups();
}
public void setItemShipGroupEstimate(BigDecimal amount, int idx) {
CartShipInfo csi = this.getOrAddShipInfo(idx);
csi.shipEstimate = amount;
}
/**
* Updates the shipBefore and shipAfterDates of all ship groups that the item belongs to, re-setting
* ship group ship before date if item ship before date is before it and ship group ship after date if
* item ship after date is before it.
* @param item
*/
public void setShipGroupShipDatesFromItem(ShoppingCartItem item) {
Map<Integer, BigDecimal> shipGroups = this.getShipGroups(item);
for (Integer shipGroup : shipGroups.keySet()) {
CartShipInfo cartShipInfo = this.getShipInfo(shipGroup);
cartShipInfo.resetShipAfterDateIfBefore(item.getShipAfterDate());
cartShipInfo.resetShipBeforeDateIfAfter(item.getShipBeforeDate());
}
}
public BigDecimal getItemShipGroupEstimate(int idx) {
CartShipInfo csi = this.getShipInfo(idx);
return csi.shipEstimate;
}
public void setItemShipGroupQty(int itemIndex, BigDecimal quantity, int idx) {
ShoppingCartItem itemIdx = this.findCartItem(itemIndex);
if(itemIdx != null) {
this.setItemShipGroupQty(itemIdx, itemIndex, quantity, idx);
}
}
public void setItemShipGroupQty(ShoppingCartItem item, BigDecimal quantity, int idx) {
this.setItemShipGroupQty(item, this.getItemIndex(item), quantity, idx);
}
public void setItemShipGroupQty(ShoppingCartItem item, int itemIndex, BigDecimal quantity, int idx) {
if (itemIndex > -1) {
CartShipInfo csi = this.getOrAddShipInfo(idx);
setItemShipGroupQty(item, csi, quantity);
}
}
public void setItemShipGroupQty(ShoppingCartItem item, CartShipInfo csi, BigDecimal quantity) { // SCIPIO: refactored from above
// never set less than zero
if (quantity.compareTo(BigDecimal.ZERO) < 0) {
quantity = BigDecimal.ZERO;
}
// never set more than quantity ordered
if (item != null) {
if (quantity.compareTo(item.getQuantity()) > 0) {
quantity = item.getQuantity();
}
// re-set the ship group's before and after dates based on the item's
csi.resetShipBeforeDateIfAfter(item.getShipBeforeDate());
csi.resetShipAfterDateIfBefore(item.getShipAfterDate());
CartShipInfo.CartShipItemInfo csii = csi.setItemInfo(item, quantity);
this.checkShipItemInfo(csi, csii);
}
}
public BigDecimal getItemShipGroupQty(ShoppingCartItem item, int idx) {
if (item != null) {
CartShipInfo csi = this.getShipInfo(idx);
CartShipInfo.CartShipItemInfo csii = csi.shipItemInfo.get(item);
if (csii != null) {
return csii.quantity;
}
}
return BigDecimal.ZERO;
}
public BigDecimal getItemShipGroupQty(int itemIndex, int idx) {
return this.getItemShipGroupQty(this.findCartItem(itemIndex), idx);
}
public void positionItemToGroup(int itemIndex, BigDecimal quantity, int fromIndex, int toIndex, boolean clearEmptyGroups) {
this.positionItemToGroup(this.findCartItem(itemIndex), quantity, fromIndex, toIndex, clearEmptyGroups);
}
public void positionItemToGroup(ShoppingCartItem item, BigDecimal quantity, int fromIndex, int toIndex, boolean clearEmptyGroups) {
if (fromIndex == toIndex || quantity.compareTo(BigDecimal.ZERO) <= 0) {
// do nothing
return;
}
// get the ship groups; create the TO group if needed
CartShipInfo fromGroup = this.getOrAddShipInfo(fromIndex);
CartShipInfo toGroup = null;
if (toIndex == -1) {
toGroup = newShipInfo();
toGroup.orderTypeId = getOrderType();
this.shipInfo.add(toGroup);
toIndex = this.shipInfo.size() - 1;
} else {
toGroup = this.getOrAddShipInfo(toIndex);
}
// adjust the quantities
if (fromGroup != null && toGroup != null) {
BigDecimal fromQty = this.getItemShipGroupQty(item, fromIndex);
BigDecimal toQty = this.getItemShipGroupQty(item, toIndex);
if (fromQty.compareTo(BigDecimal.ZERO) > 0) {
if (quantity.compareTo(fromQty) > 0) {
quantity = fromQty;
}
fromQty = fromQty.subtract(quantity);
toQty = toQty.add(quantity);
this.setItemShipGroupQty(item, fromQty, fromIndex);
this.setItemShipGroupQty(item, toQty, toIndex);
}
if (clearEmptyGroups) {
// remove any empty ship groups
this.cleanUpShipGroups();
}
}
}
// removes 0 quantity items
protected boolean checkShipItemInfo(CartShipInfo csi, CartShipInfo.CartShipItemInfo csii) {
if (csii.quantity.compareTo(BigDecimal.ZERO) == 0 || csii.item.getQuantity().compareTo(BigDecimal.ZERO) == 0) {
csi.shipItemInfo.remove(csii.item);
return false;
}
return true;
}
// returns true if no zero quantity - without modifying cart (SCIPIO)
protected boolean checkShipItemInfoReadOnly(CartShipInfo csi, CartShipInfo.CartShipItemInfo csii) {
if (csii.quantity.compareTo(BigDecimal.ZERO) == 0 || csii.item.getQuantity().compareTo(BigDecimal.ZERO) == 0) {
return false;
}
return true;
}
public void cleanUpShipGroups() {
Iterator<CartShipInfo> csi = this.shipInfo.iterator();
while (csi.hasNext()) {
CartShipInfo info = csi.next();
Iterator<ShoppingCartItem> si = info.shipItemInfo.keySet().iterator();
while (si.hasNext()) {
ShoppingCartItem item = si.next();
if (item.getQuantity().compareTo(BigDecimal.ZERO) == 0) {
si.remove();
}
}
if (info.shipItemInfo.size() == 0) {
csi.remove();
}
}
}
public int getShipInfoIndex (String shipGroupSeqId) {
int idx = -1;
for (int i=0; i<shipInfo.size(); i++) {
CartShipInfo csi = shipInfo.get(i);
if (shipGroupSeqId.equals(csi.shipGroupSeqId)) {
idx = i;
break;
}
}
return idx;
}
/**
* Return index of the (last) ship group where the item is located.
* SCIPIO: <strong>WARN:</strong> This stock method returns 0 (the first ship group) if the item is not in any ship group; it's only appropriate
* for operations intending to add an item to a ship group; to test if it's in any ship group (usually should be?), use
* {@link #getItemShipGroupIndexIfSet(int)} instead, which returns -1 if not found.
*/
public int getItemShipGroupIndex(int itemId) {
int result = getItemShipGroupIndexIfSet(itemId);
return (result >= 0) ? result : 0;
}
/**
* Return index of the (last) ship group where the item is located, or -1 if it's not in any ship group for some reason (SCIPIO).
*/
public int getItemShipGroupIndexIfSet(int itemId) {
return getItemShipGroupIndexIfSet(this.findCartItem(itemId));
}
/**
* Return index of the (last) ship group where the item is located, or -1 if it's not in any ship group for some reason (SCIPIO).
*/
public int getItemShipGroupIndexIfSet(ShoppingCartItem item) {
int shipGroupIndex = this.getShipGroupSize();
int result = -1;
for (int i = 0; i < shipGroupIndex; i++) {
CartShipInfo csi = this.getShipInfo(i);
for(ShoppingCartItem item2 : csi.shipItemInfo.keySet()) {
if (item.equals(item2)) {
result = i;
}
}
}
return result;
}
/**
* Returns all the CartShipInfo that contain this item (SCIPIO).
*/
public List<CartShipInfo> getItemShipInfos(ShoppingCartItem item) {
List<CartShipInfo> itemCsis = new ArrayList<>();
for(CartShipInfo csi : this.shipInfo) {
if (csi.shipItemInfo.containsKey(item)) {
itemCsis.add(csi);
}
}
return itemCsis;
}
/** Sets the shipping contact mech id. */
public void setShippingContactMechId(int idx, String shippingContactMechId) {
CartShipInfo csi = this.getOrAddShipInfo(idx);
if (isSalesOrder() && UtilValidate.isNotEmpty(shippingContactMechId)) {
// Verify if the new address is compatible with the ProductGeos rules of
// the products already in the cart
GenericValue shippingAddress = null;
try {
shippingAddress = this.getDelegator().findOne("PostalAddress", UtilMisc.toMap("contactMechId", shippingContactMechId), false);
} catch (GenericEntityException gee) {
Debug.logError(gee, "Error retrieving the shipping address for contactMechId [" + shippingContactMechId + "].", module);
}
if (shippingAddress != null) {
Set<ShoppingCartItem> shipItems = csi.getShipItems();
if (UtilValidate.isNotEmpty(shipItems)) {
for (ShoppingCartItem cartItem : shipItems) {
GenericValue product = cartItem.getProduct();
if (product != null) {
if (!ProductWorker.isShippableToAddress(product, shippingAddress)) {
throw new IllegalArgumentException("The shipping address is not compatible with ProductGeos rules.");
}
}
}
}
}
}
csi.setContactMechId(shippingContactMechId);
}
/**
* Sets @param shippingContactMechId in all ShipInfo(ShipGroups) associated
* with this ShoppingCart
* <p>
* @param shippingContactMechId
*/
public void setAllShippingContactMechId(String shippingContactMechId) {
for (int x=0; x < shipInfo.size(); x++) {
this.setShippingContactMechId(x, shippingContactMechId);
}
}
/**
* SCIPIO: Get all shipping contact mech IDs.
*/
public List<String> getAllShippingContactMechId() {
List<String> res = new ArrayList<>(shipInfo.size());
for (CartShipInfo info : this.shipInfo) {
res.add(info.getContactMechId());
}
return res;
}
/** Returns the shipping contact mech id. */
public String getShippingContactMechId(int idx) {
CartShipInfo csi = this.getShipInfo(idx);
return csi.getContactMechId();
}
public String getShippingContactMechId() {
return this.getShippingContactMechId(0);
}
/** Sets the shipment method type. */
public void setShipmentMethodTypeId(int idx, String shipmentMethodTypeId) {
CartShipInfo csi = this.getOrAddShipInfo(idx);
csi.shipmentMethodTypeId = shipmentMethodTypeId;
}
/**
* Sets @param shipmentMethodTypeId in all ShipInfo(ShipGroups) associated
* with this ShoppingCart
* <p>
* @param shipmentMethodTypeId
*/
public void setAllShipmentMethodTypeId(String shipmentMethodTypeId) {
for (int x=0; x < shipInfo.size(); x++) {
this.setShipmentMethodTypeId(x, shipmentMethodTypeId);
}
}
/** Returns the shipment method type ID */
public String getShipmentMethodTypeId(int idx) {
CartShipInfo csi = this.getShipInfo(idx);
return csi.shipmentMethodTypeId;
}
public String getShipmentMethodTypeId() {
return this.getShipmentMethodTypeId(0);
}
/** Returns the shipment method type. */
public GenericValue getShipmentMethodType(int idx) {
String shipmentMethodTypeId = this.getShipmentMethodTypeId(idx);
if (UtilValidate.isNotEmpty(shipmentMethodTypeId)) {
try {
return this.getDelegator().findOne("ShipmentMethodType",
UtilMisc.toMap("shipmentMethodTypeId", shipmentMethodTypeId), false);
} catch (GenericEntityException e) {
Debug.logWarning(e, module);
}
}
return null;
}
/** Sets the supplier for the given ship group (drop shipment). */
public void setSupplierPartyId(int idx, String supplierPartyId) {
CartShipInfo csi = this.getOrAddShipInfo(idx);
// TODO: before we set the value we have to verify if all the products
// already in this ship group are drop shippable from the supplier
csi.supplierPartyId = supplierPartyId;
}
/** Returns the supplier for the given ship group (drop shipment). */
public String getSupplierPartyId(int idx) {
CartShipInfo csi = this.getShipInfo(idx);
return csi.supplierPartyId;
}
/** Sets the shipping instructions. */
public void setShippingInstructions(int idx, String shippingInstructions) {
CartShipInfo csi = this.getOrAddShipInfo(idx);
csi.shippingInstructions = shippingInstructions;
}
/**
* Sets @param shippingInstructions in all ShipInfo(ShipGroups) associated
* with this ShoppingCart
* <p>
* @param shippingInstructions
*/
public void setAllShippingInstructions(String shippingInstructions) {
for (int x=0; x < shipInfo.size(); x++) {
this.setShippingInstructions(x, shippingInstructions);
}
}
/** Returns the shipping instructions. */
public String getShippingInstructions(int idx) {
CartShipInfo csi = this.getShipInfo(idx);
return csi.shippingInstructions;
}
public String getShippingInstructions() {
return this.getShippingInstructions(0);
}
public void setMaySplit(int idx, Boolean maySplit) {
CartShipInfo csi = this.getOrAddShipInfo(idx);
if (UtilValidate.isNotEmpty(maySplit)) {
csi.setMaySplit(maySplit);
}
}
/**
* Sets @param maySplit in all ShipInfo(ShipGroups) associated
* with this ShoppingCart
* <p>
* @param maySplit
*/
public void setAllMaySplit(Boolean maySplit) {
for (int x=0; x < shipInfo.size(); x++) {
this.setMaySplit(x, maySplit);
}
}
/** Returns Boolean.TRUE if the order may be split (null if unspecified) */
public String getMaySplit(int idx) {
CartShipInfo csi = this.getShipInfo(idx);
return csi.maySplit;
}
public String getMaySplit() {
return this.getMaySplit(0);
}
public void setGiftMessage(int idx, String giftMessage) {
CartShipInfo csi = this.getOrAddShipInfo(idx);
csi.giftMessage = giftMessage;
}
/**
* Sets @param giftMessage in all ShipInfo(ShipGroups) associated
* with this ShoppingCart
* <p>
* @param giftMessage
*/
public void setAllGiftMessage(String giftMessage) {
for (int x=0; x < shipInfo.size(); x++) {
this.setGiftMessage(x, giftMessage);
}
}
public String getGiftMessage(int idx) {
CartShipInfo csi = this.getShipInfo(idx);
return csi.giftMessage;
}
public String getGiftMessage() {
return this.getGiftMessage(0);
}
public void setIsGift(int idx, Boolean isGift) {
CartShipInfo csi = this.getOrAddShipInfo(idx);
if (UtilValidate.isNotEmpty(isGift)) {
csi.isGift = isGift ? "Y" : "N";
}
}
/**
* Sets @param isGift in all ShipInfo(ShipGroups) associated
* with this ShoppingCart
* <p>
* @param isGift
*/
public void setAllIsGift(Boolean isGift) {
for (int x=0; x < shipInfo.size(); x++) {
this.setIsGift(x, isGift);
}
}
public String getIsGift(int idx) {
CartShipInfo csi = this.getShipInfo(idx);
return csi.isGift;
}
public String getIsGift() {
return this.getIsGift(0);
}
public void setCarrierPartyId(int idx, String carrierPartyId) {
CartShipInfo csi = this.getOrAddShipInfo(idx);
csi.carrierPartyId = carrierPartyId;
}
/**
* Sets @param carrierPartyId in all ShipInfo(ShipGroups) associated
* with this ShoppingCart
* <p>
* @param carrierPartyId
*/
public void setAllCarrierPartyId(String carrierPartyId) {
for (int x=0; x < shipInfo.size(); x++) {
this.setCarrierPartyId(x, carrierPartyId);
}
}
public String getCarrierPartyId(int idx) {
CartShipInfo csi = this.getShipInfo(idx);
return csi.carrierPartyId;
}
public String getCarrierPartyId() {
return this.getCarrierPartyId(0);
}
public String getProductStoreShipMethId(int idx) {
CartShipInfo csi = this.getShipInfo(idx);
return csi.productStoreShipMethId;
}
public String getProductStoreShipMethId() {
return this.getProductStoreShipMethId(0);
}
public void setProductStoreShipMethId(int idx, String productStoreShipMethId) {
CartShipInfo csi = this.getOrAddShipInfo(idx);
csi.productStoreShipMethId = productStoreShipMethId;
}
/**
* Sets @param productStoreShipMethId in all ShipInfo(ShipGroups) associated
* with this ShoppingCart
* <p>
* @param productStoreShipMethId
*/
public void setAllProductStoreShipMethId(String productStoreShipMethId) {
for (int x=0; x < shipInfo.size(); x++) {
this.setProductStoreShipMethId(x, productStoreShipMethId);
}
}
public void setShipGroupFacilityId(int idx, String facilityId) {
CartShipInfo csi = this.getOrAddShipInfo(idx);
csi.facilityId = facilityId;
}
public String getShipGroupFacilityId(int idx) {
CartShipInfo csi = this.getShipInfo(idx);
return csi.facilityId;
}
public void setShipGroupVendorPartyId(int idx, String vendorPartyId) {
CartShipInfo csi = this.getOrAddShipInfo(idx);
csi.vendorPartyId = vendorPartyId;
}
public String getShipGroupVendorPartyId(int idx) {
CartShipInfo csi = this.getShipInfo(idx);
return csi.vendorPartyId;
}
public void setShipGroupSeqId(int idx, String shipGroupSeqId) {
CartShipInfo csi = this.getOrAddShipInfo(idx);
csi.shipGroupSeqId = shipGroupSeqId;
}
public String getShipGroupSeqId(int idx) {
CartShipInfo csi = this.getShipInfo(idx);
return csi.shipGroupSeqId;
}
public void setOrderAdditionalEmails(String orderAdditionalEmails) {
this.orderAdditionalEmails = orderAdditionalEmails;
}
public String getOrderAdditionalEmails() {
return orderAdditionalEmails;
}
public GenericValue getShippingAddress(int idx) {
if (this.getShippingContactMechId(idx) != null) {
try {
return getDelegator().findOne("PostalAddress", UtilMisc.toMap("contactMechId", this.getShippingContactMechId(idx)), false);
} catch (GenericEntityException e) {
Debug.logWarning(e.toString(), module);
return null;
}
}
return null;
}
public GenericValue getShippingAddress() {
return this.getShippingAddress(0);
}
// ----------------------------------------
// internal/public notes
// ----------------------------------------
public List<String> getInternalOrderNotes() {
return this.internalOrderNotes;
}
public List<String> getOrderNotes() {
return this.orderNotes;
}
public void addInternalOrderNote(String note) {
this.internalOrderNotes.add(note);
}
public void clearInternalOrderNotes() {
this.internalOrderNotes.clear();
}
public void clearOrderNotes() {
this.orderNotes.clear();
}
public void addOrderNote(String note) {
this.orderNotes.add(note);
}
// Preset with default values some of the checkout options to get a quicker checkout process.
public void setDefaultCheckoutOptions(LocalDispatcher dispatcher) {
// skip the add party screen
this.setAttribute("addpty", "Y");
if ("SALES_ORDER".equals(getOrderType())) {
// checkout options for sales orders
// set as the default shipping location the first from the list of available shipping locations
if (this.getPartyId() != null && !"_NA_".equals(this.getPartyId())) {
try {
GenericValue orderParty = this.getDelegator().findOne("Party", UtilMisc.toMap("partyId", this.getPartyId()), false);
Collection<GenericValue> shippingContactMechList = ContactHelper.getContactMech(orderParty, "SHIPPING_LOCATION", "POSTAL_ADDRESS", false);
if (UtilValidate.isNotEmpty(shippingContactMechList)) {
GenericValue shippingContactMech = (shippingContactMechList.iterator()).next();
this.setAllShippingContactMechId(shippingContactMech.getString("contactMechId"));
}
} catch (GenericEntityException e) {
Debug.logError(e, "Error setting shippingContactMechId in setDefaultCheckoutOptions() method.", module);
}
}
// set the default shipment method
ShippingEstimateWrapper shipEstimateWrapper = org.ofbiz.order.shoppingcart.shipping.ShippingEstimateWrapper.getWrapper(dispatcher, this, 0);
GenericValue carrierShipmentMethod = EntityUtil.getFirst(shipEstimateWrapper.getShippingMethods());
if (carrierShipmentMethod != null) {
this.setAllShipmentMethodTypeId(carrierShipmentMethod.getString("shipmentMethodTypeId"));
this.setAllCarrierPartyId(carrierShipmentMethod.getString("partyId"));
}
} else {
// checkout options for purchase orders
// TODO: should we select a default agreement? For now we don't do this.
// skip the order terms selection step
this.setOrderTermSet(true);
// set as the default shipping location the first from the list of available shipping locations
String companyId = this.getBillToCustomerPartyId();
if (companyId != null) {
// the facilityId should be set prior to triggering default options, otherwise we do not set up facility information
String defaultFacilityId = getFacilityId();
if (defaultFacilityId != null) {
GenericValue facilityContactMech = ContactMechWorker.getFacilityContactMechByPurpose(this.getDelegator(), facilityId, UtilMisc.toList("SHIPPING_LOCATION", "PRIMARY_LOCATION"));
if (facilityContactMech != null) {
this.setShippingContactMechId(0, facilityContactMech.getString("contactMechId"));
}
}
}
// shipping options
this.setAllShipmentMethodTypeId("NO_SHIPPING");
this.setAllCarrierPartyId("_NA_");
this.setAllShippingInstructions("");
this.setAllGiftMessage("");
this.setAllMaySplit(Boolean.TRUE);
this.setAllIsGift(Boolean.FALSE);
}
}
// Returns the tax amount for a ship group. */
public BigDecimal getTotalSalesTax(int shipGroup) {
CartShipInfo csi = this.getShipInfo(shipGroup);
return csi.getTotalTax(this);
}
/** Returns the tax amount from the cart object. */
public BigDecimal getTotalSalesTax() {
BigDecimal totalTax = BigDecimal.ZERO;
for (int i = 0; i < shipInfo.size(); i++) {
CartShipInfo csi = this.getShipInfo(i);
totalTax = totalTax.add(csi.getTotalTax(this)).setScale(taxCalcScale, taxRounding);
}
return totalTax.setScale(taxFinalScale, taxRounding);
}
/** SCIPIO: Returns the tax amount from the cart object. */
public BigDecimal getTotalVATTax() {
BigDecimal totalTax = ZERO;
for (int i = 0; i < shipInfo.size(); i++) {
CartShipInfo csi = this.getShipInfo(i);
totalTax = totalTax.add(csi.getTotalVATTax(this)).setScale(taxCalcScale, taxRounding);
}
return totalTax.setScale(taxFinalScale, taxRounding);
}
/** Returns the shipping amount from the cart object. */
public BigDecimal getTotalShipping() {
BigDecimal tempShipping = BigDecimal.ZERO;
for (CartShipInfo csi : this.shipInfo) {
tempShipping = tempShipping.add(csi.shipEstimate);
}
return tempShipping;
}
/** Returns the item-total in the cart (not including discount/tax/shipping). */
public BigDecimal getItemTotal() {
BigDecimal itemTotal = BigDecimal.ZERO;
for (ShoppingCartItem cartItem : this.cartLines) {
itemTotal = itemTotal.add(cartItem.getBasePrice());
}
return itemTotal;
}
/** Returns the sub-total in the cart (item-total - discount). */
public BigDecimal getSubTotal() {
BigDecimal itemsTotal = BigDecimal.ZERO;
for (ShoppingCartItem cartItem : this.cartLines) {
itemsTotal = itemsTotal.add(cartItem.getItemSubTotal());
}
return itemsTotal;
}
/** Returns the total from the cart, including tax/shipping. */
public BigDecimal getGrandTotal() {
// sales tax and shipping are not stored as adjustments but rather as part of the ship group
return this.getSubTotal().add(this.getTotalShipping()).add(this.getTotalSalesTax()).add(this.getOrderOtherAdjustmentTotal()).add(this.getOrderGlobalAdjustments());
}
public BigDecimal getDisplaySubTotal() {
BigDecimal itemsTotal = BigDecimal.ZERO;
for (ShoppingCartItem cartItem : this.cartLines) {
itemsTotal = itemsTotal.add(cartItem.getDisplayItemSubTotal());
}
return itemsTotal;
}
public BigDecimal getOrderGlobalAdjustments() {
List<GenericValue> cartAdjustments = this.getAdjustments();
List<GenericValue> tempAdjustmentsList = new ArrayList<>();
if (cartAdjustments != null) {
Iterator<GenericValue> cartAdjustmentIter = cartAdjustments.iterator();
while (cartAdjustmentIter.hasNext()) {
GenericValue checkOrderAdjustment = cartAdjustmentIter.next();
if (UtilValidate.isEmpty(checkOrderAdjustment.getString("shipGroupSeqId")) || DataModelConstants.SEQ_ID_NA.equals(checkOrderAdjustment.getString("shipGroupSeqId"))) {
tempAdjustmentsList.add(checkOrderAdjustment);
}
}
}
return OrderReadHelper.calcOrderAdjustments(tempAdjustmentsList, this.getSubTotal(), false, true, true);
}
public BigDecimal getDisplayTaxIncluded() {
BigDecimal taxIncluded = getDisplaySubTotal().subtract(getSubTotal());
return taxIncluded.setScale(taxFinalScale, taxRounding);
}
public BigDecimal getDisplayRecurringSubTotal() {
BigDecimal itemsTotal = BigDecimal.ZERO;
for (ShoppingCartItem cartItem : this.cartLines) {
itemsTotal = itemsTotal.add(cartItem.getDisplayItemRecurringSubTotal());
}
return itemsTotal;
}
/** Returns the total from the cart, including tax/shipping. */
public BigDecimal getDisplayGrandTotal() {
return this.getDisplaySubTotal().add(this.getTotalShipping()).add(this.getTotalSalesTax()).add(this.getOrderOtherAdjustmentTotal()).add(this.getOrderGlobalAdjustments());
}
public BigDecimal getOrderOtherAdjustmentTotal() {
return OrderReadHelper.calcOrderAdjustments(this.getAdjustments(), this.getSubTotal(), true, false, false);
}
/** Returns the sub-total in the cart (item-total - discount). */
public BigDecimal getSubTotalForPromotions() {
BigDecimal itemsTotal = BigDecimal.ZERO;
for (ShoppingCartItem cartItem : this.cartLines) {
GenericValue product = cartItem.getProduct();
if (product != null && "N".equals(product.getString("includeInPromotions"))) {
// don't include in total if this is the case...
continue;
}
itemsTotal = itemsTotal.add(cartItem.getItemSubTotal());
}
return itemsTotal.add(this.getOrderOtherAdjustmentTotal());
}
public BigDecimal getSubTotalForPromotions(Set<String> productIds) {
BigDecimal itemsTotal = BigDecimal.ZERO;
for (ShoppingCartItem cartItem : this.cartLines) {
GenericValue product = cartItem.getProduct();
if (product == null || "N".equals(product.getString("includeInPromotions")) || !productIds.contains(cartItem.getProductId())) {
// don't include in total if this is the case...
continue;
}
itemsTotal = itemsTotal.add(cartItem.getItemSubTotal());
}
return itemsTotal;
}
/** SCIPIO (10/28/2018): Gets other adjustments and tax totals */
public BigDecimal getOrderOtherAndTaxAdjustmentTotal() {
return OrderReadHelper.calcOrderAdjustments(this.getAdjustments(), this.getSubTotal(), true, true, false);
}
/** SCIPIO (10/28/2018): Returns the total in the cart ((item-total + tax) - discount). */
public BigDecimal getTotalForPromotions() {
BigDecimal itemsTotal = BigDecimal.ZERO;
for (ShoppingCartItem cartItem : this.cartLines) {
GenericValue product = cartItem.getProduct();
if (product != null && "N".equals(product.getString("includeInPromotions"))) {
// don't include in total if this is the case...
continue;
}
itemsTotal = itemsTotal.add(cartItem.getItemSubTotal());
}
return itemsTotal.add(this.getOrderOtherAndTaxAdjustmentTotal());
}
public BigDecimal getTotalForPromotions(Set<String> productIds) {
BigDecimal itemsTotal = BigDecimal.ZERO;
for (ShoppingCartItem cartItem : this.cartLines) {
GenericValue product = cartItem.getProduct();
if (product == null || "N".equals(product.getString("includeInPromotions")) || !productIds.contains(cartItem.getProductId())) {
// don't include in total if this is the case...
continue;
}
itemsTotal = itemsTotal.add(cartItem.getItemSubTotal());
}
return itemsTotal.add(this.getOrderOtherAndTaxAdjustmentTotal());
}
/**
* Get the total payment amount by payment type. Specify null to get amount
* over all types.
*/
public BigDecimal getOrderPaymentPreferenceTotalByType(String paymentMethodTypeId) {
BigDecimal total = BigDecimal.ZERO;
String thisPaymentMethodTypeId = null;
for (CartPaymentInfo payment : paymentInfo) {
if (payment.amount == null) {
continue;
}
if (payment.paymentMethodId != null) {
try {
// need to determine the payment method type from the payment method
GenericValue paymentMethod = this.getDelegator().findOne("PaymentMethod", UtilMisc.toMap("paymentMethodId", payment.paymentMethodId), true);
if (paymentMethod != null) {
thisPaymentMethodTypeId = paymentMethod.getString("paymentMethodTypeId");
}
} catch (GenericEntityException e) {
Debug.logError(e, e.getMessage(), module);
}
} else {
thisPaymentMethodTypeId = payment.paymentMethodTypeId;
}
// add the amount according to paymentMethodType
if (paymentMethodTypeId == null || paymentMethodTypeId.equals(thisPaymentMethodTypeId)) {
total = total.add(payment.amount);
}
}
return total;
}
public BigDecimal getCreditCardPaymentPreferenceTotal() {
return getOrderPaymentPreferenceTotalByType("CREDIT_CARD");
}
public BigDecimal getBillingAccountPaymentPreferenceTotal() {
return getOrderPaymentPreferenceTotalByType("EXT_BILLACT");
}
public BigDecimal getGiftCardPaymentPreferenceTotal() {
return getOrderPaymentPreferenceTotalByType("GIFT_CARD");
}
/** Add a contact mech to this purpose; the contactMechPurposeTypeId is required */
public void addContactMech(String contactMechPurposeTypeId, String contactMechId) {
if (contactMechPurposeTypeId == null) {
throw new IllegalArgumentException("You must specify a contactMechPurposeTypeId to add a ContactMech");
}
contactMechIdsMap.put(contactMechPurposeTypeId, contactMechId);
}
/** Get the contactMechId for this cart given the contactMechPurposeTypeId */
public String getContactMech(String contactMechPurposeTypeId) {
return contactMechIdsMap.get(contactMechPurposeTypeId);
}
/** Remove the contactMechId from this cart given the contactMechPurposeTypeId */
public String removeContactMech(String contactMechPurposeTypeId) {
return contactMechIdsMap.remove(contactMechPurposeTypeId);
}
public Map<String, String> getOrderContactMechIds() {
return this.contactMechIdsMap;
}
/** Get a List of adjustments on the order (ie cart) */
public List<GenericValue> getAdjustments() {
return adjustments;
}
public int getAdjustmentPromoIndex(String productPromoId) {
if (UtilValidate.isEmpty(productPromoId)) {
return -1;
}
int index = adjustments.size();
while (index > 0) {
index--;
if (productPromoId.equals(adjustments.get(index).getString("productPromoId"))) {
return (index);
}
}
return -1;
}
/** Add an adjustment to the order; don't worry about setting the orderId, orderItemSeqId or orderAdjustmentId; they will be set when the order is created */
public int addAdjustment(GenericValue adjustment) {
adjustments.add(adjustment);
return adjustments.indexOf(adjustment);
}
public void removeAdjustment(int index) {
adjustments.remove(index);
}
public GenericValue getAdjustment(int index) {
return adjustments.get(index);
}
/** Get a List of orderTerms on the order (ie cart) */
public List<GenericValue> getOrderTerms() {
return orderTerms;
}
/** Add an orderTerm to the order */
public int addOrderTerm(String termTypeId, BigDecimal termValue, Long termDays) {
return addOrderTerm(termTypeId, termValue, termDays, null);
}
/** Add an orderTerm to the order */
public int addOrderTerm(String termTypeId, BigDecimal termValue, Long termDays, String textValue) {
GenericValue orderTerm = this.getDelegator().makeValue("OrderTerm");
orderTerm.put("termTypeId", termTypeId);
orderTerm.put("termValue", termValue);
orderTerm.put("termDays", termDays);
orderTerm.put("textValue", textValue);
return addOrderTerm(orderTerm);
}
public int addOrderTerm(String termTypeId, String orderItemSeqId,BigDecimal termValue, Long termDays, String textValue, String description) {
GenericValue orderTerm = this.getDelegator().makeValue("OrderTerm");
orderTerm.put("termTypeId", termTypeId);
if (UtilValidate.isEmpty(orderItemSeqId)) {
orderItemSeqId = "_NA_";
}
orderTerm.put("orderItemSeqId", orderItemSeqId);
orderTerm.put("termValue", termValue);
orderTerm.put("termDays", termDays);
orderTerm.put("textValue", textValue);
orderTerm.put("description", description);
return addOrderTerm(orderTerm);
}
/** Add an orderTerm to the order */
public int addOrderTerm(GenericValue orderTerm) {
orderTerms.add(orderTerm);
return orderTerms.indexOf(orderTerm);
}
public void removeOrderTerm(int index) {
orderTerms.remove(index);
}
public void removeOrderTerms() {
orderTerms.clear();
}
public boolean isOrderTermSet() {
return orderTermSet;
}
public void setOrderTermSet(boolean orderTermSet) {
this.orderTermSet = orderTermSet;
}
public boolean hasOrderTerm(String termTypeId) {
if (termTypeId == null) {
return false;
}
for (GenericValue orderTerm : orderTerms) {
if (termTypeId.equals(orderTerm.getString("termTypeId"))) {
return true;
}
}
return false;
}
public boolean isReadOnlyCart() {
return readOnlyCart;
}
public void setReadOnlyCart(boolean readOnlyCart) {
this.readOnlyCart = readOnlyCart;
}
/** go through the order adjustments and remove all adjustments with the given type */
public void removeAdjustmentByType(String orderAdjustmentTypeId) {
if (orderAdjustmentTypeId == null) {
return;
}
// make a list of adjustment lists including the cart adjustments and the cartItem adjustments for each item
List<List<GenericValue>> adjsLists = new ArrayList<>();
adjsLists.add(this.getAdjustments());
for (ShoppingCartItem item : this) {
if (item.getAdjustments() != null) {
adjsLists.add(item.getAdjustments());
}
}
for (List<GenericValue> adjs: adjsLists) {
if (adjs != null) {
for (int i = 0; i < adjs.size();) {
GenericValue orderAdjustment = adjs.get(i);
if (orderAdjustmentTypeId.equals(orderAdjustment.getString("orderAdjustmentTypeId"))) {
adjs.remove(i);
} else {
i++;
}
}
}
}
}
/** Returns the total weight in the cart. */
public BigDecimal getTotalWeight() {
BigDecimal weight = BigDecimal.ZERO;
for (ShoppingCartItem item : this.cartLines) {
weight = weight.add(item.getWeight().multiply(item.getQuantity()));
}
return weight;
}
/** Returns the total quantity in the cart. */
public BigDecimal getTotalQuantity() {
BigDecimal count = BigDecimal.ZERO;
for (ShoppingCartItem item : this.cartLines) {
count = count.add(item.getQuantity());
}
return count;
}
/** Returns the SHIPPABLE item-total in the cart for a specific ship group. */
public BigDecimal getShippableTotal(int idx) {
CartShipInfo info = this.getShipInfo(idx);
BigDecimal itemTotal = BigDecimal.ZERO;
for (ShoppingCartItem item : info.shipItemInfo.keySet()) {
CartShipInfo.CartShipItemInfo csii = info.shipItemInfo.get(item);
if (csii != null && csii.quantity.compareTo(BigDecimal.ZERO) > 0) {
if (item.shippingApplies()) {
itemTotal = itemTotal.add(item.getItemSubTotal(csii.quantity));
}
}
}
return itemTotal;
}
/** Returns the total SHIPPABLE quantity in the cart for a specific ship group. */
public BigDecimal getShippableQuantity(int idx) {
CartShipInfo info = this.getShipInfo(idx);
BigDecimal count = BigDecimal.ZERO;
for (ShoppingCartItem item : info.shipItemInfo.keySet()) {
CartShipInfo.CartShipItemInfo csii = info.shipItemInfo.get(item);
if (csii != null && csii.quantity.compareTo(BigDecimal.ZERO) > 0) {
if (item.shippingApplies()) {
count = count.add(csii.quantity);
}
}
}
return count;
}
/** Returns the total SHIPPABLE weight in the cart for a specific ship group. */
public BigDecimal getShippableWeight(int idx) {
CartShipInfo info = this.getShipInfo(idx);
BigDecimal weight = BigDecimal.ZERO;
for (ShoppingCartItem item : info.shipItemInfo.keySet()) {
CartShipInfo.CartShipItemInfo csii = info.shipItemInfo.get(item);
if (csii != null && csii.quantity.compareTo(BigDecimal.ZERO) > 0) {
if (item.shippingApplies()) {
weight = weight.add(item.getWeight().multiply(csii.quantity));
}
}
}
return weight;
}
/** Returns a List of shippable item's size for a specific ship group. */
public List<BigDecimal> getShippableSizes(int idx) {
CartShipInfo info = this.getShipInfo(idx);
List<BigDecimal> shippableSizes = new ArrayList<>();
for (ShoppingCartItem item : info.shipItemInfo.keySet()) {
CartShipInfo.CartShipItemInfo csii = info.shipItemInfo.get(item);
if (csii != null && csii.quantity.compareTo(BigDecimal.ZERO) > 0) {
if (item.shippingApplies()) {
shippableSizes.add(item.getSize());
}
}
}
return shippableSizes;
}
/** Returns a List of shippable item info (quantity, size, weight) for a specific ship group */
public List<Map<String, Object>> getShippableItemInfo(int idx) {
CartShipInfo info = this.getShipInfo(idx);
List<Map<String, Object>> itemInfos = new ArrayList<>();
for (ShoppingCartItem item : info.shipItemInfo.keySet()) {
CartShipInfo.CartShipItemInfo csii = info.shipItemInfo.get(item);
if (csii != null && csii.quantity.compareTo(BigDecimal.ZERO) > 0) {
if (item.shippingApplies()) {
Map<String, Object> itemInfo = item.getItemProductInfo();
itemInfo.put("quantity", csii.quantity);
itemInfos.add(itemInfo);
}
}
}
return itemInfos;
}
/** Returns true when there are shippable items in the cart */
public boolean shippingApplies() {
boolean shippingApplies = false;
for (ShoppingCartItem item : this) {
if (item.shippingApplies()) {
shippingApplies = true;
break;
}
}
return shippingApplies;
}
/** Returns true when there are taxable items in the cart */
public boolean taxApplies() {
boolean taxApplies = false;
for (ShoppingCartItem item : this) {
if (item.taxApplies()) {
taxApplies = true;
break;
}
}
return taxApplies;
}
/** Returns a Map of all features applied to products in the cart with quantities for a specific ship group. */
public Map<String, BigDecimal> getFeatureIdQtyMap(int idx) {
CartShipInfo info = this.getShipInfo(idx);
Map<String, BigDecimal> featureMap = new HashMap<>();
for (ShoppingCartItem item : info.shipItemInfo.keySet()) {
CartShipInfo.CartShipItemInfo csii = info.shipItemInfo.get(item);
if (csii != null && csii.quantity.compareTo(BigDecimal.ZERO) > 0) {
featureMap.putAll(item.getFeatureIdQtyMap(csii.quantity));
}
}
return featureMap;
}
/** Returns true if the user wishes to view the cart everytime an item is added. */
public boolean viewCartOnAdd() {
return viewCartOnAdd;
}
/** Returns true if the user wishes to view the cart everytime an item is added. */
public void setViewCartOnAdd(boolean viewCartOnAdd) {
this.viewCartOnAdd = viewCartOnAdd;
}
/** Returns the order ID associated with this cart or null if no order has been created yet. */
public String getOrderId() {
return this.orderId;
}
/** Returns the first attempt order ID associated with this cart or null if no order has been created yet. */
public String getFirstAttemptOrderId() {
return this.firstAttemptOrderId;
}
/** Sets the orderId associated with this cart. */
public void setOrderId(String orderId) {
this.orderId = orderId;
}
public void setNextItemSeq(long seq) throws GeneralException {
if (this.nextItemSeq != 1) {
throw new GeneralException("Cannot set the item sequence once the sequence has been incremented!");
}
this.nextItemSeq = seq;
}
/** TODO: Sets the first attempt orderId for this cart. */
public void setFirstAttemptOrderId(String orderId) {
this.firstAttemptOrderId = orderId;
}
public void removeAllFreeShippingProductPromoActions() {
this.freeShippingProductPromoActions.clear();
}
/** Removes a free shipping ProductPromoAction by trying to find one in the list with the same primary key. */
public void removeFreeShippingProductPromoAction(GenericPK productPromoActionPK) {
if (productPromoActionPK == null) {
return;
}
Iterator<GenericValue> fsppas = this.freeShippingProductPromoActions.iterator();
while (fsppas.hasNext()) {
if (productPromoActionPK.equals((fsppas.next()).getPrimaryKey())) {
fsppas.remove();
}
}
}
/** Adds a ProductPromoAction to be used for free shipping (must be of type free shipping, or nothing will be done). */
public void addFreeShippingProductPromoAction(GenericValue productPromoAction) {
if (productPromoAction == null) {
return;
}
// is this a free shipping action?
if (!"PROMO_FREE_SHIPPING".equals(productPromoAction.getString("productPromoActionEnumId"))) {
return; // Changed 1-5-04 by Si Chen
}
// to easily make sure that no duplicate exists, do a remove first
this.removeFreeShippingProductPromoAction(productPromoAction.getPrimaryKey());
this.freeShippingProductPromoActions.add(productPromoAction);
}
public List<GenericValue> getFreeShippingProductPromoActions() {
return this.freeShippingProductPromoActions;
}
public void removeAllDesiredAlternateGiftByActions() {
this.desiredAlternateGiftByAction.clear();
}
public void setDesiredAlternateGiftByAction(GenericPK productPromoActionPK, String productId) {
this.desiredAlternateGiftByAction.put(productPromoActionPK, productId);
}
public String getDesiredAlternateGiftByAction(GenericPK productPromoActionPK) {
return this.desiredAlternateGiftByAction.get(productPromoActionPK);
}
public Map<GenericPK, String> getAllDesiredAlternateGiftByActionCopy() {
return new HashMap<>(this.desiredAlternateGiftByAction);
}
public void addProductPromoUse(String productPromoId, String productPromoCodeId, BigDecimal totalDiscountAmount, BigDecimal quantityLeftInActions, Map<ShoppingCartItem,BigDecimal> usageInfoMap) {
if (UtilValidate.isNotEmpty(productPromoCodeId) && !this.productPromoCodes.contains(productPromoCodeId)) {
throw new IllegalStateException("Cannot add a use to a promo code use for a code that has not been entered.");
}
if (Debug.verboseOn()) {
Debug.logVerbose("Used promotion [" + productPromoId + "] with code [" + productPromoCodeId + "] for total discount [" + totalDiscountAmount + "] and quantity left in actions [" + quantityLeftInActions + "]", module);
}
this.productPromoUseInfoList.add(new ProductPromoUseInfo(productPromoId, productPromoCodeId, totalDiscountAmount, quantityLeftInActions, usageInfoMap));
}
public void removeProductPromoUse(String productPromoId) {
if (!productPromoId.isEmpty()) {
int index = -1;
for (ProductPromoUseInfo productPromoUseInfo : this.productPromoUseInfoList) {
if (productPromoId.equals(productPromoUseInfo.productPromoId)) {
index = this.productPromoUseInfoList.indexOf(productPromoUseInfo);
break;
}
}
if (index != -1) {
this.productPromoUseInfoList.remove(index);
}
}
}
public void clearProductPromoUseInfo() {
// clear out info for general promo use
this.productPromoUseInfoList.clear();
}
public void clearCartItemUseInPromoInfo() {
// clear out info about which cart items have been used in promos
for (ShoppingCartItem cartLine : this) {
cartLine.clearPromoRuleUseInfo();
}
}
public Iterator<ProductPromoUseInfo> getProductPromoUseInfoIter() {
return productPromoUseInfoList.iterator();
}
public BigDecimal getProductPromoTotal() {
BigDecimal totalDiscount = BigDecimal.ZERO;
List<GenericValue> cartAdjustments = this.getAdjustments();
if (cartAdjustments != null) {
for (GenericValue checkOrderAdjustment : cartAdjustments) {
if (UtilValidate.isNotEmpty(checkOrderAdjustment.getString("productPromoId")) &&
UtilValidate.isNotEmpty(checkOrderAdjustment.getString("productPromoRuleId")) &&
UtilValidate.isNotEmpty(checkOrderAdjustment.getString("productPromoActionSeqId"))) {
if (checkOrderAdjustment.get("amount") != null) {
totalDiscount = totalDiscount.add(checkOrderAdjustment.getBigDecimal("amount"));
}
}
}
}
// add cart line adjustments from promo actions
for (ShoppingCartItem checkItem : this) {
Iterator<GenericValue> checkOrderAdjustments = UtilMisc.toIterator(checkItem.getAdjustments());
while (checkOrderAdjustments != null && checkOrderAdjustments.hasNext()) {
GenericValue checkOrderAdjustment = checkOrderAdjustments.next();
if (UtilValidate.isNotEmpty(checkOrderAdjustment.getString("productPromoId")) &&
UtilValidate.isNotEmpty(checkOrderAdjustment.getString("productPromoRuleId")) &&
UtilValidate.isNotEmpty(checkOrderAdjustment.getString("productPromoActionSeqId"))) {
if (checkOrderAdjustment.get("amount") != null) {
totalDiscount = totalDiscount.add(checkOrderAdjustment.getBigDecimal("amount"));
}
}
}
}
return totalDiscount;
}
/** Get total discount for a given ProductPromo, or for ANY ProductPromo if the passed in productPromoId is null. */
public BigDecimal getProductPromoUseTotalDiscount(String productPromoId) {
BigDecimal totalDiscount = BigDecimal.ZERO;
for (ProductPromoUseInfo productPromoUseInfo: this.productPromoUseInfoList) {
if (productPromoId == null || productPromoId.equals(productPromoUseInfo.productPromoId)) {
totalDiscount = totalDiscount.add(productPromoUseInfo.getTotalDiscountAmount());
}
}
return totalDiscount;
}
public int getProductPromoUseCount(String productPromoId) {
if (productPromoId == null) {
return 0;
}
int useCount = 0;
for (ProductPromoUseInfo productPromoUseInfo: this.productPromoUseInfoList) {
if (productPromoId.equals(productPromoUseInfo.productPromoId)) {
useCount++;
}
}
return useCount;
}
public int getProductPromoCodeUse(String productPromoCodeId) {
if (productPromoCodeId == null) {
return 0;
}
int useCount = 0;
for (ProductPromoUseInfo productPromoUseInfo: this.productPromoUseInfoList) {
if (productPromoCodeId.equals(productPromoUseInfo.productPromoCodeId)) {
useCount++;
}
}
return useCount;
}
public void clearAllPromotionInformation() {
this.clearAllPromotionAdjustments();
// remove all free shipping promo actions
this.removeAllFreeShippingProductPromoActions();
// clear promo uses & reset promo code uses, and reset info about cart items used for promos (ie qualifiers and benefiters)
this.clearProductPromoUseInfo();
this.clearCartItemUseInPromoInfo();
}
public void clearAllPromotionAdjustments() {
// remove cart adjustments from promo actions
List<GenericValue> cartAdjustments = this.getAdjustments();
if (cartAdjustments != null) {
Iterator<GenericValue> cartAdjustmentIter = cartAdjustments.iterator();
while (cartAdjustmentIter.hasNext()) {
GenericValue checkOrderAdjustment = cartAdjustmentIter.next();
if (UtilValidate.isNotEmpty(checkOrderAdjustment.getString("productPromoId")) &&
UtilValidate.isNotEmpty(checkOrderAdjustment.getString("productPromoRuleId")) &&
UtilValidate.isNotEmpty(checkOrderAdjustment.getString("productPromoActionSeqId"))) {
cartAdjustmentIter.remove();
}
}
}
// remove cart lines that are promos (ie GWPs) and cart line adjustments from promo actions
Iterator<ShoppingCartItem> cartItemIter = this.iterator();
while (cartItemIter.hasNext()) {
ShoppingCartItem checkItem = cartItemIter.next();
if (checkItem.getIsPromo()) {
this.clearItemShipInfo(checkItem);
cartItemIter.remove();
} else {
// found a promo item with the productId, see if it has a matching adjustment on it
Iterator<GenericValue> checkOrderAdjustments = UtilMisc.toIterator(checkItem.getAdjustments());
while (checkOrderAdjustments != null && checkOrderAdjustments.hasNext()) {
GenericValue checkOrderAdjustment = checkOrderAdjustments.next();
if (UtilValidate.isNotEmpty(checkOrderAdjustment.getString("productPromoId")) &&
UtilValidate.isNotEmpty(checkOrderAdjustment.getString("productPromoRuleId")) &&
UtilValidate.isNotEmpty(checkOrderAdjustment.getString("productPromoActionSeqId"))) {
checkOrderAdjustments.remove();
}
}
}
}
}
public void clearAllAdjustments() {
// remove all the promotion information (including adjustments)
clearAllPromotionInformation();
// remove all cart adjustments
this.adjustments.clear();
// remove all cart item adjustments
for (ShoppingCartItem checkItem : this) {
checkItem.getAdjustments().clear();
}
}
public void clearAllItemStatus() {
for (ShoppingCartItem item : this) {
item.setStatusId(null);
}
}
/** Adds a promotion code to the cart, checking if it is valid. If it is valid this will return null, otherwise it will return a message stating why it was not valid
* @param productPromoCodeId The promotion code to check and add
* @return String that is null if valid, and added to cart, or an error message of the code was not valid and not added to the cart.
*/
public String addProductPromoCode(String productPromoCodeId, LocalDispatcher dispatcher) {
if (this.productPromoCodes.contains(productPromoCodeId)) {
return UtilProperties.getMessage(resource_error, "productpromoworker.promotion_code_already_been_entered", UtilMisc.toMap("productPromoCodeId", productPromoCodeId), locale);
}
if (!this.getDoPromotions()) {
this.productPromoCodes.add(productPromoCodeId);
return null;
}
// if the promo code requires it make sure the code is valid
String checkResult = ProductPromoWorker.checkCanUsePromoCode(productPromoCodeId, this.getPartyId(), this.getDelegator(), this, locale);
if (checkResult == null) {
this.productPromoCodes.add(productPromoCodeId);
// new promo code, re-evaluate promos
ProductPromoWorker.doPromotions(this, dispatcher);
return null;
}
return checkResult;
}
public Set<String> getProductPromoCodesEntered() {
return this.productPromoCodes;
}
public synchronized void resetPromoRuleUse(String productPromoId, String productPromoRuleId) {
for (ShoppingCartItem cartItem : this) {
cartItem.resetPromoRuleUse(productPromoId, productPromoRuleId);
}
}
public synchronized void confirmPromoRuleUse(String productPromoId, String productPromoRuleId) {
for (ShoppingCartItem cartItem : this) {
cartItem.confirmPromoRuleUse(productPromoId, productPromoRuleId);
}
}
/**
* Associates a party with a role to the order.
* @param partyId identifier of the party to associate to order
* @param roleTypeId identifier of the role used in party-order association
*/
public void addAdditionalPartyRole(String partyId, String roleTypeId) {
// search if there is an existing entry
List<String> parties = additionalPartyRole.get(roleTypeId);
if (parties != null) {
for (String pi : parties) {
if (pi.equals(partyId)) {
return;
}
}
} else {
parties = new ArrayList<>();
additionalPartyRole.put(roleTypeId, parties);
}
parties.add(0, partyId);
}
/**
* Removes a previously associated party to the order.
* @param partyId identifier of the party to associate to order
* @param roleTypeId identifier of the role used in party-order association
*/
public void removeAdditionalPartyRole(String partyId, String roleTypeId) {
List<String> parties = additionalPartyRole.get(roleTypeId);
if (parties != null) {
Iterator<String> it = parties.iterator();
while (it.hasNext()) {
if ((it.next()).equals(partyId)) {
it.remove();
if (parties.isEmpty()) {
additionalPartyRole.remove(roleTypeId);
}
return;
}
}
}
}
public Map<String, List<String>> getAdditionalPartyRoleMap() {
return additionalPartyRole;
}
// =======================================================================
// Methods used for order creation
// =======================================================================
/**
* Returns the Id of an AGGREGATED_CONF product having exact configId.
* If AGGREGATED_CONF product do not exist, creates one, associates it to the AGGREGATED product, and copy its production run template.
* @param item
* @param dispatcher
*/
public String getAggregatedInstanceId (ShoppingCartItem item, LocalDispatcher dispatcher) {
if (UtilValidate.isEmpty(item.getConfigWrapper()) || UtilValidate.isEmpty(item.getConfigWrapper().getConfigId())) {
return null;
}
String newProductId = null;
String configId = item.getConfigWrapper().getConfigId();
try {
//first search for existing productId
newProductId = ProductWorker.getAggregatedInstanceId(getDelegator(), item.getProductId(), configId);
if (newProductId != null) {
return newProductId;
}
Delegator delegator = this.getDelegator();
//create new product and associate it
GenericValue product = item.getProduct();
String productName = product.getString("productName");
String description = product.getString("description");
Map<String, Object> serviceContext = new HashMap<>();
GenericValue permUserLogin = EntityQuery.use(delegator).from("UserLogin").where("userLoginId", "system").queryOne();
String internalName = item.getProductId() + "_" + configId;
serviceContext.put("internalName", internalName);
serviceContext.put("productName", productName);
serviceContext.put("description", description);
if(ProductWorker.isAggregateService(delegator, item.getProductId())) {
serviceContext.put("productTypeId", "AGGREGATEDSERV_CONF");
}
else {
serviceContext.put("productTypeId", "AGGREGATED_CONF");
}
serviceContext.put("configId", configId);
if (UtilValidate.isNotEmpty(product.getString("requirementMethodEnumId"))) {
serviceContext.put("requirementMethodEnumId", product.getString("requirementMethodEnumId"));
}
serviceContext.put("userLogin", permUserLogin);
Map<String, Object> result = dispatcher.runSync("createProduct", serviceContext);
if (ServiceUtil.isError(result)) {
Debug.logError(ServiceUtil.getErrorMessage(result), module);
return null;
}
serviceContext.clear();
newProductId = (String) result.get("productId");
serviceContext.put("productId", item.getProductId());
serviceContext.put("productIdTo", newProductId);
serviceContext.put("productAssocTypeId", "PRODUCT_CONF");
serviceContext.put("fromDate", UtilDateTime.nowTimestamp());
serviceContext.put("userLogin", permUserLogin);
result = dispatcher.runSync("createProductAssoc", serviceContext);
if (ServiceUtil.isError(result)) {
Debug.logError(ServiceUtil.getErrorMessage(result), module);
return null;
}
//create a new WorkEffortGoodStandard based on existing one of AGGREGATED product .
//Another approach could be to get WorkEffortGoodStandard of the AGGREGATED product while creating production run.
GenericValue productionRunTemplate = EntityQuery.use(delegator).from("WorkEffortGoodStandard").where("productId", item.getProductId(), "workEffortGoodStdTypeId", "ROU_PROD_TEMPLATE", "statusId", "WEGS_CREATED").filterByDate().queryFirst();
if (productionRunTemplate != null) {
serviceContext.clear();
serviceContext.put("workEffortId", productionRunTemplate.getString("workEffortId"));
serviceContext.put("productId", newProductId);
serviceContext.put("workEffortGoodStdTypeId", "ROU_PROD_TEMPLATE");
serviceContext.put("statusId", "WEGS_CREATED");
serviceContext.put("userLogin", permUserLogin);
result = dispatcher.runSync("createWorkEffortGoodStandard", serviceContext);
if (ServiceUtil.isError(result)) {
Debug.logError(ServiceUtil.getErrorMessage(result), module);
return null;
}
}
} catch (GenericEntityException | GenericServiceException e) {
Debug.logError(e, module);
return null;
} catch (Exception e) { // SCIPIO: 2018-10-09: Keep Exception here just in case
Debug.logError(e, module);
return null;
}
return newProductId;
}
public List<GenericValue> makeOrderItemGroups() {
List<GenericValue> result = new ArrayList<>();
for (ShoppingCart.ShoppingCartItemGroup itemGroup : this.itemGroupByNumberMap.values()) {
result.add(itemGroup.makeOrderItemGroup(this.getDelegator()));
}
return result;
}
private void explodeItems(LocalDispatcher dispatcher) {
if (dispatcher == null) {
return;
}
synchronized (cartLines) {
List<ShoppingCartItem> cartLineItems = new ArrayList<>(cartLines);
for (ShoppingCartItem item : cartLineItems) {
try {
int thisIndex = items().indexOf(item);
List<ShoppingCartItem> explodedItems = item.explodeItem(this, dispatcher);
// Add exploded items into cart with order item sequence id and item ship group quantity
for (ShoppingCartItem explodedItem : explodedItems) {
String orderItemSeqId = UtilFormatOut.formatPaddedNumber(nextItemSeq, 5);
explodedItem.setOrderItemSeqId(orderItemSeqId);
addItemToEnd(explodedItem);
setItemShipGroupQty(explodedItem, BigDecimal.ONE, thisIndex);
nextItemSeq++;
}
} catch (CartItemModifyException e) {
Debug.logError(e, "Problem exploding item! Item not exploded.", module);
}
}
}
}
/**
* Does an "explode", or "unitize" operation on a list of cart items.
* Resulting state for each item with quantity X is X items of quantity 1.
*
* @param shoppingCartItems
* @param dispatcher
*/
public void explodeItems(List<ShoppingCartItem> shoppingCartItems, LocalDispatcher dispatcher) {
if (dispatcher == null) {
return;
}
synchronized (cartLines) {
for (ShoppingCartItem item : shoppingCartItems) {
try {
int thisIndex = items().indexOf(item);
List<ShoppingCartItem> explodedItems = item.explodeItem(this, dispatcher);
// Add exploded items into cart with order item sequence id and item ship group quantity
for (ShoppingCartItem explodedItem : explodedItems) {
String orderItemSeqId = UtilFormatOut.formatPaddedNumber(nextItemSeq, 5);
explodedItem.setOrderItemSeqId(orderItemSeqId);
addItemToEnd(explodedItem);
setItemShipGroupQty(explodedItem, BigDecimal.ONE, thisIndex);
nextItemSeq++;
}
} catch (CartItemModifyException e) {
Debug.logError(e, "Problem exploding (unitizing) item! Item not exploded.", module);
}
}
}
}
public List<GenericValue> makeOrderItems() {
return makeOrderItems(false, false, null);
}
public List<GenericValue> makeOrderItems(LocalDispatcher dispatcher) {
return makeOrderItems(false, false, dispatcher);
}
public List<GenericValue> makeOrderItems(boolean explodeItems, boolean replaceAggregatedId, LocalDispatcher dispatcher) {
// do the explosion
if (explodeItems && dispatcher != null) {
explodeItems(dispatcher);
}
// now build the lines
synchronized (cartLines) {
List<GenericValue> result = new ArrayList<>();
for (ShoppingCartItem item : cartLines) {
if (UtilValidate.isEmpty(item.getOrderItemSeqId())) {
String orderItemSeqId = UtilFormatOut.formatPaddedNumber(nextItemSeq, 5);
item.setOrderItemSeqId(orderItemSeqId);
nextItemSeq++;
} else {
try {
int thisSeqId = Integer.parseInt(item.getOrderItemSeqId());
if (thisSeqId > nextItemSeq) {
nextItemSeq = thisSeqId + 1;
}
} catch (NumberFormatException e) {
Debug.logError(e, module);
}
}
// the initial status for all item types
String initialStatus = "ITEM_CREATED";
String status = item.getStatusId();
if (status == null) {
status = initialStatus;
}
//check for aggregated products
String aggregatedInstanceId = null;
if (replaceAggregatedId && UtilValidate.isNotEmpty(item.getConfigWrapper())) {
aggregatedInstanceId = getAggregatedInstanceId(item, dispatcher);
}
GenericValue orderItem = getDelegator().makeValue("OrderItem");
orderItem.set("orderItemSeqId", item.getOrderItemSeqId());
orderItem.set("externalId", item.getExternalId());
orderItem.set("orderItemTypeId", item.getItemType());
if (item.getItemGroup() != null) {
orderItem.set("orderItemGroupSeqId", item.getItemGroup().getGroupNumber());
}
orderItem.set("productId", UtilValidate.isNotEmpty(aggregatedInstanceId) ? aggregatedInstanceId : item.getProductId());
orderItem.set("supplierProductId", item.getSupplierProductId());
orderItem.set("prodCatalogId", item.getProdCatalogId());
orderItem.set("productCategoryId", item.getProductCategoryId());
orderItem.set("quantity", item.getQuantity());
orderItem.set("selectedAmount", item.getSelectedAmount());
orderItem.set("unitPrice", item.getBasePrice());
orderItem.set("unitListPrice", item.getListPrice());
orderItem.set("isModifiedPrice",item.getIsModifiedPrice() ? "Y" : "N");
orderItem.set("isPromo", item.getIsPromo() ? "Y" : "N");
orderItem.set("shoppingListId", item.getShoppingListId());
orderItem.set("shoppingListItemSeqId", item.getShoppingListItemSeqId());
orderItem.set("itemDescription", item.getName());
orderItem.set("comments", item.getItemComment());
orderItem.set("estimatedDeliveryDate", item.getDesiredDeliveryDate());
orderItem.set("correspondingPoId", this.getPoNumber());
orderItem.set("quoteId", item.getQuoteId());
orderItem.set("quoteItemSeqId", item.getQuoteItemSeqId());
orderItem.set("statusId", status);
orderItem.set("shipBeforeDate", item.getShipBeforeDate());
orderItem.set("shipAfterDate", item.getShipAfterDate());
orderItem.set("estimatedShipDate", item.getEstimatedShipDate());
orderItem.set("cancelBackOrderDate", item.getCancelBackOrderDate());
if (this.getUserLogin() != null) {
orderItem.set("changeByUserLoginId", this.getUserLogin().get("userLoginId"));
}
String fromInventoryItemId = (String) item.getAttribute("fromInventoryItemId");
if (fromInventoryItemId != null) {
orderItem.set("fromInventoryItemId", fromInventoryItemId);
}
result.add(orderItem);
// don't do anything with adjustments here, those will be added below in makeAllAdjustments
}
return result;
}
}
/**
* SCIPIO: Returns the ProductConfigWrapper for each order item.
* WARNING: This ONLY works if the order items have already been expanded, using {@link #makeOrderItems} or other,
* otherwise there will be no orderItemSeqIds to use!
*/
public Map<String, ProductConfigWrapper> getProductConfigWrappersByOrderItemSeqId() {
Map<String, ProductConfigWrapper> result = new HashMap<>();
for (ShoppingCartItem item : cartLines) {
if (item.getOrderItemSeqId() != null && item.getConfigWrapper() != null) {
result.put(item.getOrderItemSeqId(), item.getConfigWrapper());
}
}
return result;
}
/** create WorkEfforts from the shoppingcart items when itemType = RENTAL_ORDER_ITEM */
public List<GenericValue> makeWorkEfforts() {
List<GenericValue> allWorkEfforts = new ArrayList<>();
for (ShoppingCartItem item : cartLines) {
if ("RENTAL_ORDER_ITEM".equals(item.getItemType())) { // prepare workeffort when the order item is a rental item
GenericValue workEffort = getDelegator().makeValue("WorkEffort");
workEffort.set("workEffortId",item.getOrderItemSeqId()); // fill temporary with sequence number
workEffort.set("estimatedStartDate",item.getReservStart());
workEffort.set("estimatedCompletionDate",item.getReservStart(item.getReservLength()));
workEffort.set("reservPersons", item.getReservPersons());
workEffort.set("reserv2ndPPPerc", item.getReserv2ndPPPerc());
workEffort.set("reservNthPPPerc", item.getReservNthPPPerc());
workEffort.set("accommodationMapId", item.getAccommodationMapId());
workEffort.set("accommodationSpotId",item.getAccommodationSpotId());
allWorkEfforts.add(workEffort);
}
}
return allWorkEfforts;
}
/** make a list of all adjustments including order adjustments, order line adjustments, and special adjustments (shipping and tax if applicable) */
public List<GenericValue> makeAllAdjustments() {
List<GenericValue> allAdjs = new ArrayList<>();
// before returning adjustments, go through them to find all that need counter adjustments (for instance: free shipping)
for (GenericValue orderAdjustment: this.getAdjustments()) {
allAdjs.add(orderAdjustment);
if ("SHIPPING_CHARGES".equals(orderAdjustment.get("orderAdjustmentTypeId"))) {
Iterator<GenericValue> fsppas = this.freeShippingProductPromoActions.iterator();
while (fsppas.hasNext()) {
// TODO - we need to change the way free shipping promotions work
}
}
}
// add all of the item adjustments to this list too
for (ShoppingCartItem item : cartLines) {
Collection<GenericValue> adjs = item.getAdjustments();
if (adjs != null) {
for (GenericValue orderAdjustment: adjs) {
orderAdjustment.set("orderItemSeqId", item.getOrderItemSeqId());
allAdjs.add(orderAdjustment);
if ("SHIPPING_CHARGES".equals(orderAdjustment.get("orderAdjustmentTypeId"))) {
Iterator<GenericValue> fsppas = this.freeShippingProductPromoActions.iterator();
while (fsppas.hasNext()) {
// TODO - fix the free shipping promotions!!
}
}
}
}
}
return allAdjs;
}
/** make a list of all quote adjustments including header adjustments, line adjustments, and special adjustments (shipping and tax if applicable).
* Internally, the quote adjustments are created from the order adjustments.
*/
public List<GenericValue> makeAllQuoteAdjustments() {
List<GenericValue> quoteAdjs = new ArrayList<>();
for (GenericValue orderAdj: makeAllAdjustments()) {
GenericValue quoteAdj = this.getDelegator().makeValue("QuoteAdjustment");
quoteAdj.put("quoteAdjustmentId", orderAdj.get("orderAdjustmentId"));
quoteAdj.put("quoteAdjustmentTypeId", orderAdj.get("orderAdjustmentTypeId"));
quoteAdj.put("quoteItemSeqId", orderAdj.get("orderItemSeqId"));
quoteAdj.put("comments", orderAdj.get("comments"));
quoteAdj.put("description", orderAdj.get("description"));
quoteAdj.put("amount", orderAdj.get("amount"));
quoteAdj.put("productPromoId", orderAdj.get("productPromoId"));
quoteAdj.put("productPromoRuleId", orderAdj.get("productPromoRuleId"));
quoteAdj.put("productPromoActionSeqId", orderAdj.get("productPromoActionSeqId"));
quoteAdj.put("productFeatureId", orderAdj.get("productFeatureId"));
quoteAdj.put("correspondingProductId", orderAdj.get("correspondingProductId"));
quoteAdj.put("sourceReferenceId", orderAdj.get("sourceReferenceId"));
quoteAdj.put("sourcePercentage", orderAdj.get("sourcePercentage"));
quoteAdj.put("customerReferenceId", orderAdj.get("customerReferenceId"));
quoteAdj.put("primaryGeoId", orderAdj.get("primaryGeoId"));
quoteAdj.put("secondaryGeoId", orderAdj.get("secondaryGeoId"));
quoteAdj.put("exemptAmount", orderAdj.get("exemptAmount"));
quoteAdj.put("taxAuthGeoId", orderAdj.get("taxAuthGeoId"));
quoteAdj.put("taxAuthPartyId", orderAdj.get("taxAuthPartyId"));
quoteAdj.put("overrideGlAccountId", orderAdj.get("overrideGlAccountId"));
quoteAdj.put("includeInTax", orderAdj.get("includeInTax"));
quoteAdj.put("includeInShipping", orderAdj.get("includeInShipping"));
quoteAdj.put("createdDate", orderAdj.get("createdDate"));
quoteAdj.put("createdByUserLogin", orderAdj.get("createdByUserLogin"));
quoteAdjs.add(quoteAdj);
}
return quoteAdjs;
}
/** make a list of all OrderPaymentPreferences and Billing info including all payment methods and types */
public List<GenericValue> makeAllOrderPaymentInfos(LocalDispatcher dispatcher) {
Delegator delegator = this.getDelegator();
List<GenericValue> allOpPrefs = new ArrayList<>();
BigDecimal remainingAmount = this.getGrandTotal().subtract(this.getPaymentTotal());
remainingAmount = remainingAmount.setScale(2, RoundingMode.HALF_UP);
if (getBillingAccountId() != null && this.billingAccountAmt.compareTo(BigDecimal.ZERO) <= 0) {
BigDecimal billingAccountAvailableAmount = CheckOutHelper.availableAccountBalance(getBillingAccountId(), dispatcher);
if (this.billingAccountAmt.compareTo(BigDecimal.ZERO) == 0 && billingAccountAvailableAmount.compareTo(BigDecimal.ZERO) > 0) {
this.billingAccountAmt = billingAccountAvailableAmount;
}
if (remainingAmount.compareTo(getBillingAccountAmount()) < 0) {
this.billingAccountAmt = remainingAmount;
}
if (billingAccountAvailableAmount.compareTo(getBillingAccountAmount()) < 0) {
this.billingAccountAmt = billingAccountAvailableAmount;
}
}
for (CartPaymentInfo inf : paymentInfo) {
if (inf.amount == null) {
inf.amount = remainingAmount;
remainingAmount = BigDecimal.ZERO;
}
allOpPrefs.addAll(inf.makeOrderPaymentInfos(delegator, this));
}
return allOpPrefs;
}
/** make a list of OrderItemPriceInfos from the ShoppingCartItems */
public List<GenericValue> makeAllOrderItemPriceInfos() {
List<GenericValue> allInfos = new ArrayList<>();
// add all of the item adjustments to this list too
for (ShoppingCartItem item : cartLines) {
Collection<GenericValue> infos = item.getOrderItemPriceInfos();
if (infos != null) {
for (GenericValue orderItemPriceInfo : infos) {
orderItemPriceInfo.set("orderItemSeqId", item.getOrderItemSeqId());
allInfos.add(orderItemPriceInfo);
}
}
}
return allInfos;
}
public List<GenericValue> makeProductPromoUses() {
List<GenericValue> productPromoUses = new ArrayList<>();
String partyId = this.getPartyId();
int sequenceValue = 0;
for (ProductPromoUseInfo productPromoUseInfo: this.productPromoUseInfoList) {
GenericValue productPromoUse = this.getDelegator().makeValue("ProductPromoUse");
productPromoUse.set("promoSequenceId", UtilFormatOut.formatPaddedNumber(sequenceValue, 5));
productPromoUse.set("productPromoId", productPromoUseInfo.getProductPromoId());
productPromoUse.set("productPromoCodeId", productPromoUseInfo.getProductPromoCodeId());
productPromoUse.set("totalDiscountAmount", productPromoUseInfo.getTotalDiscountAmount());
productPromoUse.set("quantityLeftInActions", productPromoUseInfo.getQuantityLeftInActions());
productPromoUse.set("partyId", partyId);
productPromoUses.add(productPromoUse);
sequenceValue++;
}
return productPromoUses;
}
/** make a list of SurveyResponse object to update with order information set */
public List<GenericValue> makeAllOrderItemSurveyResponses() {
List<GenericValue> allInfos = new ArrayList<>();
for (ShoppingCartItem item : this) {
List<String> responses = UtilGenerics.checkList(item.getAttribute("surveyResponses"));
GenericValue response = null;
if (responses != null) {
for (String responseId : responses) {
try {
response = this.getDelegator().findOne("SurveyResponse", UtilMisc.toMap("surveyResponseId", responseId), false);
} catch (GenericEntityException e) {
Debug.logError(e, "Unable to obtain SurveyResponse record for ID : " + responseId, module);
}
}
// this case is executed when user selects "Create as new Order" for Gift cards
} else {
String surveyResponseId = (String) item.getAttribute("surveyResponseId");
try {
response = this.getDelegator().findOne("SurveyResponse", UtilMisc.toMap("surveyResponseId", surveyResponseId), false);
} catch (GenericEntityException e) {
Debug.logError(e, "Unable to obtain SurveyResponse record for ID : " + surveyResponseId, module);
}
}
if (response != null) {
response.set("orderItemSeqId", item.getOrderItemSeqId());
allInfos.add(response);
}
}
return allInfos;
}
/** SCIPIO: make a map of orderItemSeqId to SurveyResponse */
public Map<String, List<GenericValue>> makeAllOrderItemSurveyResponsesByOrderItemSeqId() {
Map<String, List<GenericValue>> allInfos = new HashMap<>();
for (ShoppingCartItem item : this) {
List<GenericValue> surveyResponses = item.getSurveyResponses();
// TODO: REVIEW: Is it safe or a good idea to set orderId and/or orderItemSeqId here?
// It might be used as an "order not placed yet" flag somewhere....
//response.set("orderItemSeqId", item.getOrderItemSeqId());
if (UtilValidate.isNotEmpty(surveyResponses)) {
allInfos.put(item.getOrderItemSeqId(), surveyResponses);
}
}
return allInfos;
}
/** make a list of OrderContactMechs from the ShoppingCart and the ShoppingCartItems */
public List<GenericValue> makeAllOrderContactMechs() {
List<GenericValue> allOrderContactMechs = new ArrayList<>();
Map<String, String> contactMechIds = this.getOrderContactMechIds();
if (contactMechIds != null) {
for (Map.Entry<String, String> entry : contactMechIds.entrySet()) {
GenericValue orderContactMech = getDelegator().makeValue("OrderContactMech");
orderContactMech.set("contactMechPurposeTypeId", entry.getKey());
orderContactMech.set("contactMechId", entry.getValue());
allOrderContactMechs.add(orderContactMech);
}
}
return allOrderContactMechs;
}
/** make a list of OrderContactMechs from the ShoppingCart and the ShoppingCartItems */
public List<GenericValue> makeAllOrderItemContactMechs() {
List<GenericValue> allOrderContactMechs = new ArrayList<>();
for (ShoppingCartItem item : cartLines) {
Map<String, String> itemContactMechIds = item.getOrderItemContactMechIds();
if (itemContactMechIds != null) {
for (Map.Entry<String, String> entry: itemContactMechIds.entrySet()) {
GenericValue orderContactMech = getDelegator().makeValue("OrderItemContactMech");
orderContactMech.set("contactMechPurposeTypeId", entry.getKey());
orderContactMech.set("contactMechId", entry.getValue());
orderContactMech.set("orderItemSeqId", item.getOrderItemSeqId());
allOrderContactMechs.add(orderContactMech);
}
}
}
return allOrderContactMechs;
}
/**
* Return all OrderItemShipGroup, OrderContactMech, OrderAdjustment and OrderItemShipGroupAssoc from ShoppingCart
* in a single list of {@link GenericValue}
*/
public List<GenericValue> makeAllShipGroupInfos() {
List<GenericValue> groups = new ArrayList<>();
long seqId = 1;
for (CartShipInfo csi : this.shipInfo) {
String shipGroupSeqId = csi.shipGroupSeqId;
if (shipGroupSeqId != null) {
groups.addAll(csi.makeItemShipGroupAndAssoc(this.getDelegator(), this, shipGroupSeqId));
} else {
groups.addAll(csi.makeItemShipGroupAndAssoc(this.getDelegator(), this, UtilFormatOut.formatPaddedNumber(seqId, 5), true));
}
seqId++;
}
return groups;
}
public int getShipInfoSize() {
return this.shipInfo.size();
}
public Map<String, List<GenericValue>> makeAllOrderItemAttributesByOrderItemSeqId() { // SCIPIO
return UtilMisc.groupMapsByKey(makeAllOrderItemAttributes(), "orderItemSeqId");
}
public List<GenericValue> makeAllOrderItemAttributes() {
return makeAllOrderItemAttributes(null, ALL);
}
public List<GenericValue> makeAllOrderItemAttributes(String orderId, int mode) {
// now build order item attributes
synchronized (cartLines) {
List<GenericValue> result = new ArrayList<>();
for (ShoppingCartItem item : cartLines) {
Map<String, String> orderItemAttributes = item.getOrderItemAttributes();
for (Entry<String, String> entry : orderItemAttributes.entrySet()) {
String value = entry.getValue();
String key = entry.getKey();
if (ALL == mode || (FILLED_ONLY == mode && UtilValidate.isNotEmpty(value)) || (EMPTY_ONLY == mode && UtilValidate.isEmpty(value))
|| (mode != ALL && mode != FILLED_ONLY && mode != EMPTY_ONLY)) {
GenericValue orderItemAttribute = getDelegator().makeValue("OrderItemAttribute");
if (UtilValidate.isNotEmpty(orderId)) {
orderItemAttribute.set("orderId", orderId);
}
orderItemAttribute.set("orderItemSeqId", item.getOrderItemSeqId());
orderItemAttribute.set("attrName", key);
orderItemAttribute.set("attrValue", value);
result.add(orderItemAttribute);
}
}
}
return result;
}
}
public List<GenericValue> makeAllOrderAttributes() {
return makeAllOrderAttributes(null, ALL);
}
public List<GenericValue> makeAllOrderAttributes(String orderId, int mode) {
List<GenericValue> allOrderAttributes = new ArrayList<>();
for (Map.Entry<String, String> entry: orderAttributes.entrySet()) {
GenericValue orderAtt = this.getDelegator().makeValue("OrderAttribute");
if (UtilValidate.isNotEmpty(orderId)) {
orderAtt.set("orderId", orderId);
}
String key = entry.getKey();
String value = entry.getValue();
orderAtt.put("attrName", key);
orderAtt.put("attrValue", value);
switch (mode) {
case FILLED_ONLY:
if (UtilValidate.isNotEmpty(value)) {
allOrderAttributes.add(orderAtt);
}
break;
case EMPTY_ONLY:
if (UtilValidate.isEmpty(value)) {
allOrderAttributes.add(orderAtt);
}
break;
case ALL:
default:
allOrderAttributes.add(orderAtt);
break;
}
}
return allOrderAttributes;
}
public List<GenericValue> makeAllOrderItemAssociations() {
List<GenericValue> allOrderItemAssociations = new ArrayList<>();
for (CartShipInfo csi : shipInfo) {
Set<ShoppingCartItem> items = csi.getShipItems();
for (ShoppingCartItem item : items) {
String requirementId = item.getRequirementId();
if (requirementId != null) {
try {
// TODO: multiple commitments for the same requirement are still not supported
GenericValue commitment = EntityQuery.use(getDelegator())
.from("OrderRequirementCommitment")
.where("requirementId", requirementId)
.queryFirst();
if (commitment != null) {
GenericValue orderItemAssociation = getDelegator().makeValue("OrderItemAssoc");
orderItemAssociation.set("orderId", commitment.getString("orderId"));
orderItemAssociation.set("orderItemSeqId", commitment.getString("orderItemSeqId"));
orderItemAssociation.set("shipGroupSeqId", "_NA_");
orderItemAssociation.set("toOrderItemSeqId", item.getOrderItemSeqId());
orderItemAssociation.set("toShipGroupSeqId", "_NA_");
orderItemAssociation.set("orderItemAssocTypeId", "PURCHASE_ORDER");
allOrderItemAssociations.add(orderItemAssociation);
}
} catch (GenericEntityException e) {
Debug.logError(e, "Unable to load OrderRequirementCommitment records for requirement ID : " + requirementId, module);
}
}
if (item.getAssociatedOrderId() != null && item.getAssociatedOrderItemSeqId() != null) {
GenericValue orderItemAssociation = getDelegator().makeValue("OrderItemAssoc");
orderItemAssociation.set("orderId", item.getAssociatedOrderId());
orderItemAssociation.set("orderItemSeqId", item.getAssociatedOrderItemSeqId());
orderItemAssociation.set("shipGroupSeqId", csi.getAssociatedShipGroupSeqId() != null ? csi.getAssociatedShipGroupSeqId() : "_NA_");
orderItemAssociation.set("toOrderItemSeqId", item.getOrderItemSeqId());
orderItemAssociation.set("toShipGroupSeqId", csi.getShipGroupSeqId() != null ? csi.getShipGroupSeqId() : "_NA_");
orderItemAssociation.set("orderItemAssocTypeId", item.getOrderItemAssocTypeId());
allOrderItemAssociations.add(orderItemAssociation);
}
}
}
return allOrderItemAssociations;
}
/** Returns a Map of cart values to pass to the storeOrder service */
public Map<String, Object> makeCartMap(LocalDispatcher dispatcher, boolean explodeItems) {
Map<String, Object> result = new HashMap<>();
result.put("orderTypeId", this.getOrderType());
result.put("orderName", this.getOrderName());
result.put("externalId", this.getExternalId());
result.put("orderDate", this.getOrderDate());
result.put("internalCode", this.getInternalCode());
result.put("salesChannelEnumId", this.getChannelType());
result.put("orderItemGroups", this.makeOrderItemGroups());
result.put("orderItems", this.makeOrderItems(explodeItems, Boolean.TRUE, dispatcher));
result.put("workEfforts", this.makeWorkEfforts());
result.put("orderAdjustments", this.makeAllAdjustments());
result.put("orderTerms", this.getOrderTerms());
result.put("orderItemPriceInfos", this.makeAllOrderItemPriceInfos());
result.put("orderProductPromoUses", this.makeProductPromoUses());
result.put("orderProductPromoCodes", this.getProductPromoCodesEntered());
result.put("orderAttributes", this.makeAllOrderAttributes());
result.put("orderItemAttributes", this.makeAllOrderItemAttributes());
result.put("orderContactMechs", this.makeAllOrderContactMechs());
result.put("orderItemContactMechs", this.makeAllOrderItemContactMechs());
result.put("orderPaymentInfo", this.makeAllOrderPaymentInfos(dispatcher));
result.put("orderItemShipGroupInfo", this.makeAllShipGroupInfos());
result.put("orderItemSurveyResponses", this.makeAllOrderItemSurveyResponses());
result.put("orderAdditionalPartyRoleMap", this.getAdditionalPartyRoleMap());
result.put("orderItemAssociations", this.makeAllOrderItemAssociations());
result.put("orderInternalNotes", this.getInternalOrderNotes());
result.put("orderNotes", this.getOrderNotes());
result.put("firstAttemptOrderId", this.getFirstAttemptOrderId());
result.put("currencyUom", this.getCurrency());
result.put("billingAccountId", this.getBillingAccountId());
result.put("partyId", this.getPartyId());
result.put("productStoreId", this.getProductStoreId());
result.put("transactionId", this.getTransactionId());
result.put("originFacilityId", this.getFacilityId());
result.put("terminalId", this.getTerminalId());
result.put("workEffortId", this.getWorkEffortId());
result.put("autoOrderShoppingListId", this.getAutoOrderShoppingListId());
result.put("billToCustomerPartyId", this.getBillToCustomerPartyId());
result.put("billFromVendorPartyId", this.getBillFromVendorPartyId());
if (this.isSalesOrder()) {
result.put("placingCustomerPartyId", this.getPlacingCustomerPartyId());
result.put("shipToCustomerPartyId", this.getShipToCustomerPartyId());
result.put("endUserCustomerPartyId", this.getEndUserCustomerPartyId());
}
if (this.isPurchaseOrder()) {
result.put("shipFromVendorPartyId", this.getShipFromVendorPartyId());
result.put("supplierAgentPartyId", this.getSupplierAgentPartyId());
}
return result;
}
public List<ShoppingCartItem> getLineListOrderedByBasePrice(boolean ascending) {
List<ShoppingCartItem> result = new ArrayList<>(this.cartLines);
Collections.sort(result, new BasePriceOrderComparator(ascending));
return result;
}
public TreeMap<Integer, CartShipInfo> getShipGroupsBySupplier(String supplierPartyId) {
TreeMap<Integer, CartShipInfo> shipGroups = new TreeMap<>();
for (int i = 0; i < this.shipInfo.size(); i++) {
CartShipInfo csi = shipInfo.get(i);
if ((csi.supplierPartyId == null && supplierPartyId == null) ||
(UtilValidate.isNotEmpty(csi.supplierPartyId) && csi.supplierPartyId.equals(supplierPartyId))) {
shipGroups.put(i, csi);
}
}
return shipGroups;
}
/**
* Examine each item of each ship group and create new ship groups if the item should be drop shipped
* @param dispatcher
* @throws CartItemModifyException
*/
public void createDropShipGroups(LocalDispatcher dispatcher) throws CartItemModifyException {
// Retrieve the facilityId from the cart's productStoreId because ShoppingCart.setFacilityId() doesn't seem to be used anywhere
String facilityId = null;
if (UtilValidate.isNotEmpty(this.getProductStoreId())) {
try {
GenericValue productStore = this.getDelegator().findOne("ProductStore", UtilMisc.toMap("productStoreId", this.getProductStoreId()), true);
facilityId = productStore.getString("inventoryFacilityId");
} catch (GenericEntityException gee) {
Debug.logError(UtilProperties.getMessage(resource_error,"OrderProblemGettingProductStoreRecords", Debug.getLogLocale()) + gee.getMessage(), module); // SCIPIO: log locale
return;
} catch (Exception e) {
Debug.logError(UtilProperties.getMessage(resource_error,"OrderProblemGettingProductStoreRecords", Debug.getLogLocale()) + e.getMessage(), module); // SCIPIO: log locale
return;
}
}
List<CartShipInfo> shipGroups = getShipGroups();
if (shipGroups == null) {
return;
}
// Intermediate structure supplierPartyId -> { ShoppingCartItem = { originalShipGroupIndex = dropShipQuantity } } to collect drop-shippable items
Map<String, Map<ShoppingCartItem, Map<Integer, BigDecimal>>> dropShipItems = new HashMap<>();
for (int shipGroupIndex = 0; shipGroupIndex < shipGroups.size(); shipGroupIndex++) {
CartShipInfo shipInfo = shipGroups.get(shipGroupIndex);
// Ignore ship groups that are already drop shipped
String shipGroupSupplierPartyId = shipInfo.getSupplierPartyId();
if (UtilValidate.isNotEmpty(shipGroupSupplierPartyId)) {
continue;
}
// Ignore empty ship groups
Set<ShoppingCartItem> shipItems = shipInfo.getShipItems();
if (UtilValidate.isEmpty(shipItems)) {
continue;
}
for (ShoppingCartItem cartItem : shipItems) {
BigDecimal itemQuantity = cartItem.getQuantity();
BigDecimal dropShipQuantity = BigDecimal.ZERO;
GenericValue product = cartItem.getProduct();
if (product == null) {
continue;
}
String productId = product.getString("productId");
String requirementMethodEnumId = product.getString("requirementMethodEnumId");
if ("PRODRQM_DS".equals(requirementMethodEnumId)) {
// Drop ship the full quantity if the product is marked drop-ship only
dropShipQuantity = itemQuantity;
} else if ("PRODRQM_DSATP".equals(requirementMethodEnumId)) {
// Drop ship the quantity not available in inventory if the product is marked drop-ship on low inventory
try {
// Get ATP for the product
Map<String, Object> getProductInventoryAvailableResult = dispatcher.runSync("getInventoryAvailableByFacility", UtilMisc.toMap("productId", productId, "facilityId", facilityId));
if (ServiceUtil.isError(getProductInventoryAvailableResult)) {
String errorMessage = ServiceUtil.getErrorMessage(getProductInventoryAvailableResult);
Debug.logError(errorMessage, module);
return;
}
BigDecimal availableToPromise = (BigDecimal) getProductInventoryAvailableResult.get("availableToPromiseTotal");
if (itemQuantity.compareTo(availableToPromise) <= 0) {
dropShipQuantity = BigDecimal.ZERO;
} else {
dropShipQuantity = itemQuantity.subtract(availableToPromise);
}
} catch (GenericServiceException gee) {
Debug.logWarning(UtilProperties.getMessage(resource_error,"OrderRunServiceGetInventoryAvailableByFacilityError", Debug.getLogLocale()) + gee.getMessage(), module); // SCIPIO: log locale
} catch (Exception e) {
Debug.logWarning(UtilProperties.getMessage(resource_error,"OrderRunServiceGetInventoryAvailableByFacilityError", Debug.getLogLocale()) + e.getMessage(), module); // SCIPIO: log locale
}
} else {
// Don't drop ship anything if the product isn't so marked
dropShipQuantity = BigDecimal.ZERO;
}
if (dropShipQuantity.compareTo(BigDecimal.ZERO) <= 0) {
continue;
}
// Find a supplier for the product
String supplierPartyId = null;
try {
Map<String, Object> getSuppliersForProductResult = dispatcher.runSync("getSuppliersForProduct", UtilMisc.<String, Object>toMap("productId", productId, "quantity", dropShipQuantity, "canDropShip", "Y", "currencyUomId", getCurrency()));
if (ServiceUtil.isError(getSuppliersForProductResult)) {
String errorMessage = ServiceUtil.getErrorMessage(getSuppliersForProductResult);
Debug.logError(errorMessage, module);
return;
}
List<GenericValue> supplierProducts = UtilGenerics.checkList(getSuppliersForProductResult.get("supplierProducts"));
// Order suppliers by supplierPrefOrderId so that preferred suppliers are used first
supplierProducts = EntityUtil.orderBy(supplierProducts, UtilMisc.toList("supplierPrefOrderId"));
GenericValue supplierProduct = EntityUtil.getFirst(supplierProducts);
if (! UtilValidate.isEmpty(supplierProduct)) {
supplierPartyId = supplierProduct.getString("partyId");
}
} catch (GenericServiceException e) {
Debug.logWarning(UtilProperties.getMessage(resource_error,"OrderRunServiceGetSuppliersForProductError", Debug.getLogLocale()) + e.getMessage(), module); // SCIPIO: log locale
}
// Leave the items untouched if we couldn't find a supplier
if (UtilValidate.isEmpty(supplierPartyId)) {
continue;
}
if (! dropShipItems.containsKey(supplierPartyId)) {
dropShipItems.put(supplierPartyId, new HashMap<ShoppingCartItem, Map<Integer, BigDecimal>>());
}
Map<ShoppingCartItem, Map<Integer, BigDecimal>> supplierCartItems = UtilGenerics.checkMap(dropShipItems.get(supplierPartyId));
if (! supplierCartItems.containsKey(cartItem)) {
supplierCartItems.put(cartItem, new HashMap<Integer, BigDecimal>());
}
Map<Integer, BigDecimal> cartItemGroupQuantities = UtilGenerics.checkMap(supplierCartItems.get(cartItem));
cartItemGroupQuantities.put(shipGroupIndex, dropShipQuantity);
}
}
// Reassign the drop-shippable item quantities to new or existing drop-ship groups
for (Entry<String, Map<ShoppingCartItem, Map<Integer, BigDecimal>>> supplierPartyEntry : dropShipItems.entrySet()) {
String supplierPartyId = supplierPartyEntry.getKey();
CartShipInfo shipInfo = null;
int newShipGroupIndex = -1 ;
// Attempt to get the first ship group for the supplierPartyId
TreeMap<Integer, CartShipInfo> supplierShipGroups = this.getShipGroupsBySupplier(supplierPartyId);
if (! UtilValidate.isEmpty(supplierShipGroups)) {
newShipGroupIndex = supplierShipGroups.firstKey();
shipInfo = supplierShipGroups.get(supplierShipGroups.firstKey());
}
if (newShipGroupIndex == -1) {
newShipGroupIndex = addShipInfo();
shipInfo = this.shipInfo.get(newShipGroupIndex);
}
shipInfo.supplierPartyId = supplierPartyId;
Map<ShoppingCartItem, Map<Integer, BigDecimal>> supplierCartItems = UtilGenerics.checkMap(supplierPartyEntry.getValue());
for (Entry<ShoppingCartItem, Map<Integer, BigDecimal>> cartItemEntry : supplierCartItems.entrySet()) {
ShoppingCartItem cartItem = cartItemEntry.getKey();
Map<Integer, BigDecimal> cartItemGroupQuantities = UtilGenerics.checkMap(cartItemEntry.getValue());
for (Entry<Integer, BigDecimal> previousShipGroupIndexEntry : cartItemGroupQuantities.entrySet()) {
Integer previousShipGroupIndex = previousShipGroupIndexEntry.getKey();
BigDecimal dropShipQuantity = previousShipGroupIndexEntry.getValue();
positionItemToGroup(cartItem, dropShipQuantity, previousShipGroupIndex, newShipGroupIndex, true);
}
}
}
}
static class BasePriceOrderComparator implements Comparator<Object>, Serializable {
private boolean ascending = false;
BasePriceOrderComparator(boolean ascending) {
this.ascending = ascending;
}
@Override
public int compare(java.lang.Object obj, java.lang.Object obj1) {
ShoppingCartItem cartItem = (ShoppingCartItem) obj;
ShoppingCartItem cartItem1 = (ShoppingCartItem) obj1;
return this.ascending
? cartItem.getBasePrice().compareTo(cartItem1.getBasePrice())
: cartItem1.getBasePrice().compareTo(cartItem.getBasePrice());
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (ascending ? 1231 : 1237);
return result;
}
@Override
public boolean equals(java.lang.Object obj) {
if (obj instanceof BasePriceOrderComparator) {
return this.ascending == ((BasePriceOrderComparator) obj).ascending;
}
return false;
}
}
/**
* Factory method (SCIPIO).
*/
public ShoppingCartItemGroup newItemGroup(long groupNumber, String groupName) {
return new ShoppingCartItemGroup(groupNumber, groupName);
}
/**
* Factory method (SCIPIO).
*/
public ShoppingCartItemGroup newItemGroup(long groupNumber, String groupName, ShoppingCartItemGroup parentGroup) {
return new ShoppingCartItemGroup(groupNumber, groupName, parentGroup);
}
/**
* Factory method (SCIPIO).
*/
public ShoppingCartItemGroup newItemGroup(String groupNumber, String groupName, ShoppingCartItemGroup parentGroup) {
return new ShoppingCartItemGroup(groupNumber, groupName, parentGroup);
}
public static class ShoppingCartItemGroup implements Serializable {
private String groupNumber;
private String groupName;
private ShoppingCartItemGroup parentGroup;
// don't allow empty constructor
@SuppressWarnings("unused")
private ShoppingCartItemGroup() {}
protected ShoppingCartItemGroup(long groupNumber, String groupName) {
this(groupNumber, groupName, null);
}
/** Note that to avoid foreign key issues when the groups are created a parentGroup should have a lower number than the child group. */
protected ShoppingCartItemGroup(long groupNumber, String groupName, ShoppingCartItemGroup parentGroup) {
this(UtilFormatOut.formatPaddedNumber(groupNumber, 2), groupName, parentGroup);
}
protected ShoppingCartItemGroup(String groupNumber, String groupName, ShoppingCartItemGroup parentGroup) {
this.groupNumber = groupNumber;
this.groupName = groupName;
this.parentGroup = parentGroup;
}
protected ShoppingCartItemGroup(ShoppingCartItemGroup itemGroup, ShoppingCartItemGroup parentGroup) {
this.groupNumber = itemGroup.groupNumber;
this.groupName = itemGroup.groupName;
this.parentGroup = parentGroup;
}
/** SCIPIO: Performs a copy of the instance. Added 2019-09-05. */
public ShoppingCartItemGroup copy(boolean exactCopy, ShoppingCartItemGroup parentGroup) {
return new ShoppingCartItemGroup(this, parentGroup);
}
/**
* SCIPIO: Tests to ensure the cart is an exact copy of the other; used to verify {@link #copy}.
* NOTE: This is NOT the same as a logical Object equals override! This is mainly for testing.
*/
protected void ensureExactEquals(ShoppingCartItemGroup other, List<String> errorMessages) {
ShoppingCart.ensureExactEquals(this.groupNumber, other.groupNumber, "ShoppingCartItemGroup.groupNumber", errorMessages);
ShoppingCart.ensureExactEquals(this.groupName, other.groupName, "ShoppingCartItemGroup.groupName", errorMessages);
ShoppingCart.ensureExactEquals(this.parentGroup, other.parentGroup, "ShoppingCartItemGroup.parentGroup", errorMessages);
}
public String getGroupNumber() {
return this.groupNumber;
}
public String getGroupName() {
return this.groupName;
}
public void setGroupName(String str) {
this.groupName = str;
}
public ShoppingCartItemGroup getParentGroup () {
return this.parentGroup;
}
protected GenericValue makeOrderItemGroup(Delegator delegator) {
GenericValue orderItemGroup = delegator.makeValue("OrderItemGroup");
orderItemGroup.set("orderItemGroupSeqId", this.getGroupNumber());
orderItemGroup.set("groupName", this.getGroupName());
if (this.parentGroup != null) {
orderItemGroup.set("parentGroupSeqId", this.parentGroup.getGroupNumber());
}
return orderItemGroup;
}
public void inheritParentsParent() {
if (this.parentGroup != null) {
this.parentGroup = this.parentGroup.getParentGroup();
}
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
// SCIPIO: 2018-10-09: TODO: REVIEW: not part of equals, so can't be here - should be in equals?
//result = prime * result + ((groupName == null) ? 0 : groupName.hashCode());
result = prime * result + ((groupNumber == null) ? 0 : groupNumber.hashCode());
// SCIPIO: 2018-10-09: TODO: REVIEW: not part of equals, so can't be here - should be in equals?
//result = prime * result + ((parentGroup == null) ? 0 : parentGroup.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof ShoppingCartItemGroup) {
ShoppingCartItemGroup that = (ShoppingCartItemGroup) obj;
if (that.groupNumber.equals(this.groupNumber)) {
return true;
}
}
return false;
}
}
public static class ProductPromoUseInfo implements Serializable, Comparable<ProductPromoUseInfo> {
// SCIPIO: 2018-11-12: All fields now final and not public
protected final String productPromoId;
protected final String productPromoCodeId;
protected final BigDecimal totalDiscountAmount; // = BigDecimal.ZERO
protected final BigDecimal quantityLeftInActions; // = BigDecimal.ZERO
private final Map<ShoppingCartItem, BigDecimal> usageInfoMap;
public ProductPromoUseInfo(String productPromoId, String productPromoCodeId, BigDecimal totalDiscountAmount, BigDecimal quantityLeftInActions, Map<ShoppingCartItem,BigDecimal> usageInfoMap) {
this.productPromoId = productPromoId;
this.productPromoCodeId = productPromoCodeId;
this.totalDiscountAmount = totalDiscountAmount;
this.quantityLeftInActions = quantityLeftInActions;
this.usageInfoMap = (usageInfoMap != null) ? Collections.unmodifiableMap(usageInfoMap) : null; // SCIPIO: unmodifiableMap
}
/**
* SCIPIO: Copy constructor.
*/
public ProductPromoUseInfo(ProductPromoUseInfo other, boolean exactCopy, Map<ShoppingCartItem, ShoppingCartItem> oldToNewItemMap) {
this.productPromoId = other.productPromoId;
this.productPromoCodeId = other.productPromoCodeId;
this.totalDiscountAmount = other.totalDiscountAmount;
this.quantityLeftInActions = other.quantityLeftInActions;
Map<ShoppingCartItem, BigDecimal> usageInfoMap = null;
if (other.usageInfoMap != null) {
usageInfoMap = new HashMap<>();
for(Map.Entry<ShoppingCartItem, BigDecimal> entry : other.usageInfoMap.entrySet()) {
ShoppingCartItem newItem = oldToNewItemMap.get(entry.getKey());
if (newItem == null) {
Debug.logError("ShoppingCartItem " + entry.getKey() + " was not cloned properly", module);
continue;
}
usageInfoMap.put(newItem, entry.getValue());
}
usageInfoMap = Collections.unmodifiableMap(usageInfoMap);
}
this.usageInfoMap = usageInfoMap;
}
/** SCIPIO: Performs a copy of the instance. Added 2019-09-05. */
public ProductPromoUseInfo copy(boolean exactCopy, Map<ShoppingCartItem, ShoppingCartItem> oldToNewItemMap) {
return new ProductPromoUseInfo(this, exactCopy, oldToNewItemMap);
}
/**
* SCIPIO: Tests to ensure the cart is an exact copy of the other; used to verify {@link #copy}.
* NOTE: This is NOT the same as a logical Object equals override! This is mainly for testing.
*/
protected void ensureExactEquals(ProductPromoUseInfo other, List<String> errorMessages) {
ShoppingCart.ensureExactEquals(this.productPromoId, other.productPromoId, "ProductPromoUseInfo.productPromoId", errorMessages);
ShoppingCart.ensureExactEquals(this.productPromoCodeId, other.productPromoCodeId, "ProductPromoUseInfo.productPromoCodeId", errorMessages);
ShoppingCart.ensureExactEquals(this.totalDiscountAmount, other.totalDiscountAmount, "ProductPromoUseInfo.totalDiscountAmount", errorMessages);
ShoppingCart.ensureExactEquals(this.quantityLeftInActions, other.quantityLeftInActions, "ProductPromoUseInfo.quantityLeftInActions", errorMessages);
ShoppingCart.ensureExactEquals(this.usageInfoMap, other.usageInfoMap, "ProductPromoUseInfo.usageInfoMap", errorMessages);
}
public String getProductPromoId() { return this.productPromoId; }
public String getProductPromoCodeId() { return this.productPromoCodeId; }
public BigDecimal getTotalDiscountAmount() { return this.totalDiscountAmount; }
public BigDecimal getQuantityLeftInActions() { return this.quantityLeftInActions; }
public Map<ShoppingCartItem,BigDecimal> getUsageInfoMap() { return this.usageInfoMap; }
public BigDecimal getUsageWeight() {
Iterator<ShoppingCartItem> lineItems = this.usageInfoMap.keySet().iterator();
BigDecimal totalAmount = BigDecimal.ZERO;
while (lineItems.hasNext()) {
ShoppingCartItem lineItem = lineItems.next();
totalAmount = totalAmount.add(lineItem.getBasePrice().multiply(usageInfoMap.get(lineItem)));
}
if (totalAmount.compareTo(BigDecimal.ZERO) == 0) {
return BigDecimal.ZERO;
}
return getTotalDiscountAmount().negate().divide(totalAmount, scale, rounding);
}
@Override
public int compareTo(ProductPromoUseInfo other) {
return other.getUsageWeight().compareTo(getUsageWeight());
}
}
/**
* Factory method (SCIPIO).
*/
public CartShipInfo newShipInfo() {
return new CartShipInfo();
}
public static class CartShipInfo implements Serializable {
// SCIPIO: 2018-11-22: Defaults moved into constructor
public Map<ShoppingCartItem, CartShipItemInfo> shipItemInfo;
public List<GenericValue> shipTaxAdj;
public String orderTypeId;
private String internalContactMechId;
public String telecomContactMechId;
public String shipmentMethodTypeId;
public String supplierPartyId;
public String carrierRoleTypeId;
public String carrierPartyId;
private String facilityId;
public String giftMessage;
public String shippingInstructions;
public String maySplit;
public String isGift;
public BigDecimal shipEstimate; // = BigDecimal.ZERO
public Timestamp shipBeforeDate;
public Timestamp shipAfterDate;
private String shipGroupSeqId;
private String associatedShipGroupSeqId;
public String vendorPartyId;
public String productStoreShipMethId;
public Map<String, Object> attributes;
/**
* SCIPIO: Default constructor.
*/
public CartShipInfo() {
this.shipItemInfo = new HashMap<>();
this.shipTaxAdj = new ArrayList<>();
this.orderTypeId = null;
this.internalContactMechId = null;
this.telecomContactMechId = null;
this.shipmentMethodTypeId = null;
this.supplierPartyId = null;
this.carrierRoleTypeId = null;
this.carrierPartyId = null;
this.facilityId = null;
this.giftMessage = null;
this.shippingInstructions = null;
this.maySplit = "N";
this.isGift = "N";
this.shipEstimate = BigDecimal.ZERO;
this.shipBeforeDate = null;
this.shipAfterDate = null;
this.shipGroupSeqId = null;
this.associatedShipGroupSeqId = null;
this.vendorPartyId = null;
this.productStoreShipMethId = null;
this.attributes = new HashMap<>();
}
/**
* SCIPIO: Copy constructor.
*/
public CartShipInfo(CartShipInfo other, boolean exactCopy, Map<ShoppingCartItem, ShoppingCartItem> oldToNewItemMap) {
Map<ShoppingCartItem, CartShipItemInfo> shipItemInfo = new HashMap<>();
for(Map.Entry<ShoppingCartItem, CartShipItemInfo> entry : other.shipItemInfo.entrySet()) {
ShoppingCartItem newItem = oldToNewItemMap.get(entry.getKey());
if (newItem == null) {
Debug.logError("ShoppingCartItem " + entry.getKey() + " was not cloned properly", module);
continue;
}
if (entry.getValue() == null) {
shipItemInfo.put(newItem, null);
} else {
shipItemInfo.put(newItem, entry.getValue().copy(exactCopy, newItem));
}
}
this.shipItemInfo = shipItemInfo;
this.shipTaxAdj = new ArrayList<>(other.shipTaxAdj);
this.orderTypeId = other.orderTypeId;
this.internalContactMechId = other.internalContactMechId;
this.telecomContactMechId = other.telecomContactMechId;
this.shipmentMethodTypeId = other.shipmentMethodTypeId;
this.supplierPartyId = other.supplierPartyId;
this.carrierRoleTypeId = other.carrierRoleTypeId;
this.carrierPartyId = other.carrierPartyId;
this.facilityId = other.facilityId;
this.giftMessage = other.giftMessage;
this.shippingInstructions = other.shippingInstructions;
this.maySplit = other.maySplit;
this.isGift = other.isGift;
this.shipEstimate = other.shipEstimate;
this.shipBeforeDate = other.shipBeforeDate;
this.shipAfterDate = other.shipAfterDate;
this.shipGroupSeqId = other.shipGroupSeqId;
this.associatedShipGroupSeqId = other.associatedShipGroupSeqId;
this.vendorPartyId = other.vendorPartyId;
this.productStoreShipMethId = other.productStoreShipMethId;
this.attributes = other.attributes;
}
/** SCIPIO: Performs a copy of the instance. Added 2019-09-05. */
public CartShipInfo copy(boolean exactCopy, Map<ShoppingCartItem, ShoppingCartItem> oldToNewItemMap) {
return new CartShipInfo(this, exactCopy, oldToNewItemMap);
}
/**
* SCIPIO: Tests to ensure the cart is an exact copy of the other; used to verify {@link #copy}.
* NOTE: This is NOT the same as a logical Object equals override! This is mainly for testing.
*/
protected void ensureExactEquals(CartShipInfo other, List<String> errorMessages) {
try {
ShoppingCart.ensureExactEquals(this.shipItemInfo, other.shipItemInfo, "CartShipInfo.shipItemInfo", errorMessages);
ShoppingCart.ensureExactEquals(this.shipTaxAdj, other.shipTaxAdj, "CartShipInfo.shipTaxAdj", errorMessages);
ShoppingCart.ensureExactEquals(this.orderTypeId, other.orderTypeId, "CartShipInfo.orderTypeId", errorMessages);
ShoppingCart.ensureExactEquals(this.internalContactMechId, other.internalContactMechId, "CartShipInfo.internalContactMechId", errorMessages);
ShoppingCart.ensureExactEquals(this.telecomContactMechId, other.telecomContactMechId, "CartShipInfo.telecomContactMechId", errorMessages);
ShoppingCart.ensureExactEquals(this.shipmentMethodTypeId, other.shipmentMethodTypeId, "CartShipInfo.shipmentMethodTypeId", errorMessages);
ShoppingCart.ensureExactEquals(this.supplierPartyId, other.supplierPartyId, "CartShipInfo.supplierPartyId", errorMessages);
ShoppingCart.ensureExactEquals(this.carrierRoleTypeId, other.carrierRoleTypeId, "CartShipInfo.carrierRoleTypeId", errorMessages);
ShoppingCart.ensureExactEquals(this.carrierPartyId, other.carrierPartyId, "CartShipInfo.carrierPartyId", errorMessages);
ShoppingCart.ensureExactEquals(this.facilityId, other.facilityId, "CartShipInfo.facilityId", errorMessages);
ShoppingCart.ensureExactEquals(this.giftMessage, other.giftMessage, "CartShipInfo.giftMessage", errorMessages);
ShoppingCart.ensureExactEquals(this.shippingInstructions, other.shippingInstructions, "CartShipInfo.shippingInstructions", errorMessages);
ShoppingCart.ensureExactEquals(this.maySplit, other.maySplit, "CartShipInfo.maySplit", errorMessages);
ShoppingCart.ensureExactEquals(this.isGift, other.isGift, "CartShipInfo.isGift", errorMessages);
ShoppingCart.ensureExactEquals(this.shipEstimate, other.shipEstimate, "CartShipInfo.shipEstimate", errorMessages);
ShoppingCart.ensureExactEquals(this.shipBeforeDate, other.shipBeforeDate, "CartShipInfo.shipBeforeDate", errorMessages);
ShoppingCart.ensureExactEquals(this.shipAfterDate, other.shipAfterDate, "CartShipInfo.shipAfterDate", errorMessages);
ShoppingCart.ensureExactEquals(this.shipGroupSeqId, other.shipGroupSeqId, "CartShipInfo.shipGroupSeqId", errorMessages);
ShoppingCart.ensureExactEquals(this.associatedShipGroupSeqId, other.associatedShipGroupSeqId, "CartShipInfo.associatedShipGroupSeqId", errorMessages);
ShoppingCart.ensureExactEquals(this.vendorPartyId, other.vendorPartyId, "CartShipInfo.vendorPartyId", errorMessages);
ShoppingCart.ensureExactEquals(this.productStoreShipMethId, other.productStoreShipMethId, "CartShipInfo.productStoreShipMethId", errorMessages);
ShoppingCart.ensureExactEquals(this.attributes, other.attributes, "CartShipInfo.attributes", errorMessages);
} catch(IllegalStateException e) {
throw new IllegalStateException("CartShipInfo field not equal: " + e.getMessage(), e);
}
}
public void setAttribute(String name, Object value) {
this.attributes.put(name, value);
}
public void removeAttribute(String name) {
this.attributes.remove(name);
}
@SuppressWarnings("unchecked")
public <T> T getAttribute(String name) {
return (T) this.attributes.get(name);
}
public String getOrderTypeId() { return orderTypeId; }
public String getContactMechId() { return internalContactMechId; }
public void setContactMechId(String contactMechId) {
this.internalContactMechId = contactMechId;
}
public String getCarrierPartyId() { return carrierPartyId; }
public String getSupplierPartyId() { return supplierPartyId; }
public String getShipmentMethodTypeId() { return shipmentMethodTypeId; }
public void setShipmentMethodTypeId(String shipmentMethodTypeId) { this.shipmentMethodTypeId = shipmentMethodTypeId; } // SCIPIO
public BigDecimal getShipEstimate() { return shipEstimate; }
public void setShipEstimate(BigDecimal shipEstimate) { this.shipEstimate = shipEstimate; } // SCIPIO
public String getShipGroupSeqId() { return shipGroupSeqId; }
public void setShipGroupSeqId(String shipGroupSeqId) {
this.shipGroupSeqId = shipGroupSeqId;
}
public String getAssociatedShipGroupSeqId() { return associatedShipGroupSeqId; }
public void setAssociatedShipGroupSeqId(String shipGroupSeqId) {
this.associatedShipGroupSeqId = shipGroupSeqId;
}
public String getFacilityId() { return facilityId; }
public void setFacilityId(String facilityId) {
this.facilityId = facilityId;
}
public String getVendorPartyId() { return vendorPartyId;}
public void setVendorPartyId(String vendorPartyId) {
this.vendorPartyId = vendorPartyId;
}
public void setMaySplit(Boolean maySplit) {
if (UtilValidate.isNotEmpty(maySplit)) {
this.maySplit = maySplit ? "Y" : "N";
}
}
public void clearAllTaxInfo() {
this.shipTaxAdj.clear();
for (CartShipItemInfo itemInfo : shipItemInfo.values()) {
itemInfo.itemTaxAdj.clear();
}
}
public List<GenericValue> makeItemShipGroupAndAssoc(Delegator delegator, ShoppingCart cart, String shipGroupSeqId) {
return makeItemShipGroupAndAssoc(delegator, cart, shipGroupSeqId, false);
}
public List<GenericValue> makeItemShipGroupAndAssoc(Delegator delegator, ShoppingCart cart, String shipGroupSeqId, boolean newShipGroup) {
List<GenericValue> values = new ArrayList<>();
// create order contact mech for shipping address
if (this.internalContactMechId != null) {
GenericValue orderCm = delegator.makeValue("OrderContactMech");
orderCm.set("contactMechPurposeTypeId", "SHIPPING_LOCATION");
orderCm.set("contactMechId", this.internalContactMechId);
values.add(orderCm);
}
// create the ship group
GenericValue shipGroup = delegator.makeValue("OrderItemShipGroup");
shipGroup.set("shipmentMethodTypeId", shipmentMethodTypeId);
shipGroup.set("carrierRoleTypeId", carrierRoleTypeId);
shipGroup.set("carrierPartyId", carrierPartyId);
shipGroup.set("supplierPartyId", supplierPartyId);
shipGroup.set("shippingInstructions", shippingInstructions);
shipGroup.set("giftMessage", giftMessage);
shipGroup.set("contactMechId", this.internalContactMechId);
shipGroup.set("telecomContactMechId", this.telecomContactMechId);
shipGroup.set("maySplit", maySplit);
shipGroup.set("isGift", isGift);
shipGroup.set("shipGroupSeqId", shipGroupSeqId);
shipGroup.set("vendorPartyId", vendorPartyId);
shipGroup.set("facilityId", facilityId);
// use the cart's default ship before and after dates here
if ((shipBeforeDate == null) && (cart.getDefaultShipBeforeDate() != null)) {
shipGroup.set("shipByDate", cart.getDefaultShipBeforeDate());
} else {
shipGroup.set("shipByDate", shipBeforeDate);
}
if ((shipAfterDate == null) && (cart.getDefaultShipAfterDate() != null)) {
shipGroup.set("shipAfterDate", cart.getDefaultShipAfterDate());
} else {
shipGroup.set("shipAfterDate", shipAfterDate);
}
values.add(shipGroup);
//set estimated ship dates
List<Timestamp> estimatedShipDates = new ArrayList<>();
for (ShoppingCartItem item : shipItemInfo.keySet()) {
Timestamp estimatedShipDate = item.getEstimatedShipDate();
if (estimatedShipDate != null) {
estimatedShipDates.add(estimatedShipDate);
}
}
if (estimatedShipDates.size() > 0) {
Collections.sort(estimatedShipDates);
Timestamp estimatedShipDate = estimatedShipDates.get(estimatedShipDates.size() - 1);
shipGroup.set("estimatedShipDate", estimatedShipDate);
}
//set estimated delivery dates
List<Timestamp> estimatedDeliveryDates = new ArrayList<>();
for (ShoppingCartItem item : shipItemInfo.keySet()) {
Timestamp estimatedDeliveryDate = item.getDesiredDeliveryDate();
if (estimatedDeliveryDate != null) {
estimatedDeliveryDates.add(estimatedDeliveryDate);
}
}
if (UtilValidate.isNotEmpty(estimatedDeliveryDates)) {
Collections.sort(estimatedDeliveryDates);
Timestamp estimatedDeliveryDate = estimatedDeliveryDates.get(estimatedDeliveryDates.size() - 1);
shipGroup.set("estimatedDeliveryDate", estimatedDeliveryDate);
}
// create the shipping estimate adjustments
if (shipEstimate.compareTo(BigDecimal.ZERO) != 0) {
GenericValue shipAdj = delegator.makeValue("OrderAdjustment");
shipAdj.set("orderAdjustmentTypeId", "SHIPPING_CHARGES");
shipAdj.set("amount", shipEstimate);
shipAdj.set("shipGroupSeqId", shipGroupSeqId);
values.add(shipAdj);
}
// create the top level tax adjustments
for (GenericValue taxAdj : shipTaxAdj) {
taxAdj.set("shipGroupSeqId", shipGroupSeqId);
values.add(taxAdj);
}
// create the ship group item associations
for (Entry<ShoppingCartItem, CartShipItemInfo> entry : shipItemInfo.entrySet()) {
ShoppingCartItem item = entry.getKey();
CartShipItemInfo itemInfo = entry.getValue();
GenericValue assoc = delegator.makeValue("OrderItemShipGroupAssoc");
assoc.set("orderItemSeqId", item.getOrderItemSeqId());
assoc.set("shipGroupSeqId", shipGroupSeqId);
assoc.set("quantity", itemInfo.quantity);
values.add(assoc);
// create the item tax adjustment
for (GenericValue taxAdj : itemInfo.itemTaxAdj) {
taxAdj.set("orderItemSeqId", item.getOrderItemSeqId());
taxAdj.set("shipGroupSeqId", shipGroupSeqId);
values.add(taxAdj);
}
}
return values;
}
public CartShipItemInfo setItemInfo(ShoppingCartItem item, BigDecimal quantity, List<GenericValue> taxAdj) {
CartShipItemInfo itemInfo = shipItemInfo.get(item);
if (itemInfo == null) {
if (!isShippableToAddress(item)) {
throw new IllegalArgumentException("The shipping address is not compatible with ProductGeos rules.");
}
itemInfo = newShipInfoItem();
itemInfo.item = item;
shipItemInfo.put(item, itemInfo);
}
if (quantity.compareTo(BigDecimal.ZERO) >= 0) {
itemInfo.quantity = quantity;
}
if (taxAdj != null) {
itemInfo.itemTaxAdj.clear();
itemInfo.itemTaxAdj.addAll(taxAdj);
}
return itemInfo;
}
public CartShipItemInfo setItemInfo(ShoppingCartItem item, List<GenericValue> taxAdj) {
return setItemInfo(item, BigDecimal.ONE.negate(), taxAdj);
}
public CartShipItemInfo setItemInfo(ShoppingCartItem item, BigDecimal quantity) {
return setItemInfo(item, quantity, null);
}
public CartShipItemInfo getShipItemInfo(ShoppingCartItem item) {
return shipItemInfo.get(item);
}
public Set<ShoppingCartItem> getShipItems() {
return shipItemInfo.keySet();
}
private boolean isShippableToAddress(ShoppingCartItem item) {
if ("SALES_ORDER".equals(getOrderTypeId())) {
// Verify if the new address is compatible with the ProductGeos rules of
// the products already in the cart
GenericValue shippingAddress = null;
try {
shippingAddress = item.getDelegator().findOne("PostalAddress", UtilMisc.toMap("contactMechId", this.internalContactMechId), false);
} catch (GenericEntityException gee) {
Debug.logError(gee, "Error retrieving the shipping address for contactMechId [" + this.internalContactMechId + "].", module);
}
if (shippingAddress != null) {
GenericValue product = item.getProduct();
if (product != null) {
return ProductWorker.isShippableToAddress(product, shippingAddress);
}
}
}
return true;
}
/**
* Reset the ship group's shipBeforeDate if it is after the parameter
* @param newShipBeforeDate the ship group's shipBeforeDate to be reset
*/
public void resetShipBeforeDateIfAfter(Timestamp newShipBeforeDate) {
if (newShipBeforeDate != null) {
if ((this.shipBeforeDate == null) || (!this.shipBeforeDate.before(newShipBeforeDate))) {
this.shipBeforeDate = newShipBeforeDate;
}
}
}
/**
* Reset the ship group's shipAfterDate if it is before the parameter
* @param newShipAfterDate the ship group's shipAfterDate to be reset
*/
public void resetShipAfterDateIfBefore(Timestamp newShipAfterDate) {
if (newShipAfterDate != null) {
if ((this.shipAfterDate == null) || (!this.shipAfterDate.after(newShipAfterDate))) {
this.shipAfterDate = newShipAfterDate;
}
}
}
public BigDecimal getTotalTax(ShoppingCart cart) {
List<GenericValue> taxAdjustments = new ArrayList<>();
taxAdjustments.addAll(shipTaxAdj);
for (CartShipItemInfo info : shipItemInfo.values()) {
taxAdjustments.addAll(info.itemTaxAdj);
}
Map<String, Object> taxByAuthority = OrderReadHelper.getOrderTaxByTaxAuthGeoAndParty(taxAdjustments);
BigDecimal taxTotal = (BigDecimal) taxByAuthority.get("taxGrandTotal");
return taxTotal;
}
/** SCIPIO: VAT Tax calculation. */
public BigDecimal getTotalVATTax(ShoppingCart cart){
List<GenericValue> taxAdjustments = new ArrayList<GenericValue>();
taxAdjustments.addAll(shipTaxAdj);
for (CartShipItemInfo info : shipItemInfo.values()) {
taxAdjustments.addAll(info.itemTaxAdj);
}
Map<String, Object> taxByAuthority = OrderReadHelper.getOrderVATTaxByTaxAuthGeoAndParty(taxAdjustments);
BigDecimal taxTotal = (BigDecimal) taxByAuthority.get("taxGrandTotal");
return taxTotal;
}
public BigDecimal getTotal() {
BigDecimal shipItemTotal = BigDecimal.ZERO;
for (CartShipItemInfo info : shipItemInfo.values()) {
shipItemTotal = shipItemTotal.add(info.getItemSubTotal());
}
return shipItemTotal;
}
/**
* Factory method (SCIPIO).
*/
public CartShipItemInfo newShipInfoItem() {
return new CartShipItemInfo();
}
public static class CartShipItemInfo implements Serializable {
// SCIPIO: 2018-11-23: Defaults moved to constructor
public List<GenericValue> itemTaxAdj;
public ShoppingCartItem item;
public BigDecimal quantity;
/**
* SCIPIO: Default constructor.
*/
public CartShipItemInfo() {
this.itemTaxAdj = new ArrayList<>();
this.item = null;
this.quantity = BigDecimal.ZERO;
}
/**
* SCIPIO: Copy constructor.
*/
public CartShipItemInfo(CartShipItemInfo other, boolean exactCopy, ShoppingCartItem parentItem) {
this.itemTaxAdj = new ArrayList<>(other.itemTaxAdj);
this.item = (parentItem != null) ? parentItem : other.item;
this.quantity = other.quantity;
}
/** SCIPIO: Performs a copy of the instance. Added 2019-09-05. */
public CartShipItemInfo copy(boolean exactCopy, ShoppingCartItem parentItem) {
return new CartShipItemInfo(this, exactCopy, parentItem);
}
/**
* SCIPIO: Tests to ensure the cart is an exact copy of the other; used to verify {@link #copy}.
* NOTE: This is NOT the same as a logical Object equals override! This is mainly for testing.
*/
protected void ensureExactEquals(CartShipItemInfo other, List<String> errorMessages) {
try {
ShoppingCart.ensureExactEquals(this.itemTaxAdj, other.itemTaxAdj, "CartShipItemInfo.itemTaxAdj", errorMessages);
ShoppingCart.ensureExactEquals(this.item, other.item, "CartShipItemInfo.item", errorMessages);
ShoppingCart.ensureExactEquals(this.quantity, other.quantity, "CartShipItemInfo.quantity", errorMessages);
} catch(IllegalStateException e) {
throw new IllegalStateException("CartShipItemInfo field not equal: " + e.getMessage(), e);
}
}
public BigDecimal getItemTax(ShoppingCart cart) {
BigDecimal itemTax = BigDecimal.ZERO;
for (int i = 0; i < itemTaxAdj.size(); i++) {
GenericValue v = itemTaxAdj.get(i);
itemTax = itemTax.add(OrderReadHelper.calcItemAdjustment(v, quantity, item.getBasePrice()));
}
return itemTax.setScale(taxCalcScale, taxRounding);
}
public ShoppingCartItem getItem() {
return this.item;
}
public BigDecimal getItemQuantity() {
return this.quantity;
}
public BigDecimal getItemSubTotal() {
return item.getItemSubTotal(quantity);
}
}
}
public static class CartPaymentInfo implements Serializable, Comparable<Object> {
public String paymentMethodTypeId;
public String paymentMethodId;
public String finAccountId;
public String securityCode;
public String postalCode;
public String[] refNum;
public String track2;
public String partyId; // SCIPIO: 2018-07-19: billing address default: partyId
public BigDecimal amount;
public boolean singleUse; // = false
public boolean isPresent; // = false
public boolean isSwiped; // = false
public boolean overflow; // = false
public BigDecimal origAmount; // SCIPIO: original amount as specified upon creation. should not change.
/**
* SCIPIO: Default constructor.
*/
public CartPaymentInfo(String partyId) { // SCIPIO: 2018-07-19: billing address default: partyId
super();
this.partyId = partyId;
this.refNum = new String[2];
// All others are null or false
}
/**
* SCIPIO: Copy constructor.
*/
public CartPaymentInfo(CartPaymentInfo other, boolean exactCopy) {
this.paymentMethodTypeId = other.paymentMethodTypeId;
this.paymentMethodId = other.paymentMethodId;
this.finAccountId = other.finAccountId;
this.securityCode = other.securityCode;
this.postalCode = other.postalCode;
this.refNum = other.refNum.clone();
this.track2 = other.track2;
this.partyId = other.partyId;
this.amount = other.amount;
this.singleUse = other.singleUse;
this.isPresent = other.isPresent;
this.isSwiped = other.isSwiped;
this.overflow = other.overflow;
this.origAmount = other.origAmount;
}
/** SCIPIO: Performs a copy of the instance. Added 2019-09-05. */
public CartPaymentInfo copy(boolean exactCopy) {
return new CartPaymentInfo(this, exactCopy);
}
/**
* SCIPIO: Tests to ensure the cart is an exact copy of the other; used to verify {@link #copy}.
* NOTE: This is NOT the same as a logical Object equals override! This is mainly for testing.
*/
protected void ensureExactEquals(CartPaymentInfo other, List<String> errorMessages) {
ShoppingCart.ensureExactEquals(this.paymentMethodTypeId, other.paymentMethodTypeId, "CartPaymentInfo.paymentMethodTypeId", errorMessages);
ShoppingCart.ensureExactEquals(this.paymentMethodId, other.paymentMethodId, "CartPaymentInfo.paymentMethodId", errorMessages);
ShoppingCart.ensureExactEquals(this.finAccountId, other.finAccountId, "CartPaymentInfo.finAccountId", errorMessages);
ShoppingCart.ensureExactEquals(this.securityCode, other.securityCode, "CartPaymentInfo.securityCode", errorMessages);
ShoppingCart.ensureExactEquals(this.postalCode, other.postalCode, "CartPaymentInfo.postalCode", errorMessages);
ShoppingCart.ensureExactEquals(this.refNum, other.refNum, "CartPaymentInfo.refNum", errorMessages);
ShoppingCart.ensureExactEquals(this.track2, other.track2, "CartPaymentInfo.track2", errorMessages);
ShoppingCart.ensureExactEquals(this.partyId, other.partyId, "CartPaymentInfo.partyId", errorMessages);
ShoppingCart.ensureExactEquals(this.amount, other.amount, "CartPaymentInfo.amount", errorMessages);
ShoppingCart.ensureExactEquals(this.singleUse, other.singleUse, "CartPaymentInfo.singleUse", errorMessages);
ShoppingCart.ensureExactEquals(this.isPresent, other.isPresent, "CartPaymentInfo.isPresent", errorMessages);
ShoppingCart.ensureExactEquals(this.isSwiped, other.isSwiped, "CartPaymentInfo.isSwiped", errorMessages);
ShoppingCart.ensureExactEquals(this.overflow, other.overflow, "CartPaymentInfo.overflow", errorMessages);
ShoppingCart.ensureExactEquals(this.origAmount, other.origAmount, "CartPaymentInfo.origAmount", errorMessages);
}
public GenericValue getValueObject(Delegator delegator) {
String entityName = null;
Map<String, String> lookupFields = null;
if (paymentMethodId != null) {
lookupFields = UtilMisc.<String, String>toMap("paymentMethodId", paymentMethodId);
entityName = "PaymentMethod";
} else if (paymentMethodTypeId != null) {
lookupFields = UtilMisc.<String, String>toMap("paymentMethodTypeId", paymentMethodTypeId);
entityName = "PaymentMethodType";
} else {
throw new IllegalArgumentException("Could not create value object because paymentMethodId and paymentMethodTypeId are null");
}
try {
return EntityQuery.use(delegator).from(entityName).where(lookupFields).cache(true).queryOne();
} catch (GenericEntityException e) {
Debug.logError(e, module);
}
return null;
}
public GenericValue getBillingAddressFromParty(Delegator delegator) { // SCIPIO: 2018-07-19: billing address default
GenericValue postalAddress = null;
try {
GenericValue partyContactMechPurpose = EntityQuery.use(delegator).from("PartyContactMechPurpose")
.where("partyId", partyId, "contactMechPurposeTypeId", "BILLING_LOCATION").orderBy("-fromDate").filterByDate().queryFirst();
if (UtilValidate.isEmpty(partyContactMechPurpose)) {
partyContactMechPurpose = EntityQuery.use(delegator).from("PartyContactMechPurpose")
.where("partyId", partyId, "contactMechPurposeTypeId", "GENERAL_LOCATION").orderBy("-fromDate").filterByDate().queryFirst();
}
if (UtilValidate.isEmpty(partyContactMechPurpose)) {
partyContactMechPurpose = EntityQuery.use(delegator).from("PartyContactMechPurpose")
.where("partyId", partyId, "contactMechPurposeTypeId", "SHIPPING_LOCATION").orderBy("-fromDate").filterByDate().queryFirst();
}
if (UtilValidate.isNotEmpty(partyContactMechPurpose)) {
postalAddress = partyContactMechPurpose.getRelatedOne("PostalAddress", false);
}
} catch (GenericEntityException e) {
Debug.logError(e, module);
}
return postalAddress;
}
public GenericValue getBillingAddress(Delegator delegator) {
GenericValue valueObj = this.getValueObject(delegator);
GenericValue postalAddress = null;
if ("PaymentMethod".equals(valueObj.getEntityName())) {
String paymentMethodTypeId = valueObj.getString("paymentMethodTypeId");
String paymentMethodId = valueObj.getString("paymentMethodId");
// billing account, credit card, gift card, eft account all have postal address
try {
GenericValue pmObj = null;
if ("CREDIT_CARD".equals(paymentMethodTypeId)) {
pmObj = EntityQuery.use(delegator).from("CreditCard").where("paymentMethodId", paymentMethodId).queryOne();
} else if ("GIFT_CARD".equals(paymentMethodTypeId)) {
pmObj = EntityQuery.use(delegator).from("GiftCard").where("paymentMethodId", paymentMethodId).queryOne();
} else if ("EFT_ACCOUNT".equals(paymentMethodTypeId)) {
pmObj = EntityQuery.use(delegator).from("EftAccount").where("paymentMethodId", paymentMethodId).queryOne();
} else if ("EXT_BILLACT".equals(paymentMethodTypeId)) {
pmObj = EntityQuery.use(delegator).from("BillingAccount").where("paymentMethodId", paymentMethodId).queryOne();
} else if ("EXT_PAYPAL".equals(paymentMethodTypeId)) {
pmObj = EntityQuery.use(delegator).from("PayPalPaymentMethod").where("paymentMethodId", paymentMethodId).queryOne();
} else { // SCIPIO: 2018-07-19: billing address default
pmObj = getBillingAddressFromParty(delegator);
}
if (pmObj != null) {
postalAddress = delegator.from("PostalAddress").where("contactMechId", pmObj.get("contactMechId")).queryOne();
} else {
Debug.logInfo("No PaymentMethod Object Found - " + paymentMethodId, module);
}
} catch (GenericEntityException e) {
Debug.logError(e, module);
}
} else { // SCIPIO: 2018-07-19: billing address default
postalAddress = getBillingAddressFromParty(delegator);
}
return postalAddress;
}
public List<GenericValue> makeOrderPaymentInfos(Delegator delegator, ShoppingCart cart) {
BigDecimal maxAmount = BigDecimal.ZERO;
GenericValue valueObj = this.getValueObject(delegator);
List<GenericValue> values = new ArrayList<>();
if (valueObj != null) {
// first create a BILLING_LOCATION for the payment method address if there is one
// SCIPIO: 05-29-20: Commented out the condition below because it doesn't make any sense to constrain OrderContactMech BILLING_LOCATION purpose when the
// entity is PaymentMethod. BillingAddress can be present no matter the orderPaymentInfos are being created from PaymentMethod or PaymentMethodType. As
// a matter of fact this can be problematic specially when SHIPPING_LOCATION and BILLING_LOCATION differ
// if ("PaymentMethod".equals(valueObj.getEntityName())) {
String billingAddressId = null;
GenericValue billingAddress = this.getBillingAddress(delegator);
if (billingAddress != null) {
billingAddressId = billingAddress.getString("contactMechId");
}
if (UtilValidate.isNotEmpty(billingAddressId)) {
GenericValue orderCm = delegator.makeValue("OrderContactMech");
orderCm.set("contactMechPurposeTypeId", "BILLING_LOCATION");
orderCm.set("contactMechId", billingAddressId);
values.add(orderCm);
}
// }
GenericValue productStore = null;
String splitPayPrefPerShpGrp = null;
try {
productStore = EntityQuery.use(delegator).from("ProductStore").where("productStoreId", cart.getProductStoreId()).queryOne();
} catch (GenericEntityException e) {
Debug.logError(e.toString(), module);
}
if (productStore != null) {
splitPayPrefPerShpGrp = productStore.getString("splitPayPrefPerShpGrp");
}
if (splitPayPrefPerShpGrp == null) {
splitPayPrefPerShpGrp = "N";
}
if ("Y".equals(splitPayPrefPerShpGrp) && cart.paymentInfo.size() > 1) {
throw new GeneralRuntimeException("Split Payment Preference per Ship Group does not yet support multiple Payment Methods");
}
if ("Y".equals(splitPayPrefPerShpGrp) && cart.paymentInfo.size() == 1) {
for (CartShipInfo csi : cart.getShipGroups()) {
maxAmount = csi.getTotal().add(cart.getOrderOtherAdjustmentTotal().add(cart.getOrderGlobalAdjustments()).divide(new BigDecimal(cart.getShipGroupSize()), generalRounding)).add(csi.getShipEstimate().add(csi.getTotalTax(cart)));
maxAmount = maxAmount.setScale(scale, rounding);
// create the OrderPaymentPreference record
GenericValue opp = delegator.makeValue("OrderPaymentPreference");
opp.set("paymentMethodTypeId", valueObj.getString("paymentMethodTypeId"));
opp.set("presentFlag", isPresent ? "Y" : "N");
opp.set("swipedFlag", isSwiped ? "Y" : "N");
opp.set("overflowFlag", overflow ? "Y" : "N");
opp.set("paymentMethodId", paymentMethodId);
opp.set("finAccountId", finAccountId);
opp.set("billingPostalCode", postalCode);
opp.set("maxAmount", maxAmount);
opp.set("shipGroupSeqId", csi.getShipGroupSeqId());
if (refNum != null) {
opp.set("manualRefNum", refNum[0]);
opp.set("manualAuthCode", refNum[1]);
}
if (securityCode != null) {
opp.set("securityCode", securityCode);
}
if (track2 != null) {
opp.set("track2", track2);
}
if (paymentMethodId != null || "FIN_ACCOUNT".equals(paymentMethodTypeId)) {
opp.set("statusId", "PAYMENT_NOT_AUTH");
} else if (paymentMethodTypeId != null) {
// external payment method types require notification when received
// internal payment method types are assumed to be in-hand
if (paymentMethodTypeId.startsWith("EXT_")) {
opp.set("statusId", "PAYMENT_NOT_RECEIVED");
} else {
opp.set("statusId", "PAYMENT_RECEIVED");
}
}
Debug.logInfo("ShipGroup [" + csi.getShipGroupSeqId() +"]", module);
Debug.logInfo("Creating OrderPaymentPreference - " + opp, module);
values.add(opp);
}
} else if ("N".equals(splitPayPrefPerShpGrp)) {
maxAmount = maxAmount.add(amount);
maxAmount = maxAmount.setScale(scale, rounding);
// create the OrderPaymentPreference record
GenericValue opp = delegator.makeValue("OrderPaymentPreference");
opp.set("paymentMethodTypeId", valueObj.getString("paymentMethodTypeId"));
opp.set("presentFlag", isPresent ? "Y" : "N");
opp.set("swipedFlag", isSwiped ? "Y" : "N");
opp.set("overflowFlag", overflow ? "Y" : "N");
opp.set("paymentMethodId", paymentMethodId);
opp.set("finAccountId", finAccountId);
opp.set("billingPostalCode", postalCode);
opp.set("maxAmount", maxAmount);
if (refNum != null) {
opp.set("manualRefNum", refNum[0]);
opp.set("manualAuthCode", refNum[1]);
}
if (securityCode != null) {
opp.set("securityCode", securityCode);
}
if (track2 != null) {
opp.set("track2", track2);
}
if (paymentMethodId != null || "FIN_ACCOUNT".equals(paymentMethodTypeId)) {
opp.set("statusId", "PAYMENT_NOT_AUTH");
} else if (paymentMethodTypeId != null) {
// external payment method types require notification when received
// internal payment method types are assumed to be in-hand
if (paymentMethodTypeId.startsWith("EXT_")) {
opp.set("statusId", "PAYMENT_NOT_RECEIVED");
} else {
opp.set("statusId", "PAYMENT_RECEIVED");
}
}
Debug.logInfo("Creating OrderPaymentPreference - " + opp, module);
values.add(opp);
}
}
return values;
}
@Override
public int compareTo(Object o) {
CartPaymentInfo that = (CartPaymentInfo) o;
Debug.logInfo("Compare [" + this.toString() + "] to [" + that.toString() + "]", module);
if (this.finAccountId == null) {
if (that.finAccountId != null) {
return -1;
}
} else if (!this.finAccountId.equals(that.finAccountId)) {
return -1;
}
if (this.paymentMethodId != null) {
if (that.paymentMethodId == null) {
return 1;
}
int pmCmp = this.paymentMethodId.compareTo(that.paymentMethodId);
if (pmCmp == 0) {
if (this.refNum != null && this.refNum[0] != null) {
if (that.refNum != null && that.refNum[0] != null) {
return this.refNum[0].compareTo(that.refNum[0]);
}
return 1;
}
if (that.refNum != null && that.refNum[0] != null) {
return -1;
}
return 0;
}
return pmCmp;
}
if (that.paymentMethodId != null) {
return -1;
}
int pmtCmp = this.paymentMethodTypeId.compareTo(that.paymentMethodTypeId);
if (pmtCmp == 0) {
if (this.refNum != null && this.refNum[0] != null) {
if (that.refNum != null && that.refNum[0] != null) {
return this.refNum[0].compareTo(that.refNum[0]);
}
return 1;
}
if (that.refNum != null && that.refNum[0] != null) {
return -1;
}
return 0;
}
return pmtCmp;
}
/* SCIPIO: 2018-10-09: TODO: REVIEW: this is much more precise than compareTo, but usage
is unclear, so will have to omit this until real case clarifies as used in a Map, Set or other
@Override
public int hashCode() {
// SCIPIO: 2018-10-09: TODO: REVIEW: this is much more precise than compareTo zero, usage unclear
final int prime = 31;
int result = 1;
result = prime * result + ((amount == null) ? 0 : amount.hashCode());
result = prime * result + ((finAccountId == null) ? 0 : finAccountId.hashCode());
result = prime * result + (isPresent ? 1231 : 1237);
result = prime * result + (isSwiped ? 1231 : 1237);
result = prime * result + (overflow ? 1231 : 1237);
result = prime * result + ((paymentMethodId == null) ? 0 : paymentMethodId.hashCode());
result = prime * result + ((paymentMethodTypeId == null) ? 0 : paymentMethodTypeId.hashCode());
result = prime * result + ((postalCode == null) ? 0 : postalCode.hashCode());
result = prime * result + Arrays.hashCode(refNum);
result = prime * result + ((securityCode == null) ? 0 : securityCode.hashCode());
result = prime * result + (singleUse ? 1231 : 1237);
result = prime * result + ((track2 == null) ? 0 : track2.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
CartPaymentInfo other = (CartPaymentInfo) obj;
if (amount == null) {
if (other.amount != null) {
return false;
}
} else if (!amount.equals(other.amount)) {
return false;
}
if (finAccountId == null) {
if (other.finAccountId != null) {
return false;
}
} else if (!finAccountId.equals(other.finAccountId)) {
return false;
}
if (isPresent != other.isPresent) {
return false;
}
if (isSwiped != other.isSwiped) {
return false;
}
if (overflow != other.overflow) {
return false;
}
if (paymentMethodId == null) {
if (other.paymentMethodId != null) {
return false;
}
} else if (!paymentMethodId.equals(other.paymentMethodId)) {
return false;
}
if (paymentMethodTypeId == null) {
if (other.paymentMethodTypeId != null) {
return false;
}
} else if (!paymentMethodTypeId.equals(other.paymentMethodTypeId)) {
return false;
}
if (postalCode == null) {
if (other.postalCode != null) {
return false;
}
} else if (!postalCode.equals(other.postalCode)) {
return false;
}
if (!Arrays.equals(refNum, other.refNum)) {
return false;
}
if (securityCode == null) {
if (other.securityCode != null) {
return false;
}
} else if (!securityCode.equals(other.securityCode)) {
return false;
}
if (singleUse != other.singleUse) {
return false;
}
if (track2 == null) {
if (other.track2 != null) {
return false;
}
} else if (!track2.equals(other.track2)) {
return false;
}
return true;
}
*/
@Override
public String toString() {
return "Pm: " + paymentMethodId + " / PmType: " + paymentMethodTypeId + " / Amt: " + amount + " / Ref: " + refNum[0] + "!" + refNum[1];
}
}
public Map<String, String> getOrderAttributes() {
return orderAttributes;
}
public void setOrderAttributes(Map<String, String> orderAttributes) {
this.orderAttributes = orderAttributes;
}
public String getOrderStatusId() {
return orderStatusId;
}
public void setOrderStatusId(String orderStatusId) {
this.orderStatusId = orderStatusId;
}
public String getOrderStatusString() {
return orderStatusString;
}
public void setOrderStatusString(String orderStatusString) {
this.orderStatusString = orderStatusString;
}
public static BigDecimal getMinimumOrderQuantity(Delegator delegator, BigDecimal itemBasePrice, String itemProductId) throws GenericEntityException {
BigDecimal minQuantity = BigDecimal.ZERO;
BigDecimal minimumOrderPrice = BigDecimal.ZERO;
List<GenericValue> minimumOrderPriceList = EntityQuery.use(delegator).from("ProductPrice")
.where("productId", itemProductId, "productPriceTypeId", "MINIMUM_ORDER_PRICE")
.filterByDate()
.queryList();
if (itemBasePrice == null) {
List<GenericValue> productPriceList = EntityQuery.use(delegator).from("ProductPrice")
.where("productId", itemProductId)
.filterByDate()
.queryList();
Map<String, BigDecimal> productPriceMap = new HashMap<>();
for (GenericValue productPrice : productPriceList) {
productPriceMap.put(productPrice.getString("productPriceTypeId"), productPrice.getBigDecimal("price"));
}
if (UtilValidate.isNotEmpty(productPriceMap.get("SPECIAL_PROMO_PRICE"))) {
itemBasePrice = productPriceMap.get("SPECIAL_PROMO_PRICE");
} else if (UtilValidate.isNotEmpty(productPriceMap.get("PROMO_PRICE"))) {
itemBasePrice = productPriceMap.get("PROMO_PRICE");
} else if (UtilValidate.isNotEmpty(productPriceMap.get("DEFAULT_PRICE"))) {
itemBasePrice = productPriceMap.get("DEFAULT_PRICE");
} else if (UtilValidate.isNotEmpty(productPriceMap.get("LIST_PRICE"))) {
itemBasePrice = productPriceMap.get("LIST_PRICE");
}
}
if (UtilValidate.isNotEmpty(minimumOrderPriceList)) {
minimumOrderPrice = EntityUtil.getFirst(minimumOrderPriceList).getBigDecimal("price");
}
if (itemBasePrice != null
&& itemBasePrice.compareTo(BigDecimal.ZERO) > 0
&& minimumOrderPrice.compareTo(BigDecimal.ZERO) > 0
&& minimumOrderPrice.compareTo(itemBasePrice) > 0) {
minQuantity = minimumOrderPrice.divide(itemBasePrice, 0, RoundingMode.UP);
}
return minQuantity;
}
/**
* SCIPIO: Gets all emails that are OR are to be associated with the order, including
* party's to-be-associated emails and order additional emails.
* <p>
* WARN: This is not guaranteed to match the final order! The party's
* emails are not stored in cart.
* <p>
* This attempts to emulate {@link OrderReadHelper#getOrderEmailList()}.
*/
public List<String> getOrderEmailList() {
List<String> emailList = new ArrayList<>();
// WARN: This must match what is done in CheckOutHelper!
String partyId = this.getPlacingCustomerPartyId();
if (UtilValidate.isNotEmpty(partyId)) {
GenericValue party;
try {
party = EntityQuery.use(delegator).from("Party").where("partyId", partyId).queryOne();
Collection<GenericValue> contactMechList = ContactHelper.getContactMechByType(party, "EMAIL_ADDRESS", false);
for(GenericValue contactMech : contactMechList) {
emailList.add(contactMech.getString("infoString"));
}
} catch (GenericEntityException e) {
Debug.logError("Could not get order emails for party '" + partyId + "'", module);
}
}
// WARN: This must match waht is done in CheckOutHelper!
List<String> addEmailList = StringUtil.split(this.getOrderAdditionalEmails(), ",");
if (UtilValidate.isNotEmpty(addEmailList)) {
emailList.addAll(addEmailList);
}
return emailList;
}
// ================= Cart Subscriptions =================
/**
* SCIPIO: Retrieve all subscription of the entire cart.
*/
public Map<String, List<GenericValue>> getItemSubscriptions() {
List<ShoppingCartItem> cartItems = items();
if (cartItems != null) {
Map<String, List<GenericValue>> cartSubscriptionItems = new HashMap<>();
for (ShoppingCartItem cartItem : cartItems) {
List<GenericValue> productSubscriptions = getItemSubscriptions(cartItem.getDelegator(), cartItem.getProductId());
if (Debug.infoOn()) {
for (GenericValue productSubscription : productSubscriptions) {
Debug.logInfo("Found cartItem [" + cartItem.getOrderItemSeqId() + "#" + cartItem.getProductId() + "] with subscription id["
+ productSubscription.getString("subscriptionResourceId") + "]", module);
}
}
if (UtilValidate.isNotEmpty(productSubscriptions)) {
cartSubscriptionItems.put(cartItem.getProductId(), productSubscriptions);
}
}
/* 2018-11-22: Removed: cartSubscriptionItems cache poses several issues
return this.cartSubscriptionItems;
*/
return cartSubscriptionItems;
}
return null;
}
/**
* SCIPIO: Retrieve all subscriptions associated to an productId.
*/
public List<GenericValue> getItemSubscriptions(Delegator delegator, String productId) {
/*
* DEV NOTE: 2018-11-22: This currently relies on entity cache for performance;
* if this proves unacceptable, maybe can add a transient List<GenericValue> productSubscriptionResources cache
* to ShoppingCartItem instead - it is less likely to go out of sync with the cart if there instead of this.cartSubscriptionItems.
*/
/* 2018-11-22: Removed: cartSubscriptionItems cache poses several issues
if (this.cartSubscriptionItems == null) {
this.cartSubscriptionItems = new HashMap<>();
}
*/
List<GenericValue> productSubscriptionResources;
try {
productSubscriptionResources = EntityQuery.use(delegator).from("ProductSubscriptionResource").where("productId", productId)
.cache(true).filterByDate().queryList();
} catch (GenericEntityException e) {
Debug.logError(e, "Error while looking up ProductSubscriptionResource for product '" + productId + "'", module);
return new ArrayList<>();
}
/* 2018-11-22: Removed: cartSubscriptionItems cache poses several issues
if (UtilValidate.isNotEmpty(productSubscriptionResources)) {
this.cartSubscriptionItems.put(productId, productSubscriptionResources);
} else {
// 2018-11-12: Keep sync when no subscription
this.cartSubscriptionItems.remove(productId);
}
*/
return productSubscriptionResources;
}
/**
* SCIPIO: Checks if any order item has an underlying subscription/s bound to it.
*/
public boolean hasSubscriptions() {
return UtilValidate.isNotEmpty(this.getItemSubscriptions());
}
/**
* SCIPIO: Checks if an order item has an underlying subscription/s bound to it.
*/
public boolean hasSubscriptions(GenericValue cartItem) {
Map<String, List<GenericValue>> cartSubscriptionItems = getItemSubscriptions();
return UtilValidate.isNotEmpty(cartSubscriptionItems) && cartSubscriptionItems.containsKey(cartItem.getString("productId"));
}
/**
* SCIPIO: Check if the order contains only subscription items.
*/
public boolean orderContainsSubscriptionItemsOnly() {
List<ShoppingCartItem> cartItems = items();
Map<String, List<GenericValue>> cartSubscriptionItems = getItemSubscriptions();
if (cartItems != null && cartSubscriptionItems != null) {
for (ShoppingCartItem orderItem : cartItems) {
if (!cartSubscriptionItems.containsKey(orderItem.getProductId())) {
return false;
}
}
return true;
}
return false;
}
/**
* SCIPIO: Returns true if should allow selecting ship method even if estimate was null ("calculated offline").
* Default: false.
* See {@link WebShoppingCart} for how this is set.
* Added 2018-11-14.
*/
public boolean isAllowMissingShipEstimates() {
return allowMissingShipEstimates;
}
/**
* SCIPIO: Sets if should allow selecting ship method even if estimate was null ("calculated offline").
* Default: false.
* See {@link WebShoppingCart} for how this is set.
* Added 2018-11-14.
*/
public void setAllowMissingShipEstimates(boolean allowMissingShipEstimates) {
this.allowMissingShipEstimates = allowMissingShipEstimates;
}
public List<SolicitationTypeEnum> getSolicitationTypes() {
return allowedSolicitations;
}
public void setSolicitationTypes(List<SolicitationTypeEnum> allowedSolicitations) {
this.allowedSolicitations = allowedSolicitations;
}
public void addAllowedSolicitation(SolicitationTypeEnum solicitationTypeEnum) {
this.allowedSolicitations.add(solicitationTypeEnum);
}
public void removeAllowedSolicitation(SolicitationTypeEnum solicitationTypeEnum) {
this.allowedSolicitations.remove(solicitationTypeEnum);
}
public void resolveAllowedSolicitation(SolicitationTypeEnum solicitationTypeEnum, Map<String, Object> params) {
if (!Arrays.stream(solicitationTypeEnum.getParameterNames()).filter(paramName -> params.containsKey(paramName) && params.get(paramName).equals("Y")).findFirst().isEmpty()) {
if (!this.allowedSolicitations.contains(solicitationTypeEnum)) {
this.addAllowedSolicitation(solicitationTypeEnum);
}
}
if (!Arrays.stream(solicitationTypeEnum.getParameterNames()).filter(paramName -> params.containsKey(paramName) && params.get(paramName).equals("N")).findFirst().isEmpty()) {
if (this.allowedSolicitations.contains(solicitationTypeEnum)) {
this.removeAllowedSolicitation(solicitationTypeEnum);
}
}
}
/**
* SCIPIO: Returns true if verbose logging is on for cart-related processes. Roughly same as verbose logging.
* WARN: Some things controlled by this may lower performance when enabled.
* Added 2018-11-30.
*/
public static boolean verboseOn() {
return (ShoppingCart.DEBUG || Debug.verboseOn());
}
// SCIPIO: 2020-02-36: New factory methods replace direct ShoppingCartItem constructor calls and static makeItem calls.
/**
* Core makeItem factory method, for overriding by client classes (SCIPIO).
*/
public ShoppingCartItem makeItem(Integer cartLocation, GenericValue product, BigDecimal selectedAmount,
BigDecimal quantity, BigDecimal unitPrice, Timestamp reservStart, BigDecimal reservLength, BigDecimal reservPersons,
String accommodationMapId,String accommodationSpotId,
Timestamp shipBeforeDate, Timestamp shipAfterDate, Map<String, GenericValue> additionalProductFeatureAndAppls, Map<String, Object> attributes,
String prodCatalogId, ProductConfigWrapper configWrapper, String itemType, ShoppingCart.ShoppingCartItemGroup itemGroup, LocalDispatcher dispatcher,
ShoppingCart cart, Boolean triggerExternalOpsBool, Boolean triggerPriceRulesBool, GenericValue parentProduct, Boolean skipInventoryChecks, Boolean skipProductChecks,
ShoppingCartItem.ExtraInitArgs extraInitArgs) throws CartItemModifyException {
return ShoppingCartItem.makeItemImpl(cartLocation, product, selectedAmount, quantity, unitPrice, reservStart, reservLength, reservPersons, accommodationMapId, accommodationSpotId,
shipBeforeDate, shipAfterDate, additionalProductFeatureAndAppls, attributes, prodCatalogId, configWrapper, itemType, itemGroup, dispatcher, cart, triggerExternalOpsBool,
triggerPriceRulesBool, parentProduct, skipInventoryChecks, skipProductChecks, extraInitArgs);
}
/**
* Core makeItem factory method, for overriding by client classes (SCIPIO).
*/
public ShoppingCartItem makeItem(Integer cartLocation, String itemType, String itemDescription, String productCategoryId,
BigDecimal basePrice, BigDecimal selectedAmount, BigDecimal quantity, Map<String, Object> attributes, String prodCatalogId, ShoppingCart.ShoppingCartItemGroup itemGroup,
LocalDispatcher dispatcher, ShoppingCart cart, Boolean triggerExternalOpsBool) throws CartItemModifyException {
return ShoppingCartItem.makeItemImpl(cartLocation, itemType, itemDescription, productCategoryId, basePrice, selectedAmount, quantity, attributes, prodCatalogId, itemGroup, dispatcher, cart, triggerExternalOpsBool);
}
/**
* Core makeItem factory method, for overriding by client classes (SCIPIO).
*/
public ShoppingCartItem makePurchaseOrderItem(Integer cartLocation, String productId, BigDecimal selectedAmount, BigDecimal quantity,
Map<String, GenericValue> additionalProductFeatureAndAppls, Map<String, Object> attributes, String prodCatalogId, ProductConfigWrapper configWrapper, String itemType, ShoppingCart.ShoppingCartItemGroup itemGroup,
LocalDispatcher dispatcher, ShoppingCart cart, GenericValue supplierProduct, Timestamp shipBeforeDate, Timestamp shipAfterDate, Timestamp cancelBackOrderDate, ShoppingCartItem.ExtraPurchaseOrderInitArgs extraInitArgs)
throws CartItemModifyException, ItemNotFoundException {
return ShoppingCartItem.makePurchaseOrderItemImpl(cartLocation, productId, selectedAmount, quantity, additionalProductFeatureAndAppls, attributes, prodCatalogId, configWrapper, itemType, itemGroup, dispatcher, cart, supplierProduct, shipBeforeDate, shipAfterDate, cancelBackOrderDate, extraInitArgs);
}
/** Creates new ShoppingCartItem object (SCIPIO). */
public ShoppingCartItem newItem() {
return new ShoppingCartItem();
}
/** Creates new ShoppingCartItem object (SCIPIO). */
public ShoppingCartItem newItem(GenericValue product, Map<String, GenericValue> additionalProductFeatureAndAppls, Map<String, Object> attributes, String prodCatalogId, Locale locale, String itemType, ShoppingCart.ShoppingCartItemGroup itemGroup) {
return new ShoppingCartItem(product, additionalProductFeatureAndAppls, attributes, prodCatalogId, locale, itemType, itemGroup);
}
/** Creates new ShoppingCartItem object (SCIPIO). */
public ShoppingCartItem newItem(GenericValue product, Map<String, GenericValue> additionalProductFeatureAndAppls, Map<String, Object> attributes, String prodCatalogId, ProductConfigWrapper configWrapper, Locale locale, String itemType, ShoppingCart.ShoppingCartItemGroup itemGroup, GenericValue parentProduct) {
return new ShoppingCartItem(product, additionalProductFeatureAndAppls, attributes, prodCatalogId, configWrapper, locale, itemType, itemGroup, parentProduct);
}
/** Creates new ShopingCartItem object (SCIPIO). */
public ShoppingCartItem newItem(Delegator delegator, String itemTypeId, String description, String categoryId, BigDecimal basePrice, Map<String, Object> attributes, String prodCatalogId, Locale locale, ShoppingCart.ShoppingCartItemGroup itemGroup) {
return new ShoppingCartItem(delegator, itemTypeId, description, categoryId, basePrice, attributes, prodCatalogId, locale, itemGroup);
}
}