/* * 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.apache.ofbiz.order.shoppingcart; import java.math.BigDecimal; import java.sql.Timestamp; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Locale; import java.util.Map; import org.apache.ofbiz.base.util.Debug; import org.apache.ofbiz.base.util.GeneralException; import org.apache.ofbiz.base.util.ObjectType; import org.apache.ofbiz.base.util.StringUtil; import org.apache.ofbiz.base.util.UtilDateTime; import org.apache.ofbiz.base.util.UtilFormatOut; import org.apache.ofbiz.base.util.UtilGenerics; import org.apache.ofbiz.base.util.UtilMisc; import org.apache.ofbiz.base.util.UtilNumber; import org.apache.ofbiz.base.util.UtilProperties; import org.apache.ofbiz.base.util.UtilValidate; import org.apache.ofbiz.entity.Delegator; import org.apache.ofbiz.entity.GenericEntityException; import org.apache.ofbiz.entity.GenericValue; import org.apache.ofbiz.entity.condition.EntityCondition; import org.apache.ofbiz.entity.condition.EntityExpr; import org.apache.ofbiz.entity.condition.EntityFunction; import org.apache.ofbiz.entity.condition.EntityOperator; import org.apache.ofbiz.entity.util.EntityQuery; import org.apache.ofbiz.entity.util.EntityTypeUtil; import org.apache.ofbiz.entity.util.EntityUtil; import org.apache.ofbiz.entity.util.EntityUtilProperties; import org.apache.ofbiz.order.finaccount.FinAccountHelper; import org.apache.ofbiz.order.order.OrderChangeHelper; import org.apache.ofbiz.order.order.OrderReadHelper; import org.apache.ofbiz.order.shoppingcart.product.ProductPromoWorker; import org.apache.ofbiz.order.shoppingcart.shipping.ShippingEvents; import org.apache.ofbiz.order.thirdparty.paypal.ExpressCheckoutEvents; import org.apache.ofbiz.party.contact.ContactHelper; import org.apache.ofbiz.party.contact.ContactMechWorker; import org.apache.ofbiz.product.store.ProductStoreWorker; import org.apache.ofbiz.service.GenericServiceException; import org.apache.ofbiz.service.LocalDispatcher; import org.apache.ofbiz.service.ModelService; import org.apache.ofbiz.service.ServiceUtil; /** * A facade over the ShoppingCart to simplify the relatively complex * processing required to create an order in the system. */ public class CheckOutHelper { public static final String module = CheckOutHelper.class.getName(); public static final String resource_error = "OrderErrorUiLabels"; public static final int scale = UtilNumber.getBigDecimalScale("order.decimals"); public static final int rounding = UtilNumber.getBigDecimalRoundingMode("order.rounding"); protected LocalDispatcher dispatcher = null; protected Delegator delegator = null; protected ShoppingCart cart = null; public CheckOutHelper(LocalDispatcher dispatcher, Delegator delegator, ShoppingCart cart) { this.delegator = delegator; this.dispatcher = dispatcher; this.cart = cart; } public Map<String, Object> setCheckOutShippingAddress(String shippingContactMechId) { List<String> errorMessages = new ArrayList<String>(); Map<String, Object> result; String errMsg = null; if (UtilValidate.isNotEmpty(this.cart)) { errorMessages.addAll(setCheckOutShippingAddressInternal(shippingContactMechId)); } else { errMsg = UtilProperties.getMessage(resource_error,"checkhelper.no_items_in_cart", (cart != null ? cart.getLocale() : Locale.getDefault())); errorMessages.add(errMsg); } if (errorMessages.size() == 1) { result = ServiceUtil.returnError(errorMessages.get(0).toString()); } else if (errorMessages.size() > 0) { result = ServiceUtil.returnError(errorMessages); } else { result = ServiceUtil.returnSuccess(); } return result; } private List<String> setCheckOutShippingAddressInternal(String shippingContactMechId) { List<String> errorMessages = new ArrayList<String>(); String errMsg = null; // set the shipping address if (UtilValidate.isNotEmpty(shippingContactMechId)) { this.cart.setAllShippingContactMechId(shippingContactMechId); } else if (cart.shippingApplies()) { // only return an error if shipping is required for this purchase errMsg = UtilProperties.getMessage(resource_error,"checkhelper.select_shipping_destination", (cart != null ? cart.getLocale() : Locale.getDefault())); errorMessages.add(errMsg); } return errorMessages; } public Map<String, Object> setCheckOutShippingOptions(String shippingMethod, String shippingInstructions, String orderAdditionalEmails, String maySplit, String giftMessage, String isGift, String internalCode, String shipBeforeDate, String shipAfterDate) { List<String> errorMessages = new ArrayList<String>(); Map<String, Object> result; String errMsg = null; if (UtilValidate.isNotEmpty(this.cart)) { errorMessages.addAll(setCheckOutShippingOptionsInternal(shippingMethod, shippingInstructions, orderAdditionalEmails, maySplit, giftMessage, isGift, internalCode, shipBeforeDate, shipAfterDate)); } else { errMsg = UtilProperties.getMessage(resource_error,"checkhelper.no_items_in_cart", (cart != null ? cart.getLocale() : Locale.getDefault())); errorMessages.add(errMsg); } if (errorMessages.size() == 1) { result = ServiceUtil.returnError(errorMessages.get(0).toString()); } else if (errorMessages.size() > 0) { result = ServiceUtil.returnError(errorMessages); } else { result = ServiceUtil.returnSuccess(); } return result; } private List<String> setCheckOutShippingOptionsInternal(String shippingMethod, String shippingInstructions, String orderAdditionalEmails, String maySplit, String giftMessage, String isGift, String internalCode, String shipBeforeDate, String shipAfterDate) { List<String> errorMessages = new ArrayList<String>(); String errMsg = null; // set the general shipping options if (UtilValidate.isNotEmpty(shippingMethod)) { int delimiterPos = shippingMethod.indexOf('@'); String shipmentMethodTypeId = null; String carrierPartyId = null; if (delimiterPos > 0) { shipmentMethodTypeId = shippingMethod.substring(0, delimiterPos); carrierPartyId = shippingMethod.substring(delimiterPos + 1); } this.cart.setAllShipmentMethodTypeId(shipmentMethodTypeId); this.cart.setAllCarrierPartyId(carrierPartyId); } else if (cart.shippingApplies()) { // only return an error if shipping is required for this purchase errMsg = UtilProperties.getMessage(resource_error,"checkhelper.select_shipping_method", (cart != null ? cart.getLocale() : Locale.getDefault())); errorMessages.add(errMsg); } // set the shipping instructions this.cart.setAllShippingInstructions(shippingInstructions); if (UtilValidate.isNotEmpty(maySplit)) { cart.setAllMaySplit(Boolean.valueOf(maySplit)); } else { errMsg = UtilProperties.getMessage(resource_error,"checkhelper.select_splitting_preference", (cart != null ? cart.getLocale() : Locale.getDefault())); errorMessages.add(errMsg); } // set the gift message this.cart.setAllGiftMessage(giftMessage); if (UtilValidate.isNotEmpty(isGift)) { cart.setAllIsGift(Boolean.valueOf(isGift)); } else { errMsg = UtilProperties.getMessage(resource_error, "checkhelper.specify_if_order_is_gift", (cart != null ? cart.getLocale() : Locale.getDefault())); errorMessages.add(errMsg); } // interal code this.cart.setInternalCode(internalCode); if (UtilValidate.isNotEmpty(shipBeforeDate)) { if (UtilValidate.isDate(shipBeforeDate)) { cart.setShipBeforeDate(UtilDateTime.toTimestamp(shipBeforeDate)); } else { errMsg = UtilProperties.getMessage(resource_error, "checkhelper.specify_if_shipBeforeDate_is_date", (cart != null ? cart.getLocale() : Locale.getDefault())); errorMessages.add(errMsg); } } if (UtilValidate.isNotEmpty(shipAfterDate)) { if (UtilValidate.isDate(shipAfterDate)) { cart.setShipAfterDate(UtilDateTime.toTimestamp(shipAfterDate)); } else { errMsg = UtilProperties.getMessage(resource_error, "checkhelper.specify_if_shipAfterDate_is_date", (cart != null ? cart.getLocale() : Locale.getDefault())); errorMessages.add(errMsg); } } // set any additional notification emails this.cart.setOrderAdditionalEmails(orderAdditionalEmails); return errorMessages; } public Map<String, Object> setCheckOutPayment(Map<String, Map<String, Object>> selectedPaymentMethods, List<String> singleUsePayments, String billingAccountId) { List<String> errorMessages = new ArrayList<String>(); Map<String, Object> result; String errMsg = null; if (UtilValidate.isNotEmpty(this.cart)) { errorMessages.addAll(setCheckOutPaymentInternal(selectedPaymentMethods, singleUsePayments, billingAccountId)); } else { errMsg = UtilProperties.getMessage(resource_error,"checkhelper.no_items_in_cart", (cart != null ? cart.getLocale() : Locale.getDefault())); errorMessages.add(errMsg); } if (errorMessages.size() == 1) { result = ServiceUtil.returnError(errorMessages.get(0).toString()); } else if (errorMessages.size() > 0) { result = ServiceUtil.returnError(errorMessages); } else { result = ServiceUtil.returnSuccess(); } return result; } public List<String> setCheckOutPaymentInternal(Map<String, Map<String, Object>> selectedPaymentMethods, List<String> singleUsePayments, String billingAccountId) { List<String> errorMessages = new ArrayList<String>(); String errMsg = null; if (singleUsePayments == null) { singleUsePayments = new ArrayList<String>(); } // set the payment method option if (UtilValidate.isNotEmpty(selectedPaymentMethods)) { // clear out the old payments cart.clearPayments(); if (UtilValidate.isNotEmpty(billingAccountId)) { Map<String, Object> billingAccountMap = selectedPaymentMethods.get("EXT_BILLACT"); BigDecimal billingAccountAmt = (BigDecimal)billingAccountMap.get("amount"); // set cart billing account data and generate a payment method containing the amount we will be charging cart.setBillingAccount(billingAccountId, (billingAccountAmt != null ? billingAccountAmt: BigDecimal.ZERO)); // copy the billing account terms as order terms try { List<GenericValue> billingAccountTerms = EntityQuery.use(delegator).from("BillingAccountTerm").where("billingAccountId", billingAccountId).queryList(); if (UtilValidate.isNotEmpty(billingAccountTerms)) { for (GenericValue billingAccountTerm : billingAccountTerms) { // the term is not copied if in the cart a term of the same type is already set if (!cart.hasOrderTerm(billingAccountTerm.getString("termTypeId"))) { cart.addOrderTerm(billingAccountTerm.getString("termTypeId"), billingAccountTerm.getBigDecimal("termValue"), billingAccountTerm.getLong("termDays")); } } } } catch (GenericEntityException gee) { Debug.logWarning("Error copying billing account terms to order terms: " + gee.getMessage(), module); } } else { // remove the billing account from the cart cart.setBillingAccount(null, BigDecimal.ZERO); } // if checkoutPaymentId == EXT_BILLACT, then we have billing account only, so make sure we have enough credit if (selectedPaymentMethods.containsKey("EXT_BILLACT") && selectedPaymentMethods.size() == 1) { BigDecimal accountCredit = this.availableAccountBalance(cart.getBillingAccountId()); BigDecimal amountToUse = cart.getBillingAccountAmount(); // if an amount was entered, check that it doesn't exceed available amount if (amountToUse.compareTo(BigDecimal.ZERO) > 0 && amountToUse.compareTo(accountCredit) > 0) { errMsg = UtilProperties.getMessage(resource_error,"checkhelper.insufficient_credit_available_on_account", (cart != null ? cart.getLocale() : Locale.getDefault())); errorMessages.add(errMsg); } else { // otherwise use the available account credit (The user might enter 10.00 for an order worth 20.00 from an account with 30.00. This makes sure that the 30.00 is used) amountToUse = accountCredit; } // check that the amount to use is enough to fulfill the order BigDecimal grandTotal = cart.getGrandTotal(); if (grandTotal.compareTo(amountToUse) > 0) { cart.setBillingAccount(null, BigDecimal.ZERO); // erase existing billing account data errMsg = UtilProperties.getMessage(resource_error,"checkhelper.insufficient_credit_available_on_account", (cart != null ? cart.getLocale() : Locale.getDefault())); errorMessages.add(errMsg); } else { // since this is the only selected payment method, let's make this amount the grand total for convenience amountToUse = grandTotal; } // associate the cart billing account amount and EXT_BILLACT selected payment method with whatever amount we have now // XXX: Note that this step is critical for the billing account to be charged correctly if (amountToUse.compareTo(BigDecimal.ZERO) > 0) { cart.setBillingAccount(billingAccountId, amountToUse); selectedPaymentMethods.put("EXT_BILLACT", UtilMisc.<String, Object>toMap("amount", amountToUse, "securityCode", null)); } } for (String checkOutPaymentId : selectedPaymentMethods.keySet()) { String finAccountId = null; if (checkOutPaymentId.indexOf("|") > -1) { // split type -- ID|Actual String[] splitStr = checkOutPaymentId.split("\\|"); checkOutPaymentId = splitStr[0]; if ("FIN_ACCOUNT".equals(checkOutPaymentId)) { finAccountId = splitStr[1]; } if (Debug.verboseOn()) Debug.logVerbose("Split checkOutPaymentId: " + splitStr[0] + " / " + splitStr[1], module); } // get the selected amount to use BigDecimal paymentAmount = null; String securityCode = null; if (selectedPaymentMethods.get(checkOutPaymentId) != null) { Map<String, Object> checkOutPaymentInfo = selectedPaymentMethods.get(checkOutPaymentId); paymentAmount = (BigDecimal) checkOutPaymentInfo.get("amount"); securityCode = (String) checkOutPaymentInfo.get("securityCode"); } boolean singleUse = singleUsePayments.contains(checkOutPaymentId); ShoppingCart.CartPaymentInfo inf = cart.addPaymentAmount(checkOutPaymentId, paymentAmount, singleUse); if (finAccountId != null) { inf.finAccountId = finAccountId; } if (securityCode != null) { inf.securityCode = securityCode; } } } else if (cart.getGrandTotal().compareTo(BigDecimal.ZERO) != 0) { // only return an error if the order total is not 0.00 errMsg = UtilProperties.getMessage(resource_error,"checkhelper.select_method_of_payment", (cart != null ? cart.getLocale() : Locale.getDefault())); errorMessages.add(errMsg); } return errorMessages; } public Map<String, Object> setCheckOutDates(Timestamp shipBefore, Timestamp shipAfter) { List<String> errorMessages = new ArrayList<String>(); Map<String, Object> result = null; String errMsg = null; if (UtilValidate.isNotEmpty(this.cart)) { this.cart.setShipBeforeDate(shipBefore); this.cart.setShipAfterDate(shipAfter); } else { errMsg = UtilProperties.getMessage(resource_error,"checkhelper.no_items_in_cart", (cart != null ? cart.getLocale() : Locale.getDefault())); errorMessages.add(errMsg); } if (errorMessages.size() == 1) { result = ServiceUtil.returnError(errorMessages.get(0).toString()); } else if (errorMessages.size() > 0) { result = ServiceUtil.returnError(errorMessages); } else { result = ServiceUtil.returnSuccess(); } return result; } public Map<String, Object> setCheckOutOptions(String shippingMethod, String shippingContactMechId, Map<String, Map<String, Object>> selectedPaymentMethods, List<String> singleUsePayments, String billingAccountId, String shippingInstructions, String orderAdditionalEmails, String maySplit, String giftMessage, String isGift, String internalCode, String shipBeforeDate, String shipAfterDate) { List<String> errorMessages = new ArrayList<String>(); Map<String, Object> result = null; String errMsg = null; if (UtilValidate.isNotEmpty(this.cart)) { // set the general shipping options and method errorMessages.addAll(setCheckOutShippingOptionsInternal(shippingMethod, shippingInstructions, orderAdditionalEmails, maySplit, giftMessage, isGift, internalCode, shipBeforeDate, shipAfterDate)); // set the shipping address errorMessages.addAll(setCheckOutShippingAddressInternal(shippingContactMechId)); // Recalc shipping costs before setting payment Map<String, Object> shipEstimateMap = ShippingEvents.getShipGroupEstimate(dispatcher, delegator, cart, 0); BigDecimal shippingTotal = (BigDecimal) shipEstimateMap.get("shippingTotal"); if (shippingTotal == null) { shippingTotal = BigDecimal.ZERO; } cart.setItemShipGroupEstimate(shippingTotal, 0); ProductPromoWorker.doPromotions(cart, dispatcher); //Recalc tax before setting payment try { this.calcAndAddTax(); } catch (GeneralException e) { Debug.logError(e, module); } // set the payment method(s) option errorMessages.addAll(setCheckOutPaymentInternal(selectedPaymentMethods, singleUsePayments, billingAccountId)); } else { errMsg = UtilProperties.getMessage(resource_error,"checkhelper.no_items_in_cart", (cart != null ? cart.getLocale() : Locale.getDefault())); errorMessages.add(errMsg); } if (errorMessages.size() == 1) { result = ServiceUtil.returnError(errorMessages.get(0).toString()); } else if (errorMessages.size() > 0) { result = ServiceUtil.returnError(errorMessages); } else { result = ServiceUtil.returnSuccess(); } return result; } public Map<String, Object> checkGiftCard(Map<String, Object> params, Map<String, Map<String, Object>> selectedPaymentMethods) { List<String> errorMessages = new ArrayList<String>(); Map<String, Object> errorMaps = new HashMap<String, Object>(); Map<String, Object> result = new HashMap<String, Object>(); String errMsg = null; // handle gift card payment if (params.get("addGiftCard") != null) { String gcNum = (String) params.get("giftCardNumber"); String gcPin = (String) params.get("giftCardPin"); String gcAmt = (String) params.get("giftCardAmount"); BigDecimal gcAmount = BigDecimal.ONE.negate(); boolean gcFieldsOkay = true; if (UtilValidate.isEmpty(gcNum)) { errMsg = UtilProperties.getMessage(resource_error,"checkhelper.enter_gift_card_number", (cart != null ? cart.getLocale() : Locale.getDefault())); errorMessages.add(errMsg); gcFieldsOkay = false; } if (cart.isPinRequiredForGC(delegator)) { // if a PIN is required, make sure the PIN is valid if (UtilValidate.isEmpty(gcPin)) { errMsg = UtilProperties.getMessage(resource_error,"checkhelper.enter_gift_card_pin_number", (cart != null ? cart.getLocale() : Locale.getDefault())); errorMessages.add(errMsg); gcFieldsOkay = false; } } // See if we should validate gift card code against FinAccount's accountCode if (cart.isValidateGCFinAccount(delegator)) { try { // No PIN required - validate gift card number against account code if (!cart.isPinRequiredForGC(delegator)) { GenericValue finAccount = FinAccountHelper.getFinAccountFromCode(gcNum, delegator); if (finAccount == null) { errMsg = UtilProperties.getMessage(resource_error,"checkhelper.gift_card_does_not_exist", (cart != null ? cart.getLocale() : Locale.getDefault())); errorMessages.add(errMsg); gcFieldsOkay = false; } else if ((finAccount.getBigDecimal("availableBalance") == null) || !((finAccount.getBigDecimal("availableBalance")).compareTo(FinAccountHelper.ZERO) > 0)) { // if account's available balance (including authorizations) is not greater than zero, then return an error errMsg = UtilProperties.getMessage(resource_error,"checkhelper.gift_card_has_no_value", (cart != null ? cart.getLocale() : Locale.getDefault())); errorMessages.add(errMsg); gcFieldsOkay = false; } } // TODO: else case when pin is required - we should validate gcNum and gcPin } catch (GenericEntityException ex) { errorMessages.add(ex.getMessage()); gcFieldsOkay = false; } } if (UtilValidate.isNotEmpty(selectedPaymentMethods)) { if (UtilValidate.isEmpty(gcAmt)) { errMsg = UtilProperties.getMessage(resource_error,"checkhelper.enter_amount_to_place_on_gift_card", (cart != null ? cart.getLocale() : Locale.getDefault())); errorMessages.add(errMsg); gcFieldsOkay = false; } } if (UtilValidate.isNotEmpty(gcAmt)) { try { gcAmount = new BigDecimal(gcAmt); } catch (NumberFormatException e) { Debug.logError(e, module); errMsg = UtilProperties.getMessage(resource_error,"checkhelper.invalid_amount_for_gift_card", (cart != null ? cart.getLocale() : Locale.getDefault())); errorMessages.add(errMsg); gcFieldsOkay = false; } } if (gcFieldsOkay) { // store the gift card Map<String, Object> gcCtx = new HashMap<String, Object>(); gcCtx.put("partyId", params.get("partyId")); gcCtx.put("cardNumber", gcNum); if (cart.isPinRequiredForGC(delegator)) { gcCtx.put("pinNumber", gcPin); } gcCtx.put("userLogin", cart.getUserLogin()); Map<String, Object> gcResult = null; try { gcResult = dispatcher.runSync("createGiftCard", gcCtx); } catch (GenericServiceException e) { Debug.logError(e, module); errorMessages.add(e.getMessage()); } if (gcResult != null) { ServiceUtil.addErrors(errorMessages, errorMaps, gcResult); if (errorMessages.size() == 0 && errorMaps.size() == 0) { // set the GC payment method BigDecimal giftCardAmount = null; if (gcAmount.compareTo(BigDecimal.ZERO) > 0) { giftCardAmount = gcAmount; } String gcPaymentMethodId = (String) gcResult.get("paymentMethodId"); result = ServiceUtil.returnSuccess(); result.put("paymentMethodId", gcPaymentMethodId); result.put("amount", giftCardAmount); } } else { errMsg = UtilProperties.getMessage(resource_error,"checkhelper.problem_with_gift_card_information", (cart != null ? cart.getLocale() : Locale.getDefault())); errorMessages.add(errMsg); } } } else { result = ServiceUtil.returnSuccess(); } // see whether we need to return an error or not if (errorMessages.size() > 0) { result.put(ModelService.ERROR_MESSAGE_LIST, errorMessages); result.put(ModelService.RESPONSE_MESSAGE, ModelService.RESPOND_ERROR); } if (errorMaps.size() > 0) { result.put(ModelService.ERROR_MESSAGE_MAP, errorMaps); result.put(ModelService.RESPONSE_MESSAGE, ModelService.RESPOND_ERROR); } return result; } public Map<String, Object> createOrder(GenericValue userLogin) { return createOrder(userLogin, null, null, null, false, null, cart.getWebSiteId()); } // Create order event - uses createOrder service for processing public Map<String, Object> createOrder(GenericValue userLogin, String distributorId, String affiliateId, List<GenericValue> trackingCodeOrders, boolean areOrderItemsExploded, String visitId, String webSiteId) { if (this.cart == null) { return null; } String orderId = this.cart.getOrderId(); String supplierPartyId = (String) this.cart.getAttribute("supplierPartyId"); String originOrderId = (String) this.cart.getAttribute("originOrderId"); this.cart.clearAllItemStatus(); BigDecimal grandTotal = this.cart.getGrandTotal(); // store the order - build the context Map<String, Object> context = this.cart.makeCartMap(this.dispatcher, areOrderItemsExploded); //get the TrackingCodeOrder List context.put("trackingCodeOrders", trackingCodeOrders); if (distributorId != null) context.put("distributorId", distributorId); if (affiliateId != null) context.put("affiliateId", affiliateId); context.put("orderId", orderId); context.put("supplierPartyId", supplierPartyId); context.put("grandTotal", grandTotal); context.put("userLogin", userLogin); context.put("visitId", visitId); if (UtilValidate.isEmpty(webSiteId)) { webSiteId = cart.getWebSiteId(); } context.put("webSiteId", webSiteId); context.put("originOrderId", originOrderId); // need the partyId; don't use userLogin in case of an order via order mgr String partyId = this.cart.getPartyId(); String productStoreId = cart.getProductStoreId(); // store the order - invoke the service Map<String, Object> storeResult = null; try { storeResult = dispatcher.runSync("storeOrder", context); orderId = (String) storeResult.get("orderId"); if (UtilValidate.isNotEmpty(orderId)) { this.cart.setOrderId(orderId); if (this.cart.getFirstAttemptOrderId() == null) { this.cart.setFirstAttemptOrderId(orderId); } } } catch (GenericServiceException e) { String service = e.getMessage(); Map<String, Object> messageMap = UtilMisc.<String, Object>toMap("service", service); String errMsg = UtilProperties.getMessage(resource_error, "checkhelper.could_not_create_order_invoking_service", messageMap, (cart != null ? cart.getLocale() : Locale.getDefault())); Debug.logError(e, errMsg, module); return ServiceUtil.returnError(errMsg); } // check for error message(s) if (ServiceUtil.isError(storeResult)) { String errMsg = UtilProperties.getMessage(resource_error, "checkhelper.did_not_complete_order_following_occurred", (cart != null ? cart.getLocale() : Locale.getDefault())); List<String> resErrorMessages = new LinkedList<String>(); resErrorMessages.add(errMsg); resErrorMessages.add(ServiceUtil.getErrorMessage(storeResult)); return ServiceUtil.returnError(resErrorMessages); } // ---------- // If needed, the production runs are created and linked to the order lines. // List<GenericValue> orderItems = UtilGenerics.checkList(context.get("orderItems")); int counter = 0; for (GenericValue orderItem : orderItems) { String productId = orderItem.getString("productId"); if (productId != null) { try { // do something tricky here: run as the "system" user // that can actually create and run a production run GenericValue permUserLogin = EntityQuery.use(delegator).from("UserLogin").where("userLoginId", "system").cache().queryOne(); GenericValue productStore = ProductStoreWorker.getProductStore(productStoreId, delegator); GenericValue product = EntityQuery.use(delegator).from("Product").where("productId", productId).queryOne(); if (EntityTypeUtil.hasParentType(delegator, "ProductType", "productTypeId", product.getString("productTypeId"), "parentTypeId", "AGGREGATED")) { org.apache.ofbiz.product.config.ProductConfigWrapper config = this.cart.findCartItem(counter).getConfigWrapper(); Map<String, Object> inputMap = new HashMap<String, Object>(); inputMap.put("config", config); inputMap.put("facilityId", productStore.getString("inventoryFacilityId")); inputMap.put("orderId", orderId); inputMap.put("orderItemSeqId", orderItem.getString("orderItemSeqId")); inputMap.put("quantity", orderItem.getBigDecimal("quantity")); inputMap.put("userLogin", permUserLogin); Map<String, Object> prunResult = dispatcher.runSync("createProductionRunFromConfiguration", inputMap); if (ServiceUtil.isError(prunResult)) { Debug.logError(ServiceUtil.getErrorMessage(prunResult) + " for input:" + inputMap, module); } } } catch (GenericEntityException e) { String service = e.getMessage(); Map<String, String> messageMap = UtilMisc.toMap("service", service); String errMsg = "Problem accessing the Product entity"; errMsg = errMsg + UtilProperties.getMessage(resource_error, "checkhelper.could_not_create_order_invoking_service", messageMap, (cart != null ? cart.getLocale() : Locale.getDefault())); Debug.logError(e, errMsg, module); return ServiceUtil.returnError(errMsg); } catch (GenericServiceException e) { String service = e.getMessage(); Map<String, String> messageMap = UtilMisc.toMap("service", service); String errMsg = UtilProperties.getMessage(resource_error, "checkhelper.could_not_create_order_invoking_service", messageMap, (cart != null ? cart.getLocale() : Locale.getDefault())); Debug.logError(e, errMsg, module); return ServiceUtil.returnError(errMsg); } catch (Exception e) { String service = e.getMessage(); Map<String, String> messageMap = UtilMisc.toMap("service", service); String errMsg = UtilProperties.getMessage(resource_error, "checkhelper.could_not_create_order_invoking_service", messageMap, (cart != null ? cart.getLocale() : Locale.getDefault())); Debug.logError(e, errMsg, module); return ServiceUtil.returnError(errMsg); } } counter++; } // ---------- // ---------- // The status of the requirement associated to the shopping cart lines is set to "ordered". // for (ShoppingCartItem shoppingCartItem : this.cart.items()) { String requirementId = shoppingCartItem.getRequirementId(); if (requirementId != null) { try { /* OrderRequirementCommitment records will map which POs which are created from which requirements. With the help of this mapping requirements will be updated to Ordered when POs will be approved. */ Map<String, Object> inputMap = UtilMisc.toMap("userLogin", userLogin, "orderId", orderId, "orderItemSeqId", shoppingCartItem.getOrderItemSeqId(), "requirementId", requirementId, "quantity", shoppingCartItem.getQuantity()); dispatcher.runSync("createOrderRequirementCommitment", inputMap); } catch (GenericServiceException e) { String service = e.getMessage(); Map<String, String> messageMap = UtilMisc.toMap("service", service); String errMsg = UtilProperties.getMessage(resource_error, "checkhelper.could_not_create_order_invoking_service", messageMap, (cart != null ? cart.getLocale() : Locale.getDefault())); Debug.logError(e, errMsg, module); return ServiceUtil.returnError(errMsg); } catch (Exception e) { String service = e.getMessage(); Map<String, String> messageMap = UtilMisc.toMap("service", service); String errMsg = UtilProperties.getMessage(resource_error, "checkhelper.could_not_create_order_invoking_service", messageMap, (cart != null ? cart.getLocale() : Locale.getDefault())); Debug.logError(e, errMsg, module); return ServiceUtil.returnError(errMsg); } } } // ---------- // set the orderId for use by chained events Map<String, Object> result = ServiceUtil.returnSuccess(); result.put("orderId", orderId); result.put("orderAdditionalEmails", this.cart.getOrderAdditionalEmails()); // save the emails to the order List<GenericValue> toBeStored = new LinkedList<GenericValue>(); GenericValue party = null; try { party = EntityQuery.use(delegator).from("Party").where("partyId", partyId).queryOne(); } catch (GenericEntityException e) { Debug.logWarning(e, UtilProperties.getMessage(resource_error,"OrderProblemsGettingPartyRecord", cart.getLocale()), module); } // create order contact mechs for the email address(s) if (party != null) { Iterator<GenericValue> emailIter = UtilMisc.toIterator(ContactHelper.getContactMechByType(party, "EMAIL_ADDRESS", false)); while (emailIter != null && emailIter.hasNext()) { GenericValue email = emailIter.next(); GenericValue orderContactMech = this.delegator.makeValue("OrderContactMech", UtilMisc.toMap("orderId", orderId, "contactMechId", email.getString("contactMechId"), "contactMechPurposeTypeId", "ORDER_EMAIL")); toBeStored.add(orderContactMech); if (UtilValidate.isEmpty(ContactHelper.getContactMechByPurpose(party, "ORDER_EMAIL", false))) { GenericValue partyContactMechPurpose = this.delegator.makeValue("PartyContactMechPurpose", UtilMisc.toMap("partyId", party.getString("partyId"), "contactMechId", email.getString("contactMechId"), "contactMechPurposeTypeId", "ORDER_EMAIL", "fromDate", UtilDateTime.nowTimestamp())); toBeStored.add(partyContactMechPurpose); } } } // create dummy contact mechs and order contact mechs for the additional emails String additionalEmails = this.cart.getOrderAdditionalEmails(); List<String> emailList = StringUtil.split(additionalEmails, ","); if (emailList == null) emailList = new ArrayList<String>(); for (String email : emailList) { String contactMechId = this.delegator.getNextSeqId("ContactMech"); GenericValue contactMech = this.delegator.makeValue("ContactMech", UtilMisc.toMap("contactMechId", contactMechId, "contactMechTypeId", "EMAIL_ADDRESS", "infoString", email)); GenericValue orderContactMech = this.delegator.makeValue("OrderContactMech", UtilMisc.toMap("orderId", orderId, "contactMechId", contactMechId, "contactMechPurposeTypeId", "ORDER_EMAIL")); toBeStored.add(contactMech); toBeStored.add(orderContactMech); } if (toBeStored.size() > 0) { try { if (Debug.verboseOn()) Debug.logVerbose("To Be Stored: " + toBeStored, module); this.delegator.storeAll(toBeStored); } catch (GenericEntityException e) { // not a fatal error; so just print a message Debug.logWarning(e, UtilProperties.getMessage(resource_error,"OrderProblemsStoringOrderEmailContactInformation", cart.getLocale()), module); } } return result; } public void calcAndAddTax() throws GeneralException { calcAndAddTax(null, false); } public void calcAndAddTax(boolean skipEmptyAddresses) throws GeneralException { calcAndAddTax(null, skipEmptyAddresses); } public void calcAndAddTax(GenericValue shipAddress) throws GeneralException { calcAndAddTax(shipAddress, false); } public void calcAndAddTax(GenericValue shipAddress, boolean skipEmptyAddresses) throws GeneralException { if (UtilValidate.isEmpty(cart.getShippingContactMechId()) && cart.getBillingAddress() == null && shipAddress == null) { return; } int shipGroups = this.cart.getShipGroupSize(); for (int i = 0; i < shipGroups; i++) { ShoppingCart.CartShipInfo csi = cart.getShipInfo(i); Map<Integer, ShoppingCartItem> shoppingCartItemIndexMap = new HashMap<Integer, ShoppingCartItem>(); Map<String, Object> serviceContext = this.makeTaxContext(i, shipAddress, shoppingCartItemIndexMap, cart.getFacilityId(), skipEmptyAddresses); if (skipEmptyAddresses && serviceContext == null) { csi.clearAllTaxInfo(); continue; } List<List<? extends Object>> taxReturn = this.getTaxAdjustments(dispatcher, "calcTax", serviceContext); if (Debug.verboseOn()) Debug.logVerbose("ReturnList: " + taxReturn, module); List<GenericValue> orderAdj = UtilGenerics.checkList(taxReturn.get(0)); List<List<GenericValue>> itemAdj = UtilGenerics.checkList(taxReturn.get(1)); // set the item adjustments if (itemAdj != null) { for (int x = 0; x < itemAdj.size(); x++) { List<GenericValue> adjs = itemAdj.get(x); ShoppingCartItem item = shoppingCartItemIndexMap.get(Integer.valueOf(x)); if (adjs == null) { adjs = new LinkedList<GenericValue>(); } csi.setItemInfo(item, adjs); if (Debug.verboseOn()) Debug.logVerbose("Added item adjustments to ship group [" + i + " / " + x + "] - " + adjs, module); } } // need to manually clear the order adjustments csi.shipTaxAdj.clear(); csi.shipTaxAdj.addAll(orderAdj); } } private Map<String, Object> makeTaxContext(int shipGroup, GenericValue shipAddress, Map<Integer, ShoppingCartItem> shoppingCartItemIndexMap, String originFacilityId, boolean skipEmptyAddresses) { ShoppingCart.CartShipInfo csi = cart.getShipInfo(shipGroup); int totalItems = csi.shipItemInfo.size(); List<GenericValue> product = new ArrayList<GenericValue>(totalItems); List<BigDecimal> amount = new ArrayList<BigDecimal>(totalItems); List<BigDecimal> price = new ArrayList<BigDecimal>(totalItems); List<BigDecimal> quantity = new ArrayList<BigDecimal>(totalItems); List<BigDecimal> shipAmt = new ArrayList<BigDecimal>(totalItems); Iterator<ShoppingCartItem> it = csi.shipItemInfo.keySet().iterator(); for (int i = 0; i < totalItems; i++) { ShoppingCartItem cartItem = it.next(); ShoppingCart.CartShipInfo.CartShipItemInfo itemInfo = csi.getShipItemInfo(cartItem); product.add(i, cartItem.getProduct()); amount.add(i, cartItem.getItemSubTotal(itemInfo.quantity)); price.add(i, cartItem.getBasePrice()); quantity.add(i, cartItem.getQuantity()); shipAmt.add(i, BigDecimal.ZERO); // no per item shipping yet shoppingCartItemIndexMap.put(Integer.valueOf(i), cartItem); } //add promotion adjustments List<GenericValue> allAdjustments = cart.getAdjustments(); BigDecimal orderPromoAmt = OrderReadHelper.calcOrderPromoAdjustmentsBd(allAdjustments); BigDecimal shipAmount = csi.shipEstimate; if (shipAddress == null) { shipAddress = cart.getShippingAddress(shipGroup); } if (shipAddress == null && skipEmptyAddresses) { return null; } // no shipping address; try the billing address if (shipAddress == null) { for (int i = 0; i < cart.selectedPayments(); i++) { ShoppingCart.CartPaymentInfo cpi = cart.getPaymentInfo(i); GenericValue billAddr = cpi.getBillingAddress(delegator); if (billAddr != null) { shipAddress = billAddr; Debug.logInfo("In makeTaxContext no shipping address, but found address with ID [" + shipAddress.get("contactMechId") + "] from payment method.", module); break; } } } if (shipAddress == null) { // face-to-face order; use the facility address if (originFacilityId != null) { GenericValue facilityContactMech = ContactMechWorker.getFacilityContactMechByPurpose(delegator, originFacilityId, UtilMisc.toList("SHIP_ORIG_LOCATION", "PRIMARY_LOCATION")); if (facilityContactMech != null) { try { shipAddress = EntityQuery.use(delegator).from("PostalAddress").where("contactMechId", facilityContactMech.getString("contactMechId")).queryOne(); } catch (GenericEntityException e) { Debug.logError(e, module); } } } } // if shippingAddress is still null then don't calculate tax; it may be an situation where no tax is applicable, or the data is bad and we don't have a way to find an address to check tax for if (shipAddress == null) { Debug.logWarning("Not calculating tax for new order because there is no shipping address, no billing address, and no address on the origin facility [" + originFacilityId + "]", module); } Map<String, Object> serviceContext = UtilMisc.<String, Object>toMap("productStoreId", cart.getProductStoreId()); serviceContext.put("payToPartyId", cart.getBillFromVendorPartyId()); serviceContext.put("billToPartyId", cart.getBillToCustomerPartyId()); serviceContext.put("itemProductList", product); serviceContext.put("itemAmountList", amount); serviceContext.put("itemPriceList", price); serviceContext.put("itemQuantityList", quantity); serviceContext.put("itemShippingList", shipAmt); serviceContext.put("orderShippingAmount", shipAmount); serviceContext.put("shippingAddress", shipAddress); serviceContext.put("orderPromotionsAmount", orderPromoAmt); return serviceContext; } // Calc the tax adjustments. private List<List<? extends Object>> getTaxAdjustments(LocalDispatcher dispatcher, String taxService, Map<String, Object> serviceContext) throws GeneralException { Map<String, Object> serviceResult = null; try { serviceResult = dispatcher.runSync(taxService, serviceContext); } catch (GenericServiceException e) { Debug.logError(e, module); throw new GeneralException("Problem occurred in tax service (" + e.getMessage() + ")", e); } if (ServiceUtil.isError(serviceResult)) { throw new GeneralException(ServiceUtil.getErrorMessage(serviceResult)); } // the adjustments (returned in order) from taxware. List<GenericValue> orderAdj = UtilGenerics.checkList(serviceResult.get("orderAdjustments")); List<List<GenericValue>> itemAdj = UtilGenerics.checkList(serviceResult.get("itemAdjustments")); return UtilMisc.toList(orderAdj, itemAdj); } public Map<String, Object> processPayment(GenericValue productStore, GenericValue userLogin) throws GeneralException { return CheckOutHelper.processPayment(this.cart.getOrderId(), this.cart.getGrandTotal(), this.cart.getCurrency(), productStore, userLogin, false, false, dispatcher, delegator); } public Map<String, Object> processPayment(GenericValue productStore, GenericValue userLogin, boolean faceToFace) throws GeneralException { return CheckOutHelper.processPayment(this.cart.getOrderId(), this.cart.getGrandTotal(), this.cart.getCurrency(), productStore, userLogin, faceToFace, false, dispatcher, delegator); } public Map<String, Object> processPayment(GenericValue productStore, GenericValue userLogin, boolean faceToFace, boolean manualHold) throws GeneralException { return CheckOutHelper.processPayment(this.cart.getOrderId(), this.cart.getGrandTotal(), this.cart.getCurrency(), productStore, userLogin, faceToFace, manualHold, dispatcher, delegator); } public static Map<String, Object> processPayment(String orderId, BigDecimal orderTotal, String currencyUomId, GenericValue productStore, GenericValue userLogin, boolean faceToFace, boolean manualHold, LocalDispatcher dispatcher, Delegator delegator) throws GeneralException { // Get some payment related strings String DECLINE_MESSAGE = productStore.getString("authDeclinedMessage"); String ERROR_MESSAGE = productStore.getString("authErrorMessage"); String RETRY_ON_ERROR = productStore.getString("retryFailedAuths"); if (RETRY_ON_ERROR == null) { RETRY_ON_ERROR = "Y"; } List<GenericValue> allPaymentPreferences = null; try { allPaymentPreferences = EntityQuery.use(delegator).from("OrderPaymentPreference").where("orderId", orderId).queryList(); } catch (GenericEntityException e) { throw new GeneralException("Problems getting payment preferences", e); } // filter out cancelled preferences List<EntityExpr> canExpr = UtilMisc.toList(EntityCondition.makeCondition("statusId", EntityOperator.NOT_EQUAL, "PAYMENT_CANCELLED")); allPaymentPreferences = EntityUtil.filterByAnd(allPaymentPreferences, canExpr); // check for online payment methods or in-hand payment types with verbal or external refs List<EntityExpr> exprs = UtilMisc.toList(EntityCondition.makeCondition("manualRefNum", EntityOperator.NOT_EQUAL, null)); List<GenericValue> manualRefPaymentPrefs = EntityUtil.filterByAnd(allPaymentPreferences, exprs); if (UtilValidate.isNotEmpty(manualRefPaymentPrefs)) { for (GenericValue opp : manualRefPaymentPrefs) { Map<String, Object> authCtx = new HashMap<String, Object>(); authCtx.put("orderPaymentPreference", opp); if (opp.get("paymentMethodId") == null) { authCtx.put("serviceTypeEnum", "PRDS_PAY_EXTERNAL"); } authCtx.put("processAmount", opp.getBigDecimal("maxAmount")); authCtx.put("authRefNum", opp.getString("manualRefNum")); authCtx.put("authResult", Boolean.TRUE); authCtx.put("userLogin", userLogin); authCtx.put("currencyUomId", currencyUomId); Map<String, Object> authResp = dispatcher.runSync("processAuthResult", authCtx); if (authResp != null && ServiceUtil.isError(authResp)) { throw new GeneralException(ServiceUtil.getErrorMessage(authResp)); } // approve the order OrderChangeHelper.approveOrder(dispatcher, userLogin, orderId, manualHold); if ("Y".equalsIgnoreCase(productStore.getString("manualAuthIsCapture"))) { Map<String, Object> captCtx = new HashMap<String, Object>(); captCtx.put("orderPaymentPreference", opp); if (opp.get("paymentMethodId") == null) { captCtx.put("serviceTypeEnum", "PRDS_PAY_EXTERNAL"); } captCtx.put("payToPartyId", productStore.get("payToPartyId")); captCtx.put("captureResult", Boolean.TRUE); captCtx.put("captureAmount", opp.getBigDecimal("maxAmount")); captCtx.put("captureRefNum", opp.getString("manualRefNum")); captCtx.put("userLogin", userLogin); captCtx.put("currencyUomId", currencyUomId); Map<String, Object> capResp = dispatcher.runSync("processCaptureResult", captCtx); if (capResp != null && ServiceUtil.isError(capResp)) { throw new GeneralException(ServiceUtil.getErrorMessage(capResp)); } } } } // check for a paypal express checkout needing completion List<EntityExpr> payPalExprs = UtilMisc.toList( EntityCondition.makeCondition("paymentMethodId", EntityOperator.NOT_EQUAL, null), EntityCondition.makeCondition("paymentMethodTypeId", "EXT_PAYPAL") ); List<GenericValue> payPalPaymentPrefs = EntityUtil.filterByAnd(allPaymentPreferences, payPalExprs); if (UtilValidate.isNotEmpty(payPalPaymentPrefs)) { GenericValue payPalPaymentPref = EntityUtil.getFirst(payPalPaymentPrefs); ExpressCheckoutEvents.doExpressCheckout(productStore.getString("productStoreId"), orderId, payPalPaymentPref, userLogin, delegator, dispatcher); } // check for online payment methods needing authorization Map<String, Object> paymentFields = UtilMisc.<String, Object>toMap("statusId", "PAYMENT_NOT_AUTH"); List<GenericValue> onlinePaymentPrefs = EntityUtil.filterByAnd(allPaymentPreferences, paymentFields); // Check the payment preferences; if we have ANY w/ status PAYMENT_NOT_AUTH invoke payment service. // Invoke payment processing. if (UtilValidate.isNotEmpty(onlinePaymentPrefs)) { boolean autoApproveOrder = UtilValidate.isEmpty(productStore.get("autoApproveOrder")) || "Y".equalsIgnoreCase(productStore.getString("autoApproveOrder")); if (orderTotal.compareTo(BigDecimal.ZERO) == 0 && autoApproveOrder) { // if there is nothing to authorize; don't bother boolean ok = OrderChangeHelper.approveOrder(dispatcher, userLogin, orderId, manualHold); if (!ok) { throw new GeneralException("Problem with order change; see above error"); } } // now there should be something to authorize; go ahead Map<String, Object> paymentResult = null; try { // invoke the payment gateway service. paymentResult = dispatcher.runSync("authOrderPayments", UtilMisc.<String, Object>toMap("orderId", orderId, "userLogin", userLogin), 180, false); } catch (GenericServiceException e) { Debug.logWarning(e, module); throw new GeneralException("Error in authOrderPayments service: " + e.toString(), e.getNested()); } if (Debug.verboseOn()) Debug.logVerbose("Finished w/ Payment Service", module); if (paymentResult != null && ServiceUtil.isError(paymentResult)) { throw new GeneralException(ServiceUtil.getErrorMessage(paymentResult)); } if (paymentResult != null && paymentResult.containsKey("processResult")) { // grab the customer messages -- only passed back in the case of an error or failure List<String> messages = UtilGenerics.checkList(paymentResult.get("authResultMsgs")); String authResp = (String) paymentResult.get("processResult"); if (authResp.equals("FAILED")) { // order was NOT approved if (Debug.verboseOn()) Debug.logVerbose("Payment auth was NOT a success!", module); boolean ok = OrderChangeHelper.rejectOrder(dispatcher, userLogin, orderId); if (!ok) { throw new GeneralException("Problem with order change; see above error"); } if (UtilValidate.isEmpty(messages)) { return ServiceUtil.returnError(DECLINE_MESSAGE); } else { return ServiceUtil.returnError(messages); } } else if (authResp.equals("APPROVED")) { // order WAS approved if (Debug.verboseOn()) Debug.logVerbose("Payment auth was a success!", module); // set the order and item status to approved if (autoApproveOrder) { List<GenericValue> productStorePaymentSettingList = EntityQuery.use(delegator).from("ProductStorePaymentSetting") .where("productStoreId", productStore.getString("productStoreId"), "paymentMethodTypeId", "CREDIT_CARD", "paymentService", "cyberSourceCCAuth") .queryList(); if (productStorePaymentSettingList.size() > 0) { String decision = (String) paymentResult.get("authCode"); if (UtilValidate.isNotEmpty(decision)) { if ("ACCEPT".equalsIgnoreCase(decision)) { boolean ok = OrderChangeHelper.approveOrder(dispatcher, userLogin, orderId, manualHold); if (!ok) { throw new GeneralException("Problem with order change; see above error"); } } } else { boolean ok = OrderChangeHelper.approveOrder(dispatcher, userLogin, orderId, manualHold); if (!ok) { throw new GeneralException("Problem with order change; see above error"); } } } else { boolean ok = OrderChangeHelper.approveOrder(dispatcher, userLogin, orderId, manualHold); if (!ok) { throw new GeneralException("Problem with order change; see above error"); } } } } else if (authResp.equals("ERROR")) { // service failed if (Debug.verboseOn()) Debug.logVerbose("Payment auth failed due to processor trouble.", module); if (!faceToFace && "Y".equalsIgnoreCase(RETRY_ON_ERROR)) { // never do this for a face to face purchase regardless of store setting return ServiceUtil.returnSuccess(ERROR_MESSAGE); } else { boolean ok = OrderChangeHelper.cancelOrder(dispatcher, userLogin, orderId); if (!ok) { throw new GeneralException("Problem with order change; see above error"); } if (UtilValidate.isEmpty(messages)) { return ServiceUtil.returnError(ERROR_MESSAGE); } else { return ServiceUtil.returnError(messages); } } } else { // should never happen return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,"OrderPleaseContactCustomerServicePaymentReturnCodeUnknown", Locale.getDefault())); } } else { // result returned null == service failed if (Debug.verboseOn()) Debug.logVerbose("Payment auth failed due to processor trouble.", module); if (!faceToFace && "Y".equalsIgnoreCase(RETRY_ON_ERROR)) { // never do this for a face to face purchase regardless of store setting return ServiceUtil.returnSuccess(ERROR_MESSAGE); } else { boolean ok = OrderChangeHelper.cancelOrder(dispatcher, userLogin, orderId); if (!ok) { throw new GeneralException("Problem with order change; see above error"); } return ServiceUtil.returnError(ERROR_MESSAGE); } } } else { // Get the paymentMethodTypeIds - this will need to change when ecom supports multiple payments List<EntityExpr> cashCodPcBaExpr = UtilMisc.toList(EntityCondition.makeCondition("paymentMethodTypeId", EntityOperator.EQUALS, "CASH"), EntityCondition.makeCondition("paymentMethodTypeId", EntityOperator.EQUALS, "EXT_COD"), EntityCondition.makeCondition("paymentMethodTypeId", EntityOperator.EQUALS, "PERSONAL_CHECK"), EntityCondition.makeCondition("paymentMethodTypeId", EntityOperator.EQUALS, "EXT_BILLACT")); List<GenericValue> cashCodPcBaPaymentPreferences = EntityUtil.filterByOr(allPaymentPreferences, cashCodPcBaExpr); if (UtilValidate.isNotEmpty(cashCodPcBaPaymentPreferences) && UtilValidate.isNotEmpty(allPaymentPreferences) && cashCodPcBaPaymentPreferences.size() == allPaymentPreferences.size()) { //if there are Check type, approve the order only if it is face to face List<GenericValue> checkPreferences = EntityUtil.filterByAnd(cashCodPcBaPaymentPreferences, UtilMisc.toMap("paymentMethodTypeId", "PERSONAL_CHECK")); if (UtilValidate.isNotEmpty(checkPreferences)) { if (faceToFace) { boolean ok = OrderChangeHelper.approveOrder(dispatcher, userLogin, orderId, manualHold); if (!ok) { throw new GeneralException("Problem with order change; see above error"); } } // approve this as long as there are only CASH, COD and Billing Account types } else { boolean ok = OrderChangeHelper.approveOrder(dispatcher, userLogin, orderId, manualHold); if (!ok) { throw new GeneralException("Problem with order change; see above error"); } } } else { // There is nothing to do, we just treat this as a success } } // check to see if we should auto-invoice/bill if (faceToFace) { if (Debug.verboseOn()) Debug.logVerbose("Face-To-Face Sale - " + orderId, module); CheckOutHelper.adjustFaceToFacePayment(orderId, orderTotal, allPaymentPreferences, userLogin, delegator); boolean ok = OrderChangeHelper.completeOrder(dispatcher, userLogin, orderId); if (Debug.verboseOn()) Debug.logVerbose("Complete Order Result - " + ok, module); if (!ok) { throw new GeneralException("Problem with order change; see error logs"); } } return ServiceUtil.returnSuccess(); } public static void adjustFaceToFacePayment(String orderId, BigDecimal cartTotal, List<GenericValue> allPaymentPrefs, GenericValue userLogin, Delegator delegator) throws GeneralException { BigDecimal prefTotal = BigDecimal.ZERO; if (allPaymentPrefs != null) { for (GenericValue pref : allPaymentPrefs) { BigDecimal maxAmount = pref.getBigDecimal("maxAmount"); if (maxAmount == null) maxAmount = BigDecimal.ZERO; prefTotal = prefTotal.add(maxAmount); } } if (prefTotal.compareTo(cartTotal) > 0) { BigDecimal change = prefTotal.subtract(cartTotal).negate(); GenericValue newPref = delegator.makeValue("OrderPaymentPreference"); newPref.set("orderId", orderId); newPref.set("paymentMethodTypeId", "CASH"); newPref.set("statusId", "PAYMENT_RECEIVED"); newPref.set("maxAmount", change); newPref.set("createdDate", UtilDateTime.nowTimestamp()); if (userLogin != null) { newPref.set("createdByUserLogin", userLogin.getString("userLoginId")); } delegator.createSetNextSeqId(newPref); } } public Map<String, Object> checkOrderBlackList() { if (cart == null) { return ServiceUtil.returnSuccess("success"); } GenericValue shippingAddressObj = this.cart.getShippingAddress(); if (shippingAddressObj == null) { return ServiceUtil.returnSuccess("success"); } String shippingAddress = UtilFormatOut.checkNull(shippingAddressObj.getString("address1")).toUpperCase(); shippingAddress = UtilFormatOut.makeSqlSafe(shippingAddress); List<EntityExpr> exprs = UtilMisc.toList(EntityCondition.makeCondition( EntityCondition.makeCondition(EntityFunction.UPPER_FIELD("blacklistString"), EntityOperator.EQUALS, EntityFunction.UPPER(shippingAddress)), EntityOperator.AND, EntityCondition.makeCondition("orderBlacklistTypeId", EntityOperator.EQUALS, "BLACKLIST_ADDRESS"))); String errMsg=null; List<GenericValue> paymentMethods = this.cart.getPaymentMethods(); for (GenericValue paymentMethod : paymentMethods) { if ((paymentMethod != null) && ("CREDIT_CARD".equals(paymentMethod.getString("paymentMethodTypeId")))) { GenericValue creditCard = null; GenericValue billingAddress = null; try { creditCard = paymentMethod.getRelatedOne("CreditCard", false); if (creditCard != null) billingAddress = creditCard.getRelatedOne("PostalAddress", false); } catch (GenericEntityException e) { Debug.logError(e, "Problems getting credit card from payment method", module); errMsg = UtilProperties.getMessage(resource_error,"checkhelper.problems_reading_database", (cart != null ? cart.getLocale() : Locale.getDefault())); return ServiceUtil.returnError(errMsg); } if (creditCard != null) { String creditCardNumber = UtilFormatOut.checkNull(creditCard.getString("cardNumber")); exprs.add(EntityCondition.makeCondition( EntityCondition.makeCondition("blacklistString", EntityOperator.EQUALS, creditCardNumber), EntityOperator.AND, EntityCondition.makeCondition("orderBlacklistTypeId", EntityOperator.EQUALS, "BLACKLIST_CREDITCARD"))); } if (billingAddress != null) { String address = UtilFormatOut.checkNull(billingAddress.getString("address1").toUpperCase()); address = UtilFormatOut.makeSqlSafe(address); exprs.add(EntityCondition.makeCondition( EntityCondition.makeCondition(EntityFunction.UPPER_FIELD("blacklistString"), EntityOperator.EQUALS, EntityFunction.UPPER(address)), EntityOperator.AND, EntityCondition.makeCondition("orderBlacklistTypeId", EntityOperator.EQUALS, "BLACKLIST_ADDRESS"))); } } } List<GenericValue> blacklistFound = null; if (exprs.size() > 0) { try { blacklistFound = EntityQuery.use(this.delegator).from("OrderBlacklist").where(exprs).queryList(); } catch (GenericEntityException e) { Debug.logError(e, "Problems with OrderBlacklist lookup.", module); errMsg = UtilProperties.getMessage(resource_error,"checkhelper.problems_reading_database", (cart != null ? cart.getLocale() : Locale.getDefault())); return ServiceUtil.returnError(errMsg); } } if (UtilValidate.isNotEmpty(blacklistFound)) { return ServiceUtil.returnFailure(UtilProperties.getMessage(resource_error,"OrderFailed", (cart != null ? cart.getLocale() : Locale.getDefault()))); } else { return ServiceUtil.returnSuccess("success"); } } @Deprecated public Map<String, Object> checkOrderBlacklist(GenericValue userLogin) { return checkOrderBlackList(); } public Map<String, Object> failedBlacklistCheck(GenericValue userLogin, GenericValue productStore) { Map<String, Object> result; String errMsg=null; String REJECT_MESSAGE = productStore.getString("authFraudMessage"); String orderId = this.cart.getOrderId(); try { if (userLogin != null) { // nuke the userlogin userLogin.set("enabled", "N"); userLogin.store(); } else { userLogin = EntityQuery.use(delegator).from("UserLogin").where("userLoginId", "system").cache().queryOne(); } } catch (GenericEntityException e) { Debug.logError(e, module); errMsg = UtilProperties.getMessage(resource_error,"checkhelper.database_error", (cart != null ? cart.getLocale() : Locale.getDefault())); result = ServiceUtil.returnError(errMsg); return result; } // set the order/item status - reverse inv OrderChangeHelper.rejectOrder(dispatcher, userLogin, orderId); result = ServiceUtil.returnSuccess(); result.put(ModelService.ERROR_MESSAGE, REJECT_MESSAGE); // wipe the cart and session this.cart.clear(); return result; } public Map<String, Object> checkExternalPayment(String orderId) { Map<String, Object> result; String errMsg = null; // warning there can only be ONE payment preference for this to work // you cannot accept multiple payment type when using an external gateway GenericValue orderHeader = null; try { orderHeader = EntityQuery.use(delegator).from("OrderHeader").where("orderId", orderId).queryOne(); } catch (GenericEntityException e) { Debug.logError(e, "Problems getting order header", module); errMsg = UtilProperties.getMessage(resource_error,"checkhelper.problems_getting_order_header", (cart != null ? cart.getLocale() : Locale.getDefault())); result = ServiceUtil.returnError(errMsg); return result; } if (orderHeader != null) { List<GenericValue> paymentPrefs = null; try { paymentPrefs = orderHeader.getRelated("OrderPaymentPreference", null, null, false); } catch (GenericEntityException e) { Debug.logError(e, "Problems getting order payments", module); errMsg = UtilProperties.getMessage(resource_error,"checkhelper.problems_getting_payment_preference", (cart != null ? cart.getLocale() : Locale.getDefault())); result = ServiceUtil.returnError(errMsg); return result; } if (UtilValidate.isNotEmpty(paymentPrefs)) { if (paymentPrefs.size() > 1) { Debug.logError("Too many payment preferences, you cannot have more then one when using external gateways", module); } GenericValue paymentPreference = EntityUtil.getFirst(paymentPrefs); String paymentMethodTypeId = paymentPreference.getString("paymentMethodTypeId"); if (paymentMethodTypeId.startsWith("EXT_")) { // PayPal with a PaymentMethod is not an external payment method if (!(paymentMethodTypeId.equals("EXT_PAYPAL") && UtilValidate.isNotEmpty(paymentPreference.getString("paymentMethodId")))) { String type = paymentMethodTypeId.substring(4); result = ServiceUtil.returnSuccess(); result.put("type", type.toLowerCase()); return result; } } } result = ServiceUtil.returnSuccess(); result.put("type", "none"); return result; } else { errMsg = UtilProperties.getMessage(resource_error,"checkhelper.problems_getting_order_header", (cart != null ? cart.getLocale() : Locale.getDefault())); result = ServiceUtil.returnError(errMsg); result.put("type", "error"); return result; } } /** * Sets the shipping contact mechanism for a given ship group on the cart * * @param shipGroupIndex The index of the ship group in the cart * @param shippingContactMechId The identifier of the contact * @return A Map conforming to the OFBiz Service conventions containing * any error messages */ public Map<String, Object> finalizeOrderEntryShip(int shipGroupIndex, String shippingContactMechId, String supplierPartyId) { Map<String, Object> result; String errMsg=null; //Verify the field is valid if (UtilValidate.isNotEmpty(shippingContactMechId)) { this.cart.setShippingContactMechId(shipGroupIndex, shippingContactMechId); if (UtilValidate.isNotEmpty(supplierPartyId)) { this.cart.setSupplierPartyId(shipGroupIndex, supplierPartyId); } result = ServiceUtil.returnSuccess(); } else { errMsg = UtilProperties.getMessage(resource_error,"checkhelper.enter_shipping_address", (cart != null ? cart.getLocale() : Locale.getDefault())); result = ServiceUtil.returnError(errMsg); } return result; } /** * Sets the options associated with the order for a given ship group * * @param shipGroupIndex The index of the ship group in the cart * @param shippingMethod The shipping method indicating the carrier and * shipment type to use * @param shippingInstructions Any additional handling instructions * @param maySplit "true" or anything else for <code>false</code> * @param giftMessage A message to have included for the recipient * @param isGift "true" or anything else for <code>false</code> * @param internalCode an internal code associated with the order * @return A Map conforming to the OFBiz Service conventions containing * any error messages */ public Map<String, Object> finalizeOrderEntryOptions(int shipGroupIndex, String shippingMethod, String shippingInstructions, String maySplit, String giftMessage, String isGift, String internalCode, String shipBeforeDate, String shipAfterDate, String orderAdditionalEmails) { this.cart.setOrderAdditionalEmails(orderAdditionalEmails); return finalizeOrderEntryOptions(shipGroupIndex, shippingMethod, shippingInstructions, maySplit, giftMessage, isGift, internalCode, shipBeforeDate, shipAfterDate, null, null); } public Map<String, Object> finalizeOrderEntryOptions(int shipGroupIndex, String shippingMethod, String shippingInstructions, String maySplit, String giftMessage, String isGift, String internalCode, String shipBeforeDate, String shipAfterDate, String internalOrderNotes, String shippingNotes, BigDecimal shipEstimate) { this.cart.setItemShipGroupEstimate(shipEstimate, shipGroupIndex); return finalizeOrderEntryOptions(shipGroupIndex, shippingMethod, shippingInstructions, maySplit, giftMessage, isGift, internalCode, shipBeforeDate, shipAfterDate, internalOrderNotes, shippingNotes); } public Map<String, Object> finalizeOrderEntryOptions(int shipGroupIndex, String shippingMethod, String shippingInstructions, String maySplit, String giftMessage, String isGift, String internalCode, String shipBeforeDate, String shipAfterDate, String internalOrderNotes, String shippingNotes) { Map<String, Object> result = ServiceUtil.returnSuccess(); String errMsg=null; //Verify the shipping method is valid if (UtilValidate.isNotEmpty(shippingMethod)) { int delimiterPos = shippingMethod.indexOf('@'); String shipmentMethodTypeId = null; String carrierPartyId = null; if (delimiterPos > 0) { shipmentMethodTypeId = shippingMethod.substring(0, delimiterPos); carrierPartyId = shippingMethod.substring(delimiterPos + 1); } this.cart.setShipmentMethodTypeId(shipGroupIndex, shipmentMethodTypeId); this.cart.setCarrierPartyId(shipGroupIndex, carrierPartyId); } else { errMsg = UtilProperties.getMessage(resource_error,"checkhelper.select_shipping_method", (cart != null ? cart.getLocale() : Locale.getDefault())); result = ServiceUtil.returnError(errMsg); } //Set the remaining order options this.cart.setShippingInstructions(shipGroupIndex, shippingInstructions); this.cart.setGiftMessage(shipGroupIndex, giftMessage); this.cart.setMaySplit(shipGroupIndex, Boolean.valueOf(maySplit)); this.cart.setIsGift(shipGroupIndex, Boolean.valueOf(isGift)); this.cart.setInternalCode(internalCode); // FIXME: the internalCode is not a ship group field and should be moved outside of this method if (UtilValidate.isNotEmpty(internalOrderNotes)) { this.cart.addInternalOrderNote(internalOrderNotes); } // set ship before date if ((shipBeforeDate != null) && (shipBeforeDate.length() > 8)) { shipBeforeDate = shipBeforeDate.trim(); if (shipBeforeDate.length() < 14) { shipBeforeDate = shipBeforeDate + " " + "00:00:00.000"; } try { this.cart.setShipBeforeDate(shipGroupIndex, (Timestamp) ObjectType.simpleTypeConvert(shipBeforeDate, "Timestamp", null, null)); } catch (Exception e) { errMsg = "Ship Before Date must be a valid date formed "; result = ServiceUtil.returnError(errMsg); } } // set ship after date if ((shipAfterDate != null) && (shipAfterDate.length() > 8)) { shipAfterDate = shipAfterDate.trim(); if (shipAfterDate.length() < 14) { shipAfterDate = shipAfterDate + " " + "00:00:00.000"; } try { this.cart.setShipAfterDate(shipGroupIndex, (Timestamp) ObjectType.simpleTypeConvert(shipAfterDate,"Timestamp", null, null)); } catch (Exception e) { errMsg = "Ship After Date must be a valid date formed "; result = ServiceUtil.returnError(errMsg); } } // Shipping Notes for order will be public if (UtilValidate.isNotEmpty(shippingNotes)) { this.cart.addOrderNote(shippingNotes); } return result; } /** * Sets the payment ID to use during the checkout process * * @param checkOutPaymentId The payment ID to be associated with the cart * @return A Map conforming to the OFBiz Service conventions containing * any error messages. */ public Map<String, Object> finalizeOrderEntryPayment(String checkOutPaymentId, BigDecimal amount, boolean singleUse, boolean append) { Map<String, Object> result = ServiceUtil.returnSuccess(); if (UtilValidate.isNotEmpty(checkOutPaymentId)) { if (!append) { cart.clearPayments(); } cart.addPaymentAmount(checkOutPaymentId, amount, singleUse); } return result; } public static BigDecimal availableAccountBalance(String billingAccountId, LocalDispatcher dispatcher) { if (billingAccountId == null) return BigDecimal.ZERO; try { Map<String, Object> res = dispatcher.runSync("calcBillingAccountBalance", UtilMisc.toMap("billingAccountId", billingAccountId)); BigDecimal availableBalance = (BigDecimal) res.get("accountBalance"); if (availableBalance != null) { return availableBalance; } } catch (GenericServiceException e) { Debug.logError(e, module); } return BigDecimal.ZERO; } public BigDecimal availableAccountBalance(String billingAccountId) { return availableAccountBalance(billingAccountId, dispatcher); } public Map<String, BigDecimal> makeBillingAccountMap(List<GenericValue> paymentPrefs) { Map<String, BigDecimal> accountMap = new HashMap<String, BigDecimal>(); if (paymentPrefs != null) { for (GenericValue pp : paymentPrefs) { if (pp.get("billingAccountId") != null) { accountMap.put(pp.getString("billingAccountId"), pp.getBigDecimal("maxAmount")); } } } return accountMap; } public Map<String, Object> validatePaymentMethods() { String errMsg = null; String billingAccountId = cart.getBillingAccountId(); BigDecimal billingAccountAmt = cart.getBillingAccountAmount(); BigDecimal availableAmount = this.availableAccountBalance(billingAccountId); if (billingAccountAmt.compareTo(availableAmount) > 0) { Debug.logError("Billing account " + billingAccountId + " has [" + availableAmount + "] available but needs [" + billingAccountAmt + "] for this order", module); Map<String, String> messageMap = UtilMisc.toMap("billingAccountId", billingAccountId); errMsg = UtilProperties.getMessage(resource_error, "checkevents.not_enough_available_on_account", messageMap, (cart != null ? cart.getLocale() : Locale.getDefault())); return ServiceUtil.returnError(errMsg); } // payment by billing account only requires more checking List<String> paymentMethods = cart.getPaymentMethodIds(); List<String> paymentTypes = cart.getPaymentMethodTypeIds(); if (paymentTypes.contains("EXT_BILLACT") && paymentTypes.size() == 1 && paymentMethods.size() == 0) { if (cart.getGrandTotal().compareTo(availableAmount) > 0) { errMsg = UtilProperties.getMessage(resource_error, "checkevents.insufficient_credit_available_on_account", (cart != null ? cart.getLocale() : Locale.getDefault())); return ServiceUtil.returnError(errMsg); } } // validate any gift card balances this.validateGiftCardAmounts(); // update the selected payment methods amount with valid numbers if (paymentMethods != null) { List<String> nullPaymentIds = new ArrayList<String>(); for (String paymentMethodId : paymentMethods) { BigDecimal paymentAmount = cart.getPaymentAmount(paymentMethodId); if (paymentAmount == null || paymentAmount.compareTo(BigDecimal.ZERO) == 0) { if (Debug.verboseOn()) Debug.logVerbose("Found null paymentMethodId - " + paymentMethodId, module); nullPaymentIds.add(paymentMethodId); } } for (String paymentMethodId : nullPaymentIds) { BigDecimal selectedPaymentTotal = cart.getPaymentTotal(); BigDecimal requiredAmount = cart.getGrandTotal(); BigDecimal newAmount = requiredAmount.subtract(selectedPaymentTotal); boolean setOverflow = false; ShoppingCart.CartPaymentInfo info = cart.getPaymentInfo(paymentMethodId); if (Debug.verboseOn()) Debug.logVerbose("Remaining total is - " + newAmount, module); if (newAmount.compareTo(BigDecimal.ZERO) > 0) { info.amount = newAmount; if (Debug.verboseOn()) Debug.logVerbose("Set null paymentMethodId - " + info.paymentMethodId + " / " + info.amount, module); } else { info.amount = BigDecimal.ZERO; if (Debug.verboseOn()) Debug.logVerbose("Set null paymentMethodId - " + info.paymentMethodId + " / " + info.amount, module); } if (!setOverflow) { info.overflow = setOverflow = true; if (Debug.verboseOn()) Debug.logVerbose("Set overflow flag on payment - " + info.paymentMethodId, module); } } } // verify the selected payment method amounts will cover the total BigDecimal reqAmtPreParse = cart.getGrandTotal().subtract(cart.getBillingAccountAmount()); BigDecimal selectedPmnt = cart.getPaymentTotal(); BigDecimal selectedPaymentTotal = selectedPmnt.setScale(scale, rounding); BigDecimal requiredAmount = reqAmtPreParse.setScale(scale, rounding); if (UtilValidate.isNotEmpty(paymentMethods) && requiredAmount.compareTo(selectedPaymentTotal) > 0) { Debug.logError("Required Amount : " + requiredAmount + " / Selected Amount : " + selectedPaymentTotal, module); errMsg = UtilProperties.getMessage(resource_error, "checkevents.payment_not_cover_this_order", (cart != null ? cart.getLocale() : Locale.getDefault())); return ServiceUtil.returnError(errMsg); } if (UtilValidate.isNotEmpty(paymentMethods) && requiredAmount.compareTo(selectedPaymentTotal) < 0) { BigDecimal changeAmount = selectedPaymentTotal.subtract(requiredAmount); if (!paymentTypes.contains("CASH")) { Debug.logError("Change Amount : " + changeAmount + " / No cash.", module); errMsg = UtilProperties.getMessage(resource_error, "checkhelper.change_returned_cannot_be_greater_than_cash", (cart != null ? cart.getLocale() : Locale.getDefault())); return ServiceUtil.returnError(errMsg); } else { int cashIndex = paymentTypes.indexOf("CASH"); String cashId = paymentTypes.get(cashIndex); BigDecimal cashAmount = cart.getPaymentAmount(cashId); if (cashAmount.compareTo(changeAmount) < 0) { Debug.logError("Change Amount : " + changeAmount + " / Cash Amount : " + cashAmount, module); errMsg = UtilProperties.getMessage(resource_error, "checkhelper.change_returned_cannot_be_greater_than_cash", (cart != null ? cart.getLocale() : Locale.getDefault())); return ServiceUtil.returnError(errMsg); } } } return ServiceUtil.returnSuccess(); } public void validateGiftCardAmounts() { // get the product store GenericValue productStore = ProductStoreWorker.getProductStore(cart.getProductStoreId(), delegator); if (productStore != null && !"Y".equalsIgnoreCase(productStore.getString("checkGcBalance"))) { return; } // get the payment config String paymentConfig = ProductStoreWorker.getProductStorePaymentProperties(delegator, cart.getProductStoreId(), "GIFT_CARD", null, true); String giftCardType = EntityUtilProperties.getPropertyValue(paymentConfig, "", "ofbiz", delegator); String balanceField = null; // get the gift card objects to check for (GenericValue gc : cart.getGiftCards()) { Map<String, Object> gcBalanceMap = null; BigDecimal gcBalance = BigDecimal.ZERO; try { Map<String, Object> ctx = UtilMisc.<String, Object>toMap("userLogin", cart.getUserLogin()); ctx.put("currency", cart.getCurrency()); if ("ofbiz".equalsIgnoreCase(giftCardType)) { balanceField = "balance"; ctx.put("cardNumber", gc.getString("cardNumber")); ctx.put("pinNumber", gc.getString("pinNumber")); gcBalanceMap = dispatcher.runSync("checkGiftCertificateBalance", ctx); } if ("valuelink".equalsIgnoreCase(giftCardType)) { balanceField = "balance"; ctx.put("paymentConfig", paymentConfig); ctx.put("cardNumber", gc.getString("cardNumber")); ctx.put("pin", gc.getString("pinNumber")); gcBalanceMap = dispatcher.runSync("balanceInquireGiftCard", ctx); } } catch (GenericServiceException e) { Debug.logError(e, module); } if (gcBalanceMap != null) { BigDecimal bal = (BigDecimal) gcBalanceMap.get(balanceField); if (bal != null) { gcBalance = bal; } } // get the bill-up to amount BigDecimal billUpTo = cart.getPaymentAmount(gc.getString("paymentMethodId")); // null bill-up to means use the full balance || update the bill-up to with the balance if (billUpTo == null || billUpTo.compareTo(BigDecimal.ZERO) == 0 || gcBalance.compareTo(billUpTo) < 0) { cart.addPaymentAmount(gc.getString("paymentMethodId"), gcBalance); } } } }