use of org.apache.ofbiz.order.shoppingcart.ShoppingCartItem in project ofbiz-framework by apache.
the class PayPalServices method getExpressCheckout.
public static Map<String, Object> getExpressCheckout(DispatchContext dctx, Map<String, Object> context) {
Locale locale = (Locale) context.get("locale");
LocalDispatcher dispatcher = dctx.getDispatcher();
Delegator delegator = dctx.getDelegator();
ShoppingCart cart = (ShoppingCart) context.get("cart");
GenericValue payPalConfig = getPaymentMethodGatewayPayPal(dctx, context, null);
if (payPalConfig == null) {
return ServiceUtil.returnError(UtilProperties.getMessage(resource, "AccountingPayPalPaymentGatewayConfigCannotFind", locale));
}
NVPEncoder encoder = new NVPEncoder();
encoder.add("METHOD", "GetExpressCheckoutDetails");
String token = (String) cart.getAttribute("payPalCheckoutToken");
if (UtilValidate.isNotEmpty(token)) {
encoder.add("TOKEN", token);
} else {
return ServiceUtil.returnError(UtilProperties.getMessage(resource, "AccountingPayPalTokenNotFound", locale));
}
NVPDecoder decoder;
try {
decoder = sendNVPRequest(payPalConfig, encoder);
} catch (PayPalException e) {
Debug.logError(e, module);
return ServiceUtil.returnError(e.getMessage());
}
if (UtilValidate.isNotEmpty(decoder.get("NOTE"))) {
cart.addOrderNote(decoder.get("NOTE"));
}
if (cart.getUserLogin() == null) {
try {
GenericValue userLogin = EntityQuery.use(delegator).from("UserLogin").where("userLoginId", "anonymous").queryOne();
try {
cart.setUserLogin(userLogin, dispatcher);
} catch (CartItemModifyException e) {
Debug.logError(e, module);
return ServiceUtil.returnError(e.getMessage());
}
} catch (GenericEntityException e) {
Debug.logError(e, module);
return ServiceUtil.returnError(e.getMessage());
}
}
boolean anon = "anonymous".equals(cart.getUserLogin().getString("userLoginId"));
// Even if anon, a party could already have been created
String partyId = cart.getOrderPartyId();
if (partyId == null && anon) {
// Check nothing has been set on the anon userLogin either
partyId = cart.getUserLogin() != null ? cart.getUserLogin().getString("partyId") : null;
cart.setOrderPartyId(partyId);
}
if (partyId != null) {
GenericValue party = null;
try {
party = EntityQuery.use(delegator).from("Party").where("partyId", partyId).queryOne();
} catch (GenericEntityException e) {
Debug.logError(e, module);
}
if (party == null) {
partyId = null;
}
}
Map<String, Object> inMap = new HashMap<String, Object>();
Map<String, Object> outMap = null;
// Create the person if necessary
boolean newParty = false;
if (partyId == null) {
newParty = true;
inMap.put("userLogin", cart.getUserLogin());
inMap.put("personalTitle", decoder.get("SALUTATION"));
inMap.put("firstName", decoder.get("FIRSTNAME"));
inMap.put("middleName", decoder.get("MIDDLENAME"));
inMap.put("lastName", decoder.get("LASTNAME"));
inMap.put("suffix", decoder.get("SUFFIX"));
try {
outMap = dispatcher.runSync("createPerson", inMap);
partyId = (String) outMap.get("partyId");
cart.setOrderPartyId(partyId);
cart.getUserLogin().setString("partyId", partyId);
inMap.clear();
inMap.put("userLogin", cart.getUserLogin());
inMap.put("partyId", partyId);
inMap.put("roleTypeId", "CUSTOMER");
dispatcher.runSync("createPartyRole", inMap);
} catch (GenericServiceException e) {
Debug.logError(e, module);
return ServiceUtil.returnError(e.getMessage());
}
}
// Create a new email address if necessary
String emailContactMechId = null;
String emailContactPurposeTypeId = "PRIMARY_EMAIL";
String emailAddress = decoder.get("EMAIL");
if (!newParty) {
EntityCondition cond = EntityCondition.makeCondition(UtilMisc.toList(EntityCondition.makeCondition(UtilMisc.toMap("partyId", partyId, "contactMechTypeId", "EMAIL_ADDRESS")), EntityCondition.makeCondition(EntityFunction.UPPER_FIELD("infoString"), EntityComparisonOperator.EQUALS, EntityFunction.UPPER(emailAddress))));
try {
GenericValue matchingEmail = EntityQuery.use(delegator).from("PartyAndContactMech").where(cond).orderBy("fromDate").filterByDate().queryFirst();
if (matchingEmail != null) {
emailContactMechId = matchingEmail.getString("contactMechId");
} else {
// No email found so we'll need to create one but first check if it should be PRIMARY or just BILLING
long primaryEmails = EntityQuery.use(delegator).from("PartyContactWithPurpose").where("partyId", partyId, "contactMechTypeId", "EMAIL_ADDRESS", "contactMechPurposeTypeId", "PRIMARY_EMAIL").filterByDate("contactFromDate", "contactThruDate", "purposeFromDate", "purposeThruDate").queryCount();
if (primaryEmails > 0)
emailContactPurposeTypeId = "BILLING_EMAIL";
}
} catch (GenericEntityException e) {
Debug.logError(e, module);
}
}
if (emailContactMechId == null) {
inMap.clear();
inMap.put("userLogin", cart.getUserLogin());
inMap.put("contactMechPurposeTypeId", emailContactPurposeTypeId);
inMap.put("emailAddress", emailAddress);
inMap.put("partyId", partyId);
inMap.put("roleTypeId", "CUSTOMER");
// Going to assume PayPal has taken care of this for us
inMap.put("verified", "Y");
inMap.put("fromDate", UtilDateTime.nowTimestamp());
try {
outMap = dispatcher.runSync("createPartyEmailAddress", inMap);
emailContactMechId = (String) outMap.get("contactMechId");
} catch (GenericServiceException e) {
Debug.logError(e, module);
return ServiceUtil.returnError(e.getMessage());
}
}
cart.addContactMech("ORDER_EMAIL", emailContactMechId);
// Phone number
String phoneNumber = decoder.get("PHONENUM");
String phoneContactId = null;
if (phoneNumber != null) {
inMap.clear();
if (phoneNumber.startsWith("+")) {
// International, format is +XXX XXXXXXXX which we'll split into countryCode + contactNumber
String[] phoneNumbers = phoneNumber.split(" ");
inMap.put("countryCode", StringUtil.removeNonNumeric(phoneNumbers[0]));
inMap.put("contactNumber", phoneNumbers[1]);
} else {
// U.S., format is XXX-XXX-XXXX which we'll split into areaCode + contactNumber
inMap.put("countryCode", "1");
String[] phoneNumbers = phoneNumber.split("-");
inMap.put("areaCode", phoneNumbers[0]);
inMap.put("contactNumber", phoneNumbers[1] + phoneNumbers[2]);
}
inMap.put("userLogin", cart.getUserLogin());
inMap.put("partyId", partyId);
try {
outMap = dispatcher.runSync("createUpdatePartyTelecomNumber", inMap);
phoneContactId = (String) outMap.get("contactMechId");
cart.addContactMech("PHONE_BILLING", phoneContactId);
} catch (GenericServiceException e) {
Debug.logError(e, module);
}
}
// Create a new Postal Address if necessary
String postalContactId = null;
boolean needsShippingPurpose = true;
// if the cart for some reason already has a billing address, we'll leave it be
boolean needsBillingPurpose = (cart.getContactMech("BILLING_LOCATION") == null);
Map<String, Object> postalMap = new HashMap<String, Object>();
postalMap.put("toName", decoder.get("SHIPTONAME"));
postalMap.put("address1", decoder.get("SHIPTOSTREET"));
postalMap.put("address2", decoder.get("SHIPTOSTREET2"));
postalMap.put("city", decoder.get("SHIPTOCITY"));
String countryGeoId = PayPalServices.getCountryGeoIdFromGeoCode(decoder.get("SHIPTOCOUNTRYCODE"), delegator);
postalMap.put("countryGeoId", countryGeoId);
postalMap.put("stateProvinceGeoId", parseStateProvinceGeoId(decoder.get("SHIPTOSTATE"), countryGeoId, delegator));
postalMap.put("postalCode", decoder.get("SHIPTOZIP"));
if (!newParty) {
// We want an exact match only
EntityCondition cond = EntityCondition.makeCondition(UtilMisc.toList(EntityCondition.makeCondition(postalMap), EntityCondition.makeCondition(UtilMisc.toMap("attnName", null, "directions", null, "postalCodeExt", null, "postalCodeGeoId", null)), EntityCondition.makeCondition("partyId", partyId)));
try {
GenericValue postalMatch = EntityQuery.use(delegator).from("PartyAndPostalAddress").where(cond).orderBy("fromDate").filterByDate().queryFirst();
if (postalMatch != null) {
postalContactId = postalMatch.getString("contactMechId");
List<GenericValue> postalPurposes = EntityQuery.use(delegator).from("PartyContactMechPurpose").where("partyId", partyId, "contactMechId", postalContactId).filterByDate().queryList();
List<Object> purposeStrings = EntityUtil.getFieldListFromEntityList(postalPurposes, "contactMechPurposeTypeId", false);
if (UtilValidate.isNotEmpty(purposeStrings) && purposeStrings.contains("SHIPPING_LOCATION")) {
needsShippingPurpose = false;
}
if (needsBillingPurpose && UtilValidate.isNotEmpty(purposeStrings) && purposeStrings.contains("BILLING_LOCATION")) {
needsBillingPurpose = false;
}
}
} catch (GenericEntityException e) {
Debug.logError(e, module);
}
}
if (postalContactId == null) {
postalMap.put("userLogin", cart.getUserLogin());
postalMap.put("fromDate", UtilDateTime.nowTimestamp());
try {
outMap = dispatcher.runSync("createPartyPostalAddress", postalMap);
postalContactId = (String) outMap.get("contactMechId");
} catch (GenericServiceException e) {
Debug.logError(e, module);
return ServiceUtil.returnError(e.getMessage());
}
}
if (needsShippingPurpose || needsBillingPurpose) {
inMap.clear();
inMap.put("userLogin", cart.getUserLogin());
inMap.put("contactMechId", postalContactId);
inMap.put("partyId", partyId);
try {
if (needsShippingPurpose) {
inMap.put("contactMechPurposeTypeId", "SHIPPING_LOCATION");
dispatcher.runSync("createPartyContactMechPurpose", inMap);
}
if (needsBillingPurpose) {
inMap.put("contactMechPurposeTypeId", "BILLING_LOCATION");
dispatcher.runSync("createPartyContactMechPurpose", inMap);
}
} catch (GenericServiceException e) {
// Not the end of the world, we'll carry on
Debug.logInfo(e.getMessage(), module);
}
}
// Load the selected shipping method - thanks to PayPal's less than sane API all we've to work with is the shipping option label
// that was shown to the customer
String shipMethod = decoder.get("SHIPPINGOPTIONNAME");
if ("Calculated Offline".equals(shipMethod)) {
cart.setAllCarrierPartyId("_NA_");
cart.setAllShipmentMethodTypeId("NO_SHIPPING");
} else {
String[] shipMethodSplit = shipMethod.split(" - ");
cart.setAllCarrierPartyId(shipMethodSplit[0]);
String shippingMethodTypeDesc = StringUtils.join(shipMethodSplit, " - ", 1, shipMethodSplit.length);
try {
GenericValue shipmentMethod = EntityQuery.use(delegator).from("ProductStoreShipmentMethView").where("productStoreId", cart.getProductStoreId(), "partyId", shipMethodSplit[0], "roleTypeId", "CARRIER", "description", shippingMethodTypeDesc).queryFirst();
cart.setAllShipmentMethodTypeId(shipmentMethod.getString("shipmentMethodTypeId"));
} catch (GenericEntityException e1) {
Debug.logError(e1, module);
}
}
// Get rid of any excess ship groups
List<CartShipInfo> shipGroups = cart.getShipGroups();
for (int i = 1; i < shipGroups.size(); i++) {
Map<ShoppingCartItem, BigDecimal> items = cart.getShipGroupItems(i);
for (Map.Entry<ShoppingCartItem, BigDecimal> entry : items.entrySet()) {
cart.positionItemToGroup(entry.getKey(), entry.getValue(), i, 0, false);
}
}
cart.cleanUpShipGroups();
cart.setAllShippingContactMechId(postalContactId);
Map<String, Object> result = ShippingEvents.getShipGroupEstimate(dispatcher, delegator, cart, 0);
if (result.get(ModelService.RESPONSE_MESSAGE).equals(ModelService.RESPOND_ERROR)) {
return ServiceUtil.returnError((String) result.get(ModelService.ERROR_MESSAGE));
}
BigDecimal shippingTotal = (BigDecimal) result.get("shippingTotal");
if (shippingTotal == null) {
shippingTotal = BigDecimal.ZERO;
}
cart.setItemShipGroupEstimate(shippingTotal, 0);
CheckOutHelper cho = new CheckOutHelper(dispatcher, delegator, cart);
try {
cho.calcAndAddTax();
} catch (GeneralException e) {
Debug.logError(e, module);
return ServiceUtil.returnError(e.getMessage());
}
// Create the PayPal payment method
inMap.clear();
inMap.put("userLogin", cart.getUserLogin());
inMap.put("partyId", partyId);
inMap.put("contactMechId", postalContactId);
inMap.put("fromDate", UtilDateTime.nowTimestamp());
inMap.put("payerId", decoder.get("PAYERID"));
inMap.put("expressCheckoutToken", token);
inMap.put("payerStatus", decoder.get("PAYERSTATUS"));
try {
outMap = dispatcher.runSync("createPayPalPaymentMethod", inMap);
} catch (GenericServiceException e) {
Debug.logError(e, module);
return ServiceUtil.returnError(e.getMessage());
}
String paymentMethodId = (String) outMap.get("paymentMethodId");
cart.clearPayments();
BigDecimal maxAmount = cart.getGrandTotal().setScale(2, RoundingMode.HALF_UP);
cart.addPaymentAmount(paymentMethodId, maxAmount, true);
return ServiceUtil.returnSuccess();
}
use of org.apache.ofbiz.order.shoppingcart.ShoppingCartItem in project ofbiz-framework by apache.
the class OrderServices method runSubscriptionAutoReorders.
public static Map<String, Object> runSubscriptionAutoReorders(DispatchContext dctx, Map<String, ? extends Object> context) {
LocalDispatcher dispatcher = dctx.getDispatcher();
Delegator delegator = dctx.getDelegator();
GenericValue userLogin = (GenericValue) context.get("userLogin");
Locale locale = (Locale) context.get("locale");
int count = 0;
Map<String, Object> result = null;
boolean beganTransaction = false;
List<EntityExpr> exprs = UtilMisc.toList(EntityCondition.makeCondition("automaticExtend", EntityOperator.EQUALS, "Y"), EntityCondition.makeCondition("orderId", EntityOperator.NOT_EQUAL, null), EntityCondition.makeCondition("productId", EntityOperator.NOT_EQUAL, null));
try {
beganTransaction = TransactionUtil.begin();
} catch (GenericTransactionException e1) {
Debug.logError(e1, "[Delegator] Could not begin transaction: " + e1.toString(), module);
}
try (EntityListIterator eli = EntityQuery.use(delegator).from("Subscription").where(exprs).queryIterator()) {
if (eli != null) {
GenericValue subscription;
while (((subscription = eli.next()) != null)) {
Calendar endDate = Calendar.getInstance();
endDate.setTime(UtilDateTime.nowTimestamp());
// Check if today date + cancel period (if provided) is earlier than the thrudate
int field = Calendar.MONTH;
if (subscription.get("canclAutmExtTime") != null && subscription.get("canclAutmExtTimeUomId") != null) {
if ("TF_day".equals(subscription.getString("canclAutmExtTimeUomId"))) {
field = Calendar.DAY_OF_YEAR;
} else if ("TF_wk".equals(subscription.getString("canclAutmExtTimeUomId"))) {
field = Calendar.WEEK_OF_YEAR;
} else if ("TF_mon".equals(subscription.getString("canclAutmExtTimeUomId"))) {
field = Calendar.MONTH;
} else if ("TF_yr".equals(subscription.getString("canclAutmExtTimeUomId"))) {
field = Calendar.YEAR;
} else {
Debug.logWarning("Don't know anything about canclAutmExtTimeUomId [" + subscription.getString("canclAutmExtTimeUomId") + "], defaulting to month", module);
}
endDate.add(field, Integer.parseInt(subscription.getString("canclAutmExtTime")));
}
Calendar endDateSubscription = Calendar.getInstance();
endDateSubscription.setTime(subscription.getTimestamp("thruDate"));
if (endDate.before(endDateSubscription)) {
// nor expired yet.....
continue;
}
result = dispatcher.runSync("loadCartFromOrder", UtilMisc.toMap("orderId", subscription.get("orderId"), "userLogin", userLogin));
if (ServiceUtil.isError(result)) {
return ServiceUtil.returnError(ServiceUtil.getErrorMessage(result));
}
ShoppingCart cart = (ShoppingCart) result.get("shoppingCart");
// remove former orderId from cart (would cause duplicate entry).
// orderId is set by order-creation services (including store-specific prefixes, e.g.)
cart.setOrderId(null);
// only keep the orderitem with the related product.
List<ShoppingCartItem> cartItems = cart.items();
for (ShoppingCartItem shoppingCartItem : cartItems) {
if (!subscription.get("productId").equals(shoppingCartItem.getProductId())) {
cart.removeCartItem(shoppingCartItem, dispatcher);
}
}
CheckOutHelper helper = new CheckOutHelper(dispatcher, delegator, cart);
// store the order
Map<String, Object> createResp = helper.createOrder(userLogin);
if (createResp != null && ServiceUtil.isError(createResp)) {
Debug.logError("Cannot create order for shopping list - " + subscription, module);
} else {
String orderId = (String) createResp.get("orderId");
// authorize the payments
Map<String, Object> payRes = null;
try {
payRes = helper.processPayment(ProductStoreWorker.getProductStore(cart.getProductStoreId(), delegator), userLogin);
} catch (GeneralException e) {
Debug.logError(e, module);
}
if (payRes != null && ServiceUtil.isError(payRes)) {
Debug.logError("Payment processing problems with shopping list - " + subscription, module);
}
// remove the automatic extension flag
subscription.put("automaticExtend", "N");
subscription.store();
// send notification
if (orderId != null) {
dispatcher.runAsync("sendOrderPayRetryNotification", UtilMisc.toMap("orderId", orderId));
}
count++;
}
}
}
} catch (GenericServiceException e) {
Debug.logError("Could call service to create cart", module);
return ServiceUtil.returnError(e.toString());
} catch (CartItemModifyException e) {
Debug.logError("Could not modify cart: " + e.toString(), module);
return ServiceUtil.returnError(e.toString());
} catch (GenericEntityException e) {
try {
// only rollback the transaction if we started one...
TransactionUtil.rollback(beganTransaction, "Error creating subscription auto-reorders", e);
} catch (GenericEntityException e2) {
Debug.logError(e2, "[Delegator] Could not rollback transaction: " + e2.toString(), module);
}
Debug.logError(e, "Error while creating new shopping list based automatic reorder" + e.toString(), module);
return ServiceUtil.returnError(UtilProperties.getMessage(resource, "OrderShoppingListCreationError", UtilMisc.toMap("errorString", e.toString()), locale));
} finally {
try {
// only commit the transaction if we started one... this will throw an exception if it fails
TransactionUtil.commit(beganTransaction);
} catch (GenericEntityException e) {
Debug.logError(e, "Could not commit transaction for creating new shopping list based automatic reorder", module);
}
}
return ServiceUtil.returnSuccess(UtilProperties.getMessage(resource, "OrderRunSubscriptionAutoReorders", UtilMisc.toMap("count", count), locale));
}
use of org.apache.ofbiz.order.shoppingcart.ShoppingCartItem in project ofbiz-framework by apache.
the class OrderServices method checkCreateDropShipPurchaseOrders.
public static Map<String, Object> checkCreateDropShipPurchaseOrders(DispatchContext ctx, Map<String, ? extends Object> context) {
Delegator delegator = ctx.getDelegator();
LocalDispatcher dispatcher = ctx.getDispatcher();
// TODO (use the "system" user)
GenericValue userLogin = (GenericValue) context.get("userLogin");
String orderId = (String) context.get("orderId");
Locale locale = (Locale) context.get("locale");
OrderReadHelper orh = new OrderReadHelper(delegator, orderId);
try {
// if sales order
if ("SALES_ORDER".equals(orh.getOrderTypeId())) {
// get the order's ship groups
for (GenericValue shipGroup : orh.getOrderItemShipGroups()) {
if (UtilValidate.isNotEmpty(shipGroup.getString("supplierPartyId"))) {
// This ship group is a drop shipment: we create a purchase order for it
String supplierPartyId = shipGroup.getString("supplierPartyId");
// Set supplier preferred currency for drop-ship (PO) order to support multi currency
GenericValue supplierParty = EntityQuery.use(delegator).from("Party").where("partyId", supplierPartyId).queryOne();
String currencyUomId = supplierParty.getString("preferredCurrencyUomId");
// If supplier currency not found then set currency of sales order
if (UtilValidate.isEmpty(currencyUomId)) {
currencyUomId = orh.getCurrency();
}
// create the cart
ShoppingCart cart = new ShoppingCart(delegator, orh.getProductStoreId(), null, currencyUomId);
cart.setOrderType("PURCHASE_ORDER");
// Company
cart.setBillToCustomerPartyId(cart.getBillFromVendorPartyId());
cart.setBillFromVendorPartyId(supplierPartyId);
cart.setOrderPartyId(supplierPartyId);
cart.setAgreementId(shipGroup.getString("supplierAgreementId"));
// Get the items associated to it and create po
List<GenericValue> items = orh.getValidOrderItems(shipGroup.getString("shipGroupSeqId"));
if (UtilValidate.isNotEmpty(items)) {
for (GenericValue item : items) {
try {
int itemIndex = cart.addOrIncreaseItem(item.getString("productId"), // amount
null, item.getBigDecimal("quantity"), // reserv
null, // reserv
null, // reserv
null, item.getTimestamp("shipBeforeDate"), item.getTimestamp("shipAfterDate"), null, null, null, null, null, null, null, dispatcher);
ShoppingCartItem sci = cart.findCartItem(itemIndex);
sci.setAssociatedOrderId(orderId);
sci.setAssociatedOrderItemSeqId(item.getString("orderItemSeqId"));
sci.setOrderItemAssocTypeId("DROP_SHIPMENT");
} catch (CartItemModifyException | ItemNotFoundException e) {
return ServiceUtil.returnError(UtilProperties.getMessage(resource, "OrderOrderCreatingDropShipmentsError", UtilMisc.toMap("orderId", orderId, "errorString", e.getMessage()), locale));
}
}
}
// If there are indeed items to drop ship, then create the purchase order
if (UtilValidate.isNotEmpty(cart.items())) {
// resolve supplier promotion
ProductPromoWorker.doPromotions(cart, dispatcher);
// set checkout options
cart.setDefaultCheckoutOptions(dispatcher);
// the shipping address is the one of the customer
cart.setAllShippingContactMechId(shipGroup.getString("contactMechId"));
// associate ship groups of sales and purchase orders
ShoppingCart.CartShipInfo cartShipInfo = cart.getShipGroups().get(0);
cartShipInfo.setAssociatedShipGroupSeqId(shipGroup.getString("shipGroupSeqId"));
// create the order
CheckOutHelper coh = new CheckOutHelper(dispatcher, delegator, cart);
coh.createOrder(userLogin);
} else {
// if there are no items to drop ship, then clear out the supplier partyId
Debug.logWarning("No drop ship items found for order [" + shipGroup.getString("orderId") + "] and ship group [" + shipGroup.getString("shipGroupSeqId") + "] and supplier party [" + shipGroup.getString("supplierPartyId") + "]. Supplier party information will be cleared for this ship group", module);
shipGroup.set("supplierPartyId", null);
shipGroup.store();
}
}
}
}
} catch (GenericEntityException exc) {
// TODO: imporve error handling
return ServiceUtil.returnError(UtilProperties.getMessage(resource, "OrderOrderCreatingDropShipmentsError", UtilMisc.toMap("orderId", orderId, "errorString", exc.getMessage()), locale));
}
return ServiceUtil.returnSuccess();
}
use of org.apache.ofbiz.order.shoppingcart.ShoppingCartItem in project ofbiz-framework by apache.
the class ProductPromoWorker method performAction.
public static void performAction(ActionResultInfo actionResultInfo, GenericValue productPromoAction, ShoppingCart cart, Delegator delegator, LocalDispatcher dispatcher, Timestamp nowTimestamp) throws GenericEntityException, CartItemModifyException {
String productPromoActionEnumId = productPromoAction.getString("productPromoActionEnumId");
if ("PROMO_SERVICE".equals(productPromoActionEnumId)) {
Map<String, Object> serviceCtx = UtilMisc.<String, Object>toMap("productPromoAction", productPromoAction, "shoppingCart", cart, "nowTimestamp", nowTimestamp, "actionResultInfo", actionResultInfo);
String serviceName = productPromoAction.getString("serviceName");
Map<String, Object> actionResult;
try {
actionResult = dispatcher.runSync(serviceName, serviceCtx);
} catch (GenericServiceException e) {
Debug.logError("Error calling promo action service [" + serviceName + "]", module);
throw new CartItemModifyException("Error calling promo action service [" + serviceName + "]", e);
}
if (ServiceUtil.isError(actionResult)) {
Debug.logError("Error calling promo action service [" + serviceName + "], result is: " + actionResult, module);
throw new CartItemModifyException(ServiceUtil.getErrorMessage(actionResult));
}
CartItemModifyException cartItemModifyException = (CartItemModifyException) actionResult.get("cartItemModifyException");
if (cartItemModifyException != null) {
throw cartItemModifyException;
}
} else if ("PROMO_GWP".equals(productPromoActionEnumId)) {
String productStoreId = cart.getProductStoreId();
// the code was in there for this, so even though I don't think we want to restrict this, just adding this flag to make it easy to change; could make option dynamic, but now implied by the use limit
boolean allowMultipleGwp = true;
Integer itemLoc = findPromoItem(productPromoAction, cart);
if (!allowMultipleGwp && itemLoc != null) {
if (Debug.verboseOn()) {
Debug.logVerbose("Not adding promo item, already there; action: " + productPromoAction, module);
}
actionResultInfo.ranAction = false;
} else {
BigDecimal quantity;
if (productPromoAction.get("quantity") != null) {
quantity = productPromoAction.getBigDecimal("quantity");
} else {
if ("Y".equals(productPromoAction.get("useCartQuantity"))) {
quantity = BigDecimal.ZERO;
List<ShoppingCartItem> used = getCartItemsUsed(cart, productPromoAction);
for (ShoppingCartItem item : used) {
BigDecimal available = item.getPromoQuantityAvailable();
quantity = quantity.add(available).add(item.getPromoQuantityCandidateUseActionAndAllConds(productPromoAction));
item.addPromoQuantityCandidateUse(available, productPromoAction, false);
}
} else {
quantity = BigDecimal.ZERO;
}
}
List<String> optionProductIds = new LinkedList<>();
String productId = productPromoAction.getString("productId");
GenericValue product = null;
if (UtilValidate.isNotEmpty(productId)) {
product = EntityQuery.use(delegator).from("Product").where("productId", productId).cache().queryOne();
if (product == null) {
String errMsg = "GWP Product not found with ID [" + productId + "] for ProductPromoAction [" + productPromoAction.get("productPromoId") + ":" + productPromoAction.get("productPromoRuleId") + ":" + productPromoAction.get("productPromoActionSeqId") + "]";
Debug.logError(errMsg, module);
throw new CartItemModifyException(errMsg);
}
if ("Y".equals(product.getString("isVirtual"))) {
List<GenericValue> productAssocs = EntityUtil.filterByDate(product.getRelated("MainProductAssoc", UtilMisc.toMap("productAssocTypeId", "PRODUCT_VARIANT"), UtilMisc.toList("sequenceNum"), true));
for (GenericValue productAssoc : productAssocs) {
optionProductIds.add(productAssoc.getString("productIdTo"));
}
productId = null;
product = null;
} else {
// NOTE: even though the store may not require inventory for purchase, we will always require inventory for gifts
try {
// get the quantity in cart for inventory check
BigDecimal quantityAlreadyInCart = BigDecimal.ZERO;
List<ShoppingCartItem> matchingItems = cart.findAllCartItems(productId);
for (ShoppingCartItem item : matchingItems) {
quantityAlreadyInCart = quantityAlreadyInCart.add(item.getQuantity());
}
Map<String, Object> invReqResult = dispatcher.runSync("isStoreInventoryAvailable", UtilMisc.<String, Object>toMap("productStoreId", productStoreId, "productId", productId, "product", product, "quantity", quantity.add(quantityAlreadyInCart)));
if (ServiceUtil.isError(invReqResult)) {
Debug.logError("Error calling isStoreInventoryAvailable service, result is: " + invReqResult, module);
throw new CartItemModifyException(ServiceUtil.getErrorMessage(invReqResult));
} else if (!"Y".equals(invReqResult.get("available"))) {
Debug.logWarning(UtilProperties.getMessage(resource_error, "OrderNotApplyingGwpBecauseProductIdIsOutOfStockForProductPromoAction", UtilMisc.toMap("productId", productId, "productPromoAction", productPromoAction), cart.getLocale()), module);
productId = null;
product = null;
}
} catch (GenericServiceException e) {
String errMsg = "Fatal error calling inventory checking services: " + e.toString();
Debug.logError(e, errMsg, module);
throw new CartItemModifyException(errMsg);
}
}
}
// support multiple gift options if products are attached to the action, or if the productId on the action is a virtual product
Set<String> productIds = ProductPromoWorker.getPromoRuleActionProductIds(productPromoAction, delegator, nowTimestamp);
optionProductIds.addAll(productIds);
// make sure these optionProducts have inventory...
Iterator<String> optionProductIdIter = optionProductIds.iterator();
while (optionProductIdIter.hasNext()) {
String optionProductId = optionProductIdIter.next();
try {
// get the quantity in cart for inventory check
BigDecimal quantityAlreadyInCart = BigDecimal.ZERO;
List<ShoppingCartItem> matchingItems = cart.findAllCartItems(optionProductId);
for (ShoppingCartItem item : matchingItems) {
quantityAlreadyInCart = quantityAlreadyInCart.add(item.getQuantity());
}
Map<String, Object> invReqResult = dispatcher.runSync("isStoreInventoryAvailable", UtilMisc.<String, Object>toMap("productStoreId", productStoreId, "productId", optionProductId, "product", product, "quantity", quantity.add(quantityAlreadyInCart)));
if (ServiceUtil.isError(invReqResult)) {
Debug.logError("Error calling isStoreInventoryAvailable service, result is: " + invReqResult, module);
throw new CartItemModifyException(ServiceUtil.getErrorMessage(invReqResult));
} else if (!"Y".equals(invReqResult.get("available"))) {
optionProductIdIter.remove();
}
} catch (GenericServiceException e) {
String errMsg = "Fatal error calling inventory checking services: " + e.toString();
Debug.logError(e, errMsg, module);
throw new CartItemModifyException(errMsg);
}
}
// check to see if any desired productIds have been selected for this promo action
String alternateGwpProductId = cart.getDesiredAlternateGiftByAction(productPromoAction.getPrimaryKey());
if (UtilValidate.isNotEmpty(alternateGwpProductId)) {
// also check to make sure this isn't a spoofed ID somehow, check to see if it is in the Set
if (optionProductIds.contains(alternateGwpProductId)) {
if (UtilValidate.isNotEmpty(productId)) {
optionProductIds.add(productId);
}
optionProductIds.remove(alternateGwpProductId);
productId = alternateGwpProductId;
product = EntityQuery.use(delegator).from("Product").where("productId", productId).cache().queryOne();
} else {
Debug.logWarning(UtilProperties.getMessage(resource_error, "OrderAnAlternateGwpProductIdWasInPlaceButWasEitherNotValidOrIsNoLongerInStockForId", UtilMisc.toMap("alternateGwpProductId", alternateGwpProductId), cart.getLocale()), module);
}
}
// if product is null, get one from the productIds set
if (product == null && optionProductIds.size() > 0) {
// get the first from an iterator and remove it since it will be the current one
Iterator<String> optionProductIdTempIter = optionProductIds.iterator();
productId = optionProductIdTempIter.next();
optionProductIdTempIter.remove();
product = EntityQuery.use(delegator).from("Product").where("productId", productId).cache().queryOne();
}
if (product == null) {
// no product found to add as GWP, just return
return;
}
// pass null for cartLocation to add to end of cart, pass false for doPromotions to avoid infinite recursion
ShoppingCartItem gwpItem = null;
try {
// just leave the prodCatalogId null, this line won't be associated with a catalog
gwpItem = ShoppingCartItem.makeItem(null, product, null, quantity, null, null, null, null, null, null, null, null, null, null, null, null, dispatcher, cart, Boolean.FALSE, Boolean.TRUE, null, Boolean.FALSE, Boolean.FALSE);
if (optionProductIds.size() > 0) {
gwpItem.setAlternativeOptionProductIds(optionProductIds);
} else {
gwpItem.setAlternativeOptionProductIds(null);
}
} catch (CartItemModifyException e) {
Debug.logError(e.getMessage(), module);
throw e;
}
BigDecimal discountAmount = quantity.multiply(gwpItem.getBasePrice()).negate();
doOrderItemPromoAction(productPromoAction, gwpItem, discountAmount, "amount", delegator);
// set promo after create; note that to setQuantity we must clear this flag, setQuantity, then re-set the flag
gwpItem.setIsPromo(true);
if (Debug.verboseOn()) {
Debug.logVerbose("gwpItem adjustments: " + gwpItem.getAdjustments(), module);
}
actionResultInfo.ranAction = true;
actionResultInfo.totalDiscountAmount = discountAmount;
}
} else if ("PROMO_FREE_SHIPPING".equals(productPromoActionEnumId)) {
// this may look a bit funny: on each pass all rules that do free shipping will set their own rule id for it,
// and on unapply if the promo and rule ids are the same then it will clear it; essentially on any pass
// through the promos and rules if any free shipping should be there, it will be there
cart.addFreeShippingProductPromoAction(productPromoAction);
// don't consider this as a cart change?
actionResultInfo.ranAction = true;
// should probably set the totalDiscountAmount to something, but we have no idea what it will be, so leave at 0, will still get run
} else if ("PROMO_PROD_DISC".equals(productPromoActionEnumId)) {
BigDecimal quantityDesired = productPromoAction.get("quantity") == null ? BigDecimal.ONE : productPromoAction.getBigDecimal("quantity");
BigDecimal startingQuantity = quantityDesired;
BigDecimal discountAmountTotal = BigDecimal.ZERO;
Set<String> productIds = ProductPromoWorker.getPromoRuleActionProductIds(productPromoAction, delegator, nowTimestamp);
List<ShoppingCartItem> lineOrderedByBasePriceList = cart.getLineListOrderedByBasePrice(false);
Iterator<ShoppingCartItem> lineOrderedByBasePriceIter = lineOrderedByBasePriceList.iterator();
while (quantityDesired.compareTo(BigDecimal.ZERO) > 0 && lineOrderedByBasePriceIter.hasNext()) {
ShoppingCartItem cartItem = lineOrderedByBasePriceIter.next();
// only include if it is in the productId Set for this check and if it is not a Promo (GWP) item
GenericValue product = cartItem.getProduct();
String parentProductId = cartItem.getParentProductId();
boolean passedItemConds = checkConditionsForItem(productPromoAction, cart, cartItem, delegator, dispatcher, nowTimestamp);
if (passedItemConds && !cartItem.getIsPromo() && (productIds.contains(cartItem.getProductId()) || (parentProductId != null && productIds.contains(parentProductId))) && (product == null || !"N".equals(product.getString("includeInPromotions")))) {
// reduce quantity still needed to qualify for promo (quantityNeeded)
BigDecimal quantityUsed = cartItem.addPromoQuantityCandidateUse(quantityDesired, productPromoAction, false);
if (quantityUsed.compareTo(BigDecimal.ZERO) > 0) {
quantityDesired = quantityDesired.subtract(quantityUsed);
// create an adjustment and add it to the cartItem that implements the promotion action
BigDecimal percentModifier = productPromoAction.get("amount") == null ? BigDecimal.ZERO : productPromoAction.getBigDecimal("amount").movePointLeft(2);
BigDecimal lineAmount = quantityUsed.multiply(cartItem.getBasePrice()).multiply(cartItem.getRentalAdjustment());
BigDecimal discountAmount = lineAmount.multiply(percentModifier).negate();
discountAmountTotal = discountAmountTotal.add(discountAmount);
// not doing this any more, now distributing among conditions and actions (see call below): doOrderItemPromoAction(productPromoAction, cartItem, discountAmount, "amount", delegator);
}
}
}
if (quantityDesired.compareTo(startingQuantity) == 0 || quantityDesired.compareTo(BigDecimal.ZERO) > 0) {
// couldn't find any (or enough) cart items to give a discount to, don't consider action run
actionResultInfo.ranAction = false;
// clear out any action uses for this so they don't become part of anything else
cart.resetPromoRuleUse(productPromoAction.getString("productPromoId"), productPromoAction.getString("productPromoRuleId"));
} else {
BigDecimal totalAmount = getCartItemsUsedTotalAmount(cart, productPromoAction);
if (Debug.verboseOn()) {
Debug.logVerbose("Applying promo [" + productPromoAction.getPrimaryKey() + "]\n totalAmount=" + totalAmount + ", discountAmountTotal=" + discountAmountTotal, module);
}
distributeDiscountAmount(discountAmountTotal, totalAmount, getCartItemsUsed(cart, productPromoAction), productPromoAction, delegator);
actionResultInfo.ranAction = true;
actionResultInfo.totalDiscountAmount = discountAmountTotal;
actionResultInfo.quantityLeftInAction = quantityDesired;
}
} else if ("PROMO_PROD_AMDISC".equals(productPromoActionEnumId)) {
BigDecimal quantityDesired = productPromoAction.get("quantity") == null ? BigDecimal.ONE : productPromoAction.getBigDecimal("quantity");
BigDecimal startingQuantity = quantityDesired;
BigDecimal discountAmountTotal = BigDecimal.ZERO;
Set<String> productIds = ProductPromoWorker.getPromoRuleActionProductIds(productPromoAction, delegator, nowTimestamp);
List<ShoppingCartItem> lineOrderedByBasePriceList = cart.getLineListOrderedByBasePrice(false);
Iterator<ShoppingCartItem> lineOrderedByBasePriceIter = lineOrderedByBasePriceList.iterator();
while (quantityDesired.compareTo(BigDecimal.ZERO) > 0 && lineOrderedByBasePriceIter.hasNext()) {
ShoppingCartItem cartItem = lineOrderedByBasePriceIter.next();
// only include if it is in the productId Set for this check and if it is not a Promo (GWP) item
String parentProductId = cartItem.getParentProductId();
GenericValue product = cartItem.getProduct();
boolean passedItemConds = checkConditionsForItem(productPromoAction, cart, cartItem, delegator, dispatcher, nowTimestamp);
if (passedItemConds && !cartItem.getIsPromo() && (productIds.contains(cartItem.getProductId()) || (parentProductId != null && productIds.contains(parentProductId))) && (product == null || !"N".equals(product.getString("includeInPromotions")))) {
// reduce quantity still needed to qualify for promo (quantityNeeded)
BigDecimal quantityUsed = cartItem.addPromoQuantityCandidateUse(quantityDesired, productPromoAction, false);
quantityDesired = quantityDesired.subtract(quantityUsed);
// create an adjustment and add it to the cartItem that implements the promotion action
BigDecimal discount = productPromoAction.get("amount") == null ? BigDecimal.ZERO : productPromoAction.getBigDecimal("amount");
// don't allow the discount to be greater than the price
if (discount.compareTo(cartItem.getBasePrice().multiply(cartItem.getRentalAdjustment())) > 0) {
discount = cartItem.getBasePrice().multiply(cartItem.getRentalAdjustment());
}
BigDecimal discountAmount = quantityUsed.multiply(discount).negate();
discountAmountTotal = discountAmountTotal.add(discountAmount);
// not doing this any more, now distributing among conditions and actions (see call below): doOrderItemPromoAction(productPromoAction, cartItem, discountAmount, "amount", delegator);
}
}
if (quantityDesired.compareTo(startingQuantity) == 0) {
// couldn't find any cart items to give a discount to, don't consider action run
actionResultInfo.ranAction = false;
} else {
BigDecimal totalAmount = getCartItemsUsedTotalAmount(cart, productPromoAction);
if (Debug.verboseOn()) {
Debug.logVerbose("Applying promo [" + productPromoAction.getPrimaryKey() + "]\n totalAmount=" + totalAmount + ", discountAmountTotal=" + discountAmountTotal, module);
}
distributeDiscountAmount(discountAmountTotal, totalAmount, getCartItemsUsed(cart, productPromoAction), productPromoAction, delegator);
actionResultInfo.ranAction = true;
actionResultInfo.totalDiscountAmount = discountAmountTotal;
actionResultInfo.quantityLeftInAction = quantityDesired;
}
} else if ("PROMO_PROD_PRICE".equals(productPromoActionEnumId)) {
// with this we want the set of used items to be one price, so total the price for all used items, subtract the amount we want them to cost, and create an adjustment for what is left
BigDecimal quantityDesired = productPromoAction.get("quantity") == null ? BigDecimal.ONE : productPromoAction.getBigDecimal("quantity");
BigDecimal desiredAmount = productPromoAction.get("amount") == null ? BigDecimal.ZERO : productPromoAction.getBigDecimal("amount");
BigDecimal totalAmount = BigDecimal.ZERO;
Set<String> productIds = ProductPromoWorker.getPromoRuleActionProductIds(productPromoAction, delegator, nowTimestamp);
List<ShoppingCartItem> cartItemsUsed = new LinkedList<>();
List<ShoppingCartItem> lineOrderedByBasePriceList = cart.getLineListOrderedByBasePrice(false);
Iterator<ShoppingCartItem> lineOrderedByBasePriceIter = lineOrderedByBasePriceList.iterator();
while (quantityDesired.compareTo(BigDecimal.ZERO) > 0 && lineOrderedByBasePriceIter.hasNext()) {
ShoppingCartItem cartItem = lineOrderedByBasePriceIter.next();
// only include if it is in the productId Set for this check and if it is not a Promo (GWP) item
String parentProductId = cartItem.getParentProductId();
GenericValue product = cartItem.getProduct();
boolean passedItemConds = checkConditionsForItem(productPromoAction, cart, cartItem, delegator, dispatcher, nowTimestamp);
if (passedItemConds && !cartItem.getIsPromo() && (productIds.contains(cartItem.getProductId()) || (parentProductId != null && productIds.contains(parentProductId))) && (product == null || !"N".equals(product.getString("includeInPromotions")))) {
// reduce quantity still needed to qualify for promo (quantityNeeded)
BigDecimal quantityUsed = cartItem.addPromoQuantityCandidateUse(quantityDesired, productPromoAction, false);
if (quantityUsed.compareTo(BigDecimal.ZERO) > 0) {
quantityDesired = quantityDesired.subtract(quantityUsed);
totalAmount = totalAmount.add(quantityUsed.multiply(cartItem.getBasePrice()).multiply(cartItem.getRentalAdjustment()));
cartItemsUsed.add(cartItem);
}
}
}
if (totalAmount.compareTo(desiredAmount) > 0 && quantityDesired.compareTo(BigDecimal.ZERO) == 0) {
BigDecimal discountAmountTotal = totalAmount.subtract(desiredAmount).negate();
distributeDiscountAmount(discountAmountTotal, totalAmount, cartItemsUsed, productPromoAction, delegator);
actionResultInfo.ranAction = true;
actionResultInfo.totalDiscountAmount = discountAmountTotal;
// no use setting the quantityLeftInAction because that does not apply for buy X for $Y type promotions, it is all or nothing
} else {
actionResultInfo.ranAction = false;
// clear out any action uses for this so they don't become part of anything else
cart.resetPromoRuleUse(productPromoAction.getString("productPromoId"), productPromoAction.getString("productPromoRuleId"));
}
} else if ("PROMO_ORDER_PERCENT".equals(productPromoActionEnumId)) {
BigDecimal percentage = (productPromoAction.get("amount") == null ? BigDecimal.ZERO : (productPromoAction.getBigDecimal("amount").movePointLeft(2))).negate();
Set<String> productIds = ProductPromoWorker.getPromoRuleActionProductIds(productPromoAction, delegator, nowTimestamp);
BigDecimal amount;
if (productIds.isEmpty()) {
amount = cart.getSubTotalForPromotions().multiply(percentage);
} else {
amount = cart.getSubTotalForPromotions(productIds).multiply(percentage);
}
if (amount.compareTo(BigDecimal.ZERO) != 0) {
doOrderPromoAction(productPromoAction, cart, amount, "amount", delegator);
actionResultInfo.ranAction = true;
actionResultInfo.totalDiscountAmount = amount;
}
} else if ("PROMO_ORDER_AMOUNT".equals(productPromoActionEnumId)) {
BigDecimal amount = (productPromoAction.get("amount") == null ? BigDecimal.ZERO : productPromoAction.getBigDecimal("amount")).negate();
// if amount is greater than the order sub total, set equal to order sub total, this normally wouldn't happen because there should be a condition that the order total be above a certain amount, but just in case...
BigDecimal subTotal = cart.getSubTotalForPromotions();
if (amount.negate().compareTo(subTotal) > 0) {
amount = subTotal.negate();
}
if (amount.compareTo(BigDecimal.ZERO) != 0) {
doOrderPromoAction(productPromoAction, cart, amount, "amount", delegator);
actionResultInfo.ranAction = true;
actionResultInfo.totalDiscountAmount = amount;
}
} else if ("PROMO_PROD_SPPRC".equals(productPromoActionEnumId)) {
// if there are productIds associated with the action then restrict to those productIds, otherwise apply for all products
Set<String> productIds = ProductPromoWorker.getPromoRuleActionProductIds(productPromoAction, delegator, nowTimestamp);
// go through the cart items and for each product that has a specialPromoPrice use that price
for (ShoppingCartItem cartItem : cart.items()) {
String itemProductId = cartItem.getProductId();
if (UtilValidate.isEmpty(itemProductId)) {
continue;
}
if (productIds.size() > 0 && !productIds.contains(itemProductId)) {
continue;
}
if (cartItem.getSpecialPromoPrice() == null) {
continue;
}
// get difference between basePrice and specialPromoPrice and adjust for that
BigDecimal difference = cartItem.getBasePrice().multiply(cartItem.getRentalAdjustment()).subtract(cartItem.getSpecialPromoPrice()).negate();
if (difference.compareTo(BigDecimal.ZERO) != 0) {
BigDecimal quantityUsed = cartItem.addPromoQuantityCandidateUse(cartItem.getQuantity(), productPromoAction, false);
if (quantityUsed.compareTo(BigDecimal.ZERO) > 0) {
BigDecimal amount = difference.multiply(quantityUsed);
doOrderItemPromoAction(productPromoAction, cartItem, amount, "amount", delegator);
actionResultInfo.ranAction = true;
actionResultInfo.totalDiscountAmount = amount;
}
}
}
} else if ("PROMO_SHIP_CHARGE".equals(productPromoActionEnumId)) {
BigDecimal percentage = (productPromoAction.get("amount") == null ? BigDecimal.ZERO : (productPromoAction.getBigDecimal("amount").movePointLeft(2))).negate();
BigDecimal amount = cart.getTotalShipping().multiply(percentage);
if (amount.compareTo(BigDecimal.ZERO) != 0) {
int existingOrderPromoIndex = cart.getAdjustmentPromoIndex(productPromoAction.getString("productPromoId"));
if (existingOrderPromoIndex != -1 && cart.getAdjustment(existingOrderPromoIndex).getBigDecimal("amount").compareTo(amount) == 0) {
// already ran, no need to repeat
actionResultInfo.ranAction = false;
} else {
if (existingOrderPromoIndex != -1 && cart.getAdjustment(existingOrderPromoIndex).getBigDecimal("amount").compareTo(amount) != 0) {
cart.removeAdjustment(existingOrderPromoIndex);
}
doOrderPromoAction(productPromoAction, cart, amount, "amount", delegator);
actionResultInfo.ranAction = true;
actionResultInfo.totalDiscountAmount = amount;
}
}
} else if ("PROMO_TAX_PERCENT".equals(productPromoActionEnumId)) {
BigDecimal percentage = (productPromoAction.get("amount") == null ? BigDecimal.ZERO : (productPromoAction.getBigDecimal("amount").movePointLeft(2))).negate();
BigDecimal amount = cart.getTotalSalesTax().multiply(percentage);
if (amount.compareTo(BigDecimal.ZERO) != 0) {
doOrderPromoAction(productPromoAction, cart, amount, "amount", delegator);
actionResultInfo.ranAction = true;
actionResultInfo.totalDiscountAmount = amount;
}
} else {
Debug.logError("An un-supported productPromoActionType was used: " + productPromoActionEnumId + ", not performing any action", module);
actionResultInfo.ranAction = false;
}
// in action, if doesn't have enough quantity to use the promo at all, remove candidate promo uses and increment promoQuantityUsed; this should go for all actions, if any action runs we confirm
if (actionResultInfo.ranAction) {
cart.confirmPromoRuleUse(productPromoAction.getString("productPromoId"), productPromoAction.getString("productPromoRuleId"));
} else {
cart.resetPromoRuleUse(productPromoAction.getString("productPromoId"), productPromoAction.getString("productPromoRuleId"));
}
}
use of org.apache.ofbiz.order.shoppingcart.ShoppingCartItem in project ofbiz-framework by apache.
the class ProductPromoWorker method checkCondition.
private static boolean checkCondition(GenericValue productPromoCond, ShoppingCart cart, Delegator delegator, LocalDispatcher dispatcher, Timestamp nowTimestamp) throws GenericEntityException {
String condValue = productPromoCond.getString("condValue");
String otherValue = productPromoCond.getString("otherValue");
String inputParamEnumId = productPromoCond.getString("inputParamEnumId");
String operatorEnumId = productPromoCond.getString("operatorEnumId");
String shippingMethod = "";
String carrierPartyId = "";
if (otherValue != null && otherValue.contains("@")) {
carrierPartyId = otherValue.substring(0, otherValue.indexOf('@'));
shippingMethod = otherValue.substring(otherValue.indexOf('@') + 1);
otherValue = "";
}
String partyId = cart.getPartyId();
GenericValue userLogin = cart.getUserLogin();
if (userLogin == null) {
userLogin = cart.getAutoUserLogin();
}
if (Debug.verboseOn()) {
Debug.logVerbose("Checking promotion condition: " + productPromoCond, module);
}
Integer compareBase = null;
if ("PPIP_SERVICE".equals(inputParamEnumId)) {
Map<String, Object> serviceCtx = UtilMisc.<String, Object>toMap("productPromoCond", productPromoCond, "shoppingCart", cart, "nowTimestamp", nowTimestamp);
Map<String, Object> condResult;
try {
condResult = dispatcher.runSync(condValue, serviceCtx);
} catch (GenericServiceException e) {
Debug.logError(e, "Fatal error calling promo condition check service [" + condValue + "]", module);
return false;
}
if (ServiceUtil.isError(condResult)) {
Debug.logError("Error calling calling promo condition check service [" + condValue + "]", module);
return false;
}
Boolean directResult = (Boolean) condResult.get("directResult");
if (directResult != null) {
return directResult.booleanValue();
}
compareBase = (Integer) condResult.get("compareBase");
if (condResult.containsKey("operatorEnumId")) {
operatorEnumId = (String) condResult.get("operatorEnumId");
}
} else if ("PPIP_PRODUCT_AMOUNT".equals(inputParamEnumId)) {
// for this type of promo force the operatorEnumId = PPC_EQ, effectively ignore that setting because the comparison is implied in the code
operatorEnumId = "PPC_EQ";
// this type of condition requires items involved to not be involved in any other quantity consuming cond/action, and does not pro-rate the price, just uses the base price
BigDecimal amountNeeded = BigDecimal.ZERO;
if (UtilValidate.isNotEmpty(condValue)) {
amountNeeded = new BigDecimal(condValue);
}
Set<String> productIds = ProductPromoWorker.getPromoRuleCondProductIds(productPromoCond, delegator, nowTimestamp);
List<ShoppingCartItem> lineOrderedByBasePriceList = cart.getLineListOrderedByBasePrice(false);
Iterator<ShoppingCartItem> lineOrderedByBasePriceIter = lineOrderedByBasePriceList.iterator();
while (amountNeeded.compareTo(BigDecimal.ZERO) > 0 && lineOrderedByBasePriceIter.hasNext()) {
ShoppingCartItem cartItem = lineOrderedByBasePriceIter.next();
// only include if it is in the productId Set for this check and if it is not a Promo (GWP) item
GenericValue product = cartItem.getProduct();
String parentProductId = cartItem.getParentProductId();
boolean passedItemConds = checkConditionsForItem(productPromoCond, cart, cartItem, delegator, dispatcher, nowTimestamp);
if (passedItemConds && !cartItem.getIsPromo() && (productIds.contains(cartItem.getProductId()) || (parentProductId != null && productIds.contains(parentProductId))) && (product == null || !"N".equals(product.getString("includeInPromotions")))) {
BigDecimal basePrice = cartItem.getBasePrice();
// get a rough price, round it up to an integer
BigDecimal quantityNeeded = amountNeeded.divide(basePrice, generalRounding).setScale(0, RoundingMode.CEILING);
// reduce amount still needed to qualify for promo (amountNeeded)
BigDecimal quantity = cartItem.addPromoQuantityCandidateUse(quantityNeeded, productPromoCond, false);
// get pro-rated amount based on discount
amountNeeded = amountNeeded.subtract(quantity.multiply(basePrice));
}
}
// if amountNeeded > 0 then the promo condition failed, so remove candidate promo uses and increment the promoQuantityUsed to restore it
if (amountNeeded.compareTo(BigDecimal.ZERO) > 0) {
// failed, reset the entire rule, ie including all other conditions that might have been done before
cart.resetPromoRuleUse(productPromoCond.getString("productPromoId"), productPromoCond.getString("productPromoRuleId"));
compareBase = Integer.valueOf(-1);
} else {
// we got it, the conditions are in place...
compareBase = Integer.valueOf(0);
// NOTE: don't confirm promo rule use here, wait until actions are complete for the rule to do that
}
} else if ("PPIP_PRODUCT_TOTAL".equals(inputParamEnumId)) {
// this type of condition allows items involved to be involved in other quantity consuming cond/action, and does pro-rate the price
if (UtilValidate.isNotEmpty(condValue)) {
BigDecimal amountNeeded = new BigDecimal(condValue);
BigDecimal amountAvailable = BigDecimal.ZERO;
Set<String> productIds = ProductPromoWorker.getPromoRuleCondProductIds(productPromoCond, delegator, nowTimestamp);
List<ShoppingCartItem> lineOrderedByBasePriceList = cart.getLineListOrderedByBasePrice(false);
for (ShoppingCartItem cartItem : lineOrderedByBasePriceList) {
// only include if it is in the productId Set for this check and if it is not a Promo (GWP) item
GenericValue product = cartItem.getProduct();
String parentProductId = cartItem.getParentProductId();
boolean passedItemConds = checkConditionsForItem(productPromoCond, cart, cartItem, delegator, dispatcher, nowTimestamp);
if (passedItemConds && !cartItem.getIsPromo() && (productIds.contains(cartItem.getProductId()) || (parentProductId != null && productIds.contains(parentProductId))) && (product == null || !"N".equals(product.getString("includeInPromotions")))) {
// just count the entire sub-total of the item
amountAvailable = amountAvailable.add(cartItem.getItemSubTotal());
}
}
compareBase = Integer.valueOf(amountAvailable.compareTo(amountNeeded));
}
} else if ("PPIP_PRODUCT_QUANT".equals(inputParamEnumId)) {
if (operatorEnumId == null) {
// if the operator is not specified in the condition, then assume as default PPC_EQ (for backward compatibility)
operatorEnumId = "PPC_EQ";
}
BigDecimal quantityNeeded = BigDecimal.ONE;
if (UtilValidate.isNotEmpty(condValue)) {
quantityNeeded = new BigDecimal(condValue);
}
Set<String> productIds = ProductPromoWorker.getPromoRuleCondProductIds(productPromoCond, delegator, nowTimestamp);
List<ShoppingCartItem> lineOrderedByBasePriceList = cart.getLineListOrderedByBasePrice(false);
Iterator<ShoppingCartItem> lineOrderedByBasePriceIter = lineOrderedByBasePriceList.iterator();
while (quantityNeeded.compareTo(BigDecimal.ZERO) > 0 && lineOrderedByBasePriceIter.hasNext()) {
ShoppingCartItem cartItem = lineOrderedByBasePriceIter.next();
// only include if it is in the productId Set for this check and if it is not a Promo (GWP) item
GenericValue product = cartItem.getProduct();
String parentProductId = cartItem.getParentProductId();
boolean passedItemConds = checkConditionsForItem(productPromoCond, cart, cartItem, delegator, dispatcher, nowTimestamp);
if (passedItemConds && !cartItem.getIsPromo() && (productIds.contains(cartItem.getProductId()) || (parentProductId != null && productIds.contains(parentProductId))) && (product == null || !"N".equals(product.getString("includeInPromotions")))) {
// reduce quantity still needed to qualify for promo (quantityNeeded)
quantityNeeded = quantityNeeded.subtract(cartItem.addPromoQuantityCandidateUse(quantityNeeded, productPromoCond, !"PPC_EQ".equals(operatorEnumId)));
}
}
// if quantityNeeded > 0 then the promo condition failed, so remove candidate promo uses and increment the promoQuantityUsed to restore it
if (quantityNeeded.compareTo(BigDecimal.ZERO) > 0) {
// failed, reset the entire rule, ie including all other conditions that might have been done before
cart.resetPromoRuleUse(productPromoCond.getString("productPromoId"), productPromoCond.getString("productPromoRuleId"));
compareBase = Integer.valueOf(-1);
} else {
// we got it, the conditions are in place...
compareBase = Integer.valueOf(0);
// NOTE: don't confirm rpomo rule use here, wait until actions are complete for the rule to do that
}
} else if ("PPIP_NEW_ACCT".equals(inputParamEnumId)) {
if (UtilValidate.isNotEmpty(condValue)) {
BigDecimal acctDays = cart.getPartyDaysSinceCreated(nowTimestamp);
if (acctDays == null) {
// condition always fails if we don't know how many days since account created
return false;
}
compareBase = acctDays.compareTo(new BigDecimal(condValue));
}
} else if ("PPIP_PARTY_ID".equals(inputParamEnumId)) {
if (partyId != null && UtilValidate.isNotEmpty(condValue)) {
compareBase = Integer.valueOf(partyId.compareTo(condValue));
} else {
compareBase = Integer.valueOf(1);
}
} else if ("PPIP_PARTY_GRP_MEM".equals(inputParamEnumId)) {
if (UtilValidate.isEmpty(partyId) || UtilValidate.isEmpty(condValue)) {
compareBase = Integer.valueOf(1);
} else {
String groupPartyId = condValue;
if (partyId.equals(groupPartyId)) {
compareBase = Integer.valueOf(0);
} else {
// look for PartyRelationship with partyRelationshipTypeId=GROUP_ROLLUP, the partyIdTo is the group member, so the partyIdFrom is the groupPartyId
// and from/thru date within range
List<GenericValue> partyRelationshipList = EntityQuery.use(delegator).from("PartyRelationship").where("partyIdFrom", groupPartyId, "partyIdTo", partyId, "partyRelationshipTypeId", "GROUP_ROLLUP").cache(true).filterByDate().queryList();
if (UtilValidate.isNotEmpty(partyRelationshipList)) {
compareBase = Integer.valueOf(0);
} else {
compareBase = Integer.valueOf(checkConditionPartyHierarchy(delegator, nowTimestamp, groupPartyId, partyId));
}
}
}
} else if ("PPIP_PARTY_CLASS".equals(inputParamEnumId)) {
if (UtilValidate.isEmpty(partyId) || UtilValidate.isEmpty(condValue)) {
compareBase = Integer.valueOf(1);
} else {
String partyClassificationGroupId = condValue;
// find any PartyClassification
// and from/thru date within range
List<GenericValue> partyClassificationList = EntityQuery.use(delegator).from("PartyClassification").where("partyId", partyId, "partyClassificationGroupId", partyClassificationGroupId).cache(true).filterByDate().queryList();
// then 0 (equals), otherwise 1 (not equals)
if (UtilValidate.isNotEmpty(partyClassificationList)) {
compareBase = Integer.valueOf(0);
} else {
compareBase = Integer.valueOf(1);
}
}
} else if ("PPIP_ROLE_TYPE".equals(inputParamEnumId)) {
if (partyId != null && UtilValidate.isNotEmpty(condValue)) {
// if a PartyRole exists for this partyId and the specified roleTypeId
GenericValue partyRole = EntityQuery.use(delegator).from("PartyRole").where("partyId", partyId, "roleTypeId", condValue).cache(true).queryOne();
// then 0 (equals), otherwise 1 (not equals)
if (partyRole != null) {
compareBase = Integer.valueOf(0);
} else {
compareBase = Integer.valueOf(1);
}
} else {
compareBase = Integer.valueOf(1);
}
} else if ("PPIP_GEO_ID".equals(inputParamEnumId)) {
compareBase = Integer.valueOf(1);
GenericValue shippingAddress = cart.getShippingAddress();
if (UtilValidate.isNotEmpty(condValue) && shippingAddress != null) {
if (condValue.equals(shippingAddress.getString("countryGeoId")) || condValue.equals(shippingAddress.getString("countyGeoId")) || condValue.equals(shippingAddress.getString("postalCodeGeoId")) || condValue.equals(shippingAddress.getString("stateProvinceGeoId"))) {
compareBase = Integer.valueOf(0);
} else {
List<GenericValue> geoAssocList = EntityQuery.use(delegator).from("GeoAssoc").where("geoIdTo", condValue).queryList();
for (GenericValue geo : geoAssocList) {
if (geo.get("geoId").equals(shippingAddress.getString("countryGeoId")) || geo.get("geoId").equals(shippingAddress.getString("countyGeoId")) || geo.get("geoId").equals(shippingAddress.getString("postalCodeGeoId")) || condValue.equals(shippingAddress.getString("stateProvinceGeoId"))) {
compareBase = Integer.valueOf(0);
break;
}
}
}
}
} else if ("PPIP_ORDER_TOTAL".equals(inputParamEnumId)) {
if (UtilValidate.isNotEmpty(condValue)) {
BigDecimal orderSubTotal = cart.getSubTotalForPromotions();
if (Debug.verboseOn())
Debug.logVerbose("Doing order total compare: orderSubTotal=" + orderSubTotal, module);
compareBase = Integer.valueOf(orderSubTotal.compareTo(new BigDecimal(condValue)));
}
} else if ("PPIP_ORST_HIST".equals(inputParamEnumId)) {
// description="Order sub-total X in last Y Months"
if (partyId != null && userLogin != null && UtilValidate.isNotEmpty(condValue)) {
// call the getOrderedSummaryInformation service to get the sub-total
int monthsToInclude = 12;
if (otherValue != null) {
monthsToInclude = Integer.parseInt(otherValue);
}
Map<String, Object> serviceIn = UtilMisc.<String, Object>toMap("partyId", partyId, "roleTypeId", "PLACING_CUSTOMER", "orderTypeId", "SALES_ORDER", "statusId", "ORDER_COMPLETED", "monthsToInclude", Integer.valueOf(monthsToInclude), "userLogin", userLogin);
try {
Map<String, Object> result = dispatcher.runSync("getOrderedSummaryInformation", serviceIn);
if (ServiceUtil.isError(result)) {
Debug.logError("Error calling getOrderedSummaryInformation service for the PPIP_ORST_HIST ProductPromo condition input value: " + ServiceUtil.getErrorMessage(result), module);
return false;
} else {
BigDecimal orderSubTotal = (BigDecimal) result.get("totalSubRemainingAmount");
BigDecimal orderSubTotalAndCartSubTotal = orderSubTotal.add(cart.getSubTotal());
if (Debug.verboseOn())
Debug.logVerbose("Doing order history sub-total compare: orderSubTotal=" + orderSubTotal + ", for the last " + monthsToInclude + " months.", module);
compareBase = Integer.valueOf(orderSubTotalAndCartSubTotal.compareTo(new BigDecimal(condValue)));
}
} catch (GenericServiceException e) {
Debug.logError(e, "Error getting order history sub-total in the getOrderedSummaryInformation service, evaluating condition to false.", module);
return false;
}
} else {
return false;
}
} else if ("PPIP_ORST_YEAR".equals(inputParamEnumId)) {
// description="Order sub-total X since beginning of current year"
if (partyId != null && userLogin != null && UtilValidate.isNotEmpty(condValue)) {
// call the getOrderedSummaryInformation service to get the sub-total
Calendar calendar = Calendar.getInstance();
calendar.setTime(nowTimestamp);
int monthsToInclude = calendar.get(Calendar.MONTH) + 1;
Map<String, Object> serviceIn = UtilMisc.<String, Object>toMap("partyId", partyId, "roleTypeId", "PLACING_CUSTOMER", "orderTypeId", "SALES_ORDER", "statusId", "ORDER_COMPLETED", "monthsToInclude", Integer.valueOf(monthsToInclude), "userLogin", userLogin);
try {
Map<String, Object> result = dispatcher.runSync("getOrderedSummaryInformation", serviceIn);
if (ServiceUtil.isError(result)) {
Debug.logError("Error calling getOrderedSummaryInformation service for the PPIP_ORST_YEAR ProductPromo condition input value: " + ServiceUtil.getErrorMessage(result), module);
return false;
} else {
BigDecimal orderSubTotal = (BigDecimal) result.get("totalSubRemainingAmount");
if (Debug.verboseOn())
Debug.logVerbose("Doing order history sub-total compare: orderSubTotal=" + orderSubTotal + ", for the last " + monthsToInclude + " months.", module);
compareBase = Integer.valueOf(orderSubTotal.compareTo(new BigDecimal((condValue))));
}
} catch (GenericServiceException e) {
Debug.logError(e, "Error getting order history sub-total in the getOrderedSummaryInformation service, evaluating condition to false.", module);
return false;
}
}
} else if ("PPIP_ORST_LAST_YEAR".equals(inputParamEnumId)) {
// description="Order sub-total X since beginning of last year"
if (partyId != null && userLogin != null && UtilValidate.isNotEmpty(condValue)) {
// call the getOrderedSummaryInformation service to get the sub-total
Calendar calendar = Calendar.getInstance();
calendar.setTime(nowTimestamp);
int lastYear = calendar.get(Calendar.YEAR) - 1;
Calendar fromDateCalendar = Calendar.getInstance();
fromDateCalendar.set(lastYear, 0, 0, 0, 0);
Timestamp fromDate = new Timestamp(fromDateCalendar.getTime().getTime());
Calendar thruDateCalendar = Calendar.getInstance();
thruDateCalendar.set(lastYear, 12, 0, 0, 0);
Timestamp thruDate = new Timestamp(thruDateCalendar.getTime().getTime());
Map<String, Object> serviceIn = UtilMisc.toMap("partyId", partyId, "roleTypeId", "PLACING_CUSTOMER", "orderTypeId", "SALES_ORDER", "statusId", "ORDER_COMPLETED", "fromDate", fromDate, "thruDate", thruDate, "userLogin", userLogin);
try {
Map<String, Object> result = dispatcher.runSync("getOrderedSummaryInformation", serviceIn);
if (ServiceUtil.isError(result)) {
Debug.logError("Error calling getOrderedSummaryInformation service for the PPIP_ORST_LAST_YEAR ProductPromo condition input value: " + ServiceUtil.getErrorMessage(result), module);
return false;
} else {
Double orderSubTotal = (Double) result.get("totalSubRemainingAmount");
if (Debug.verboseOn())
Debug.logVerbose("Doing order history sub-total compare: orderSubTotal=" + orderSubTotal + ", for last year.", module);
compareBase = Integer.valueOf(orderSubTotal.compareTo(Double.valueOf(condValue)));
}
} catch (GenericServiceException e) {
Debug.logError(e, "Error getting order history sub-total in the getOrderedSummaryInformation service, evaluating condition to false.", module);
return false;
}
} else {
return false;
}
} else if ("PPIP_RECURRENCE".equals(inputParamEnumId)) {
if (UtilValidate.isNotEmpty(condValue)) {
compareBase = Integer.valueOf(1);
GenericValue recurrenceInfo = EntityQuery.use(delegator).from("RecurrenceInfo").where("recurrenceInfoId", condValue).cache().queryOne();
if (recurrenceInfo != null) {
RecurrenceInfo recurrence = null;
try {
recurrence = new RecurrenceInfo(recurrenceInfo);
} catch (RecurrenceInfoException e) {
Debug.logError(e, module);
}
// check the current recurrence
if (recurrence != null) {
if (recurrence.isValidCurrent()) {
compareBase = Integer.valueOf(0);
}
}
}
}
} else if ("PPIP_ORDER_SHIPTOTAL".equals(inputParamEnumId) && shippingMethod.equals(cart.getShipmentMethodTypeId()) && carrierPartyId.equals(cart.getCarrierPartyId())) {
if (UtilValidate.isNotEmpty(condValue)) {
BigDecimal orderTotalShipping = cart.getTotalShipping();
if (Debug.verboseOn()) {
Debug.logVerbose("Doing order total Shipping compare: ordertotalShipping=" + orderTotalShipping, module);
}
compareBase = orderTotalShipping.compareTo(new BigDecimal(condValue));
}
} else if ("PPIP_LPMUP_AMT".equals(inputParamEnumId)) {
// does nothing on order level, only checked on item level, so ignore by always considering passed
return true;
} else if ("PPIP_LPMUP_PER".equals(inputParamEnumId)) {
// does nothing on order level, only checked on item level, so ignore by always considering passed
return true;
} else {
Debug.logWarning(UtilProperties.getMessage(resource_error, "OrderAnUnSupportedProductPromoCondInputParameterLhs", UtilMisc.toMap("inputParamEnumId", productPromoCond.getString("inputParamEnumId")), cart.getLocale()), module);
return false;
}
if (Debug.verboseOn())
Debug.logVerbose("Condition compare done, compareBase=" + compareBase, module);
if (compareBase != null) {
int compare = compareBase.intValue();
if ("PPC_EQ".equals(operatorEnumId)) {
if (compare == 0)
return true;
} else if ("PPC_NEQ".equals(operatorEnumId)) {
if (compare != 0)
return true;
} else if ("PPC_LT".equals(operatorEnumId)) {
if (compare < 0)
return true;
} else if ("PPC_LTE".equals(operatorEnumId)) {
if (compare <= 0)
return true;
} else if ("PPC_GT".equals(operatorEnumId)) {
if (compare > 0)
return true;
} else if ("PPC_GTE".equals(operatorEnumId)) {
if (compare >= 0)
return true;
} else {
Debug.logWarning(UtilProperties.getMessage(resource_error, "OrderAnUnSupportedProductPromoCondCondition", UtilMisc.toMap("operatorEnumId", operatorEnumId), cart.getLocale()), module);
return false;
}
}
// default to not meeting the condition
return false;
}
Aggregations