use of org.apache.ofbiz.order.shoppingcart.ShoppingCartItem in project ofbiz-framework by apache.
the class ProductDisplayWorker method getRandomCartProductAssoc.
/* ========================================================================================*/
/* ============================= Special Data Retrieval Methods ===========================*/
public static List<GenericValue> getRandomCartProductAssoc(ServletRequest request, boolean checkViewAllow) {
Delegator delegator = (Delegator) request.getAttribute("delegator");
HttpServletRequest httpRequest = (HttpServletRequest) request;
ShoppingCart cart = (ShoppingCart) httpRequest.getSession().getAttribute("shoppingCart");
if (cart == null || cart.size() <= 0) {
return null;
}
List<GenericValue> cartAssocs = null;
try {
Map<String, GenericValue> products = new HashMap<>();
Iterator<ShoppingCartItem> cartiter = cart.iterator();
while (cartiter != null && cartiter.hasNext()) {
ShoppingCartItem item = cartiter.next();
// since ProductAssoc records have a fromDate and thruDate, we can filter by now so that only assocs in the date range are included
List<GenericValue> complementProducts = EntityQuery.use(delegator).from("ProductAssoc").where("productId", item.getProductId(), "productAssocTypeId", "PRODUCT_COMPLEMENT").cache(true).filterByDate().queryList();
List<GenericValue> productsCategories = EntityQuery.use(delegator).from("ProductCategoryMember").where("productId", item.getProductId()).cache(true).filterByDate().queryList();
if (productsCategories != null) {
for (GenericValue productsCategoryMember : productsCategories) {
GenericValue productsCategory = productsCategoryMember.getRelatedOne("ProductCategory", true);
if ("CROSS_SELL_CATEGORY".equals(productsCategory.getString("productCategoryTypeId"))) {
List<GenericValue> curPcms = productsCategory.getRelated("ProductCategoryMember", null, null, true);
if (curPcms != null) {
for (GenericValue curPcm : curPcms) {
if (!products.containsKey(curPcm.getString("productId"))) {
GenericValue product = curPcm.getRelatedOne("Product", true);
products.put(product.getString("productId"), product);
}
}
}
}
}
}
if (UtilValidate.isNotEmpty(complementProducts)) {
for (GenericValue productAssoc : complementProducts) {
if (!products.containsKey(productAssoc.getString("productIdTo"))) {
GenericValue product = productAssoc.getRelatedOne("AssocProduct", true);
products.put(product.getString("productId"), product);
}
}
}
}
// remove all products that are already in the cart
cartiter = cart.iterator();
while (cartiter != null && cartiter.hasNext()) {
ShoppingCartItem item = cartiter.next();
products.remove(item.getProductId());
}
// if desired check view allow category
if (checkViewAllow) {
String currentCatalogId = CatalogWorker.getCurrentCatalogId(request);
String viewProductCategoryId = CatalogWorker.getCatalogViewAllowCategoryId(delegator, currentCatalogId);
if (viewProductCategoryId != null) {
List<GenericValue> tempList = new LinkedList<>();
tempList.addAll(products.values());
tempList = CategoryWorker.filterProductsInCategory(delegator, tempList, viewProductCategoryId, "productId");
cartAssocs = new LinkedList<>();
cartAssocs.addAll(tempList);
}
}
if (cartAssocs == null) {
cartAssocs = new LinkedList<>();
cartAssocs.addAll(products.values());
}
// randomly remove products while there are more than 3
while (cartAssocs.size() > 3) {
int toRemove = (int) (Math.random() * cartAssocs.size());
cartAssocs.remove(toRemove);
}
} catch (GenericEntityException e) {
Debug.logWarning(e, module);
}
if (UtilValidate.isNotEmpty(cartAssocs)) {
return cartAssocs;
}
return null;
}
use of org.apache.ofbiz.order.shoppingcart.ShoppingCartItem in project ofbiz-framework by apache.
the class ProductPromoWorker method distributeDiscountAmount.
private static void distributeDiscountAmount(BigDecimal discountAmountTotal, BigDecimal totalAmount, List<ShoppingCartItem> cartItemsUsed, GenericValue productPromoAction, Delegator delegator) {
BigDecimal discountAmount = discountAmountTotal;
// distribute the discount evenly weighted according to price over the order items that the individual quantities came from; avoids a number of issues with tax/shipping calc, inclusion in the sub-total for other promotions, etc
Iterator<ShoppingCartItem> cartItemsUsedIter = cartItemsUsed.iterator();
while (cartItemsUsedIter.hasNext()) {
ShoppingCartItem cartItem = cartItemsUsedIter.next();
// to minimize rounding issues use the remaining total for the last one, otherwise use a calculated value
if (cartItemsUsedIter.hasNext()) {
BigDecimal quantityUsed = cartItem.getPromoQuantityCandidateUseActionAndAllConds(productPromoAction);
BigDecimal ratioOfTotal = quantityUsed.multiply(cartItem.getBasePrice()).divide(totalAmount, generalRounding);
BigDecimal weightedAmount = ratioOfTotal.multiply(discountAmountTotal);
// round the weightedAmount to 3 decimal places, we don't want an exact number cents/whatever because this will be added up as part of a subtotal which will be rounded to 2 decimal places
weightedAmount = weightedAmount.setScale(3, RoundingMode.HALF_UP);
discountAmount = discountAmount.subtract(weightedAmount);
doOrderItemPromoAction(productPromoAction, cartItem, weightedAmount, "amount", delegator);
} else {
// last one, just use discountAmount
doOrderItemPromoAction(productPromoAction, cartItem, discountAmount, "amount", delegator);
}
}
// this is the old way that causes problems: doOrderPromoAction(productPromoAction, cart, discountAmount, "amount", delegator);
}
use of org.apache.ofbiz.order.shoppingcart.ShoppingCartItem in project ofbiz-framework by apache.
the class ProductPromoWorker method prepareDeltaProductUsageInfoMap.
private static Map<ShoppingCartItem, BigDecimal> prepareDeltaProductUsageInfoMap(Map<ShoppingCartItem, BigDecimal> oldMap, Map<ShoppingCartItem, BigDecimal> newMap) {
Map<ShoppingCartItem, BigDecimal> deltaUsageInfoMap = new HashMap<>(newMap);
for (Entry<ShoppingCartItem, BigDecimal> entry : oldMap.entrySet()) {
ShoppingCartItem key = entry.getKey();
BigDecimal oldUsed = entry.getValue();
BigDecimal newUsed = entry.getValue();
if (newUsed.compareTo(oldUsed) > 0) {
deltaUsageInfoMap.put(key, newUsed.add(oldUsed.negate()));
} else {
deltaUsageInfoMap.remove(key);
}
}
return deltaUsageInfoMap;
}
use of org.apache.ofbiz.order.shoppingcart.ShoppingCartItem in project ofbiz-framework by apache.
the class ProductPromoWorker method prepareProductUsageInfoMap.
private static Map<ShoppingCartItem, BigDecimal> prepareProductUsageInfoMap(ShoppingCart cart) {
Map<ShoppingCartItem, BigDecimal> usageInfoMap = new HashMap<>();
List<ShoppingCartItem> lineOrderedByBasePriceList = cart.getLineListOrderedByBasePrice(false);
for (ShoppingCartItem cartItem : lineOrderedByBasePriceList) {
BigDecimal used = cartItem.getPromoQuantityUsed();
if (used.compareTo(BigDecimal.ZERO) != 0) {
usageInfoMap.put(cartItem, used);
}
}
return usageInfoMap;
}
use of org.apache.ofbiz.order.shoppingcart.ShoppingCartItem in project ofbiz-framework by apache.
the class ProductPromoWorker method runProductPromoRules.
private static boolean runProductPromoRules(ShoppingCart cart, Long useLimit, boolean requireCode, String productPromoCodeId, Long codeUseLimit, long maxUseLimit, GenericValue productPromo, List<GenericValue> productPromoRules, LocalDispatcher dispatcher, Delegator delegator, Timestamp nowTimestamp) throws GenericEntityException, UseLimitException {
boolean cartChanged = false;
Map<ShoppingCartItem, BigDecimal> usageInfoMap = prepareProductUsageInfoMap(cart);
String productPromoId = productPromo.getString("productPromoId");
while ((useLimit == null || useLimit.longValue() > cart.getProductPromoUseCount(productPromoId)) && (!requireCode || UtilValidate.isNotEmpty(productPromoCodeId)) && (codeUseLimit == null || codeUseLimit.longValue() > cart.getProductPromoCodeUse(productPromoCodeId))) {
boolean promoUsed = false;
BigDecimal totalDiscountAmount = BigDecimal.ZERO;
BigDecimal quantityLeftInActions = BigDecimal.ZERO;
Iterator<GenericValue> promoRulesIter = productPromoRules.iterator();
while (promoRulesIter != null && promoRulesIter.hasNext()) {
GenericValue productPromoRule = promoRulesIter.next();
// if apply then performActions when no conditions are false, so default to true
boolean performActions = true;
// loop through conditions for rule, if any false, set allConditionsTrue to false
List<GenericValue> productPromoConds = EntityQuery.use(delegator).from("ProductPromoCond").where("productPromoId", productPromo.get("productPromoId")).orderBy("productPromoCondSeqId").cache(true).queryList();
productPromoConds = EntityUtil.filterByAnd(productPromoConds, UtilMisc.toMap("productPromoRuleId", productPromoRule.get("productPromoRuleId")));
// using the other method to consolidate cache entries because the same cache is used elsewhere: List productPromoConds = productPromoRule.getRelated("ProductPromoCond", null, UtilMisc.toList("productPromoCondSeqId"), true);
if (Debug.verboseOn()) {
Debug.logVerbose("Checking " + productPromoConds.size() + " conditions for rule " + productPromoRule, module);
}
Iterator<GenericValue> productPromoCondIter = UtilMisc.toIterator(productPromoConds);
while (productPromoCondIter != null && productPromoCondIter.hasNext()) {
GenericValue productPromoCond = productPromoCondIter.next();
boolean conditionSatisfied = checkCondition(productPromoCond, cart, delegator, dispatcher, nowTimestamp);
// any false condition will cause it to NOT perform the action
if (!conditionSatisfied) {
performActions = false;
break;
}
}
if (performActions) {
// perform all actions, either apply or unapply
List<GenericValue> productPromoActions = productPromoRule.getRelated("ProductPromoAction", null, UtilMisc.toList("productPromoActionSeqId"), true);
Iterator<GenericValue> productPromoActionIter = UtilMisc.toIterator(productPromoActions);
while (productPromoActionIter != null && productPromoActionIter.hasNext()) {
GenericValue productPromoAction = productPromoActionIter.next();
try {
ActionResultInfo actionResultInfo = performAction(productPromoAction, cart, delegator, dispatcher, nowTimestamp);
totalDiscountAmount = totalDiscountAmount.add(actionResultInfo.totalDiscountAmount);
quantityLeftInActions = quantityLeftInActions.add(actionResultInfo.quantityLeftInAction);
// only set if true, don't set back to false: implements OR logic (ie if ANY actions change content, redo loop)
boolean actionChangedCart = actionResultInfo.ranAction;
if (actionChangedCart) {
promoUsed = true;
cartChanged = true;
}
} catch (CartItemModifyException e) {
Debug.logError(e, "Error modifying the cart while performing promotion action [" + productPromoAction.getPrimaryKey() + "]", module);
}
}
}
}
if (promoUsed) {
// Get product use information from the cart
Map<ShoppingCartItem, BigDecimal> newUsageInfoMap = prepareProductUsageInfoMap(cart);
Map<ShoppingCartItem, BigDecimal> deltaUsageInfoMap = prepareDeltaProductUsageInfoMap(usageInfoMap, newUsageInfoMap);
usageInfoMap = newUsageInfoMap;
cart.addProductPromoUse(productPromo.getString("productPromoId"), productPromoCodeId, totalDiscountAmount, quantityLeftInActions, deltaUsageInfoMap);
} else {
// the promotion was not used, don't try again until we finish a full pass and come back to see the promo conditions are now satisfied based on changes to the cart
break;
}
if (cart.getProductPromoUseCount(productPromoId) > maxUseLimit) {
throw new UseLimitException("ERROR: While calculating promotions the promotion [" + productPromoId + "] action was applied more than " + maxUseLimit + " times, so the calculation has been ended. This should generally never happen unless you have bad rule definitions.");
}
}
return cartChanged;
}
Aggregations