blob: d06d999a7e14750d93938d7d1d3210b334621872 [file] [log] [blame]
/*
* 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.order;
import java.math.BigDecimal;
import java.sql.Timestamp;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Locale;
import java.util.Map;
import org.apache.ofbiz.base.util.Debug;
import org.apache.ofbiz.base.util.GeneralRuntimeException;
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.EntityConditionList;
import org.apache.ofbiz.entity.condition.EntityExpr;
import org.apache.ofbiz.entity.condition.EntityOperator;
import org.apache.ofbiz.entity.util.EntityQuery;
import org.apache.ofbiz.entity.util.EntityUtil;
import org.apache.ofbiz.entity.util.EntityUtilProperties;
import org.apache.ofbiz.order.thirdparty.paypal.ExpressCheckoutEvents;
import org.apache.ofbiz.product.product.ProductContentWrapper;
import org.apache.ofbiz.product.product.ProductWorker;
import org.apache.ofbiz.product.store.ProductStoreWorker;
import org.apache.ofbiz.service.DispatchContext;
import org.apache.ofbiz.service.GenericServiceException;
import org.apache.ofbiz.service.LocalDispatcher;
import org.apache.ofbiz.service.ModelService;
import org.apache.ofbiz.service.ServiceUtil;
import com.ibm.icu.util.Calendar;
/**
* OrderReturnServices
*/
public class OrderReturnServices {
public static final String module = OrderReturnServices.class.getName();
public static final String resource = "OrderUiLabels";
public static final String resource_error = "OrderErrorUiLabels";
public static final String resourceProduct = "ProductUiLabels";
// set some BigDecimal properties
private static BigDecimal ZERO = BigDecimal.ZERO;
private static int decimals = -1;
private static int rounding = -1;
static {
decimals = UtilNumber.getBigDecimalScale("invoice.decimals");
rounding = UtilNumber.getBigDecimalRoundingMode("invoice.rounding");
// set zero to the proper scale
if (decimals != -1) ZERO = ZERO.setScale(decimals);
}
// locate the return item's initial inventory item cost
public static Map<String, Object> getReturnItemInitialCost(DispatchContext dctx, Map<String, ? extends Object> context) {
Delegator delegator = dctx.getDelegator();
String returnId = (String) context.get("returnId");
String returnItemSeqId = (String) context.get("returnItemSeqId");
Map<String, Object> result = ServiceUtil.returnSuccess();
result.put("initialItemCost", getReturnItemInitialCost(delegator, returnId, returnItemSeqId));
return result;
}
// obtain order/return total information
public static Map<String, Object> getOrderAvailableReturnedTotal(DispatchContext dctx, Map<String, ? extends Object> context) {
Delegator delegator = dctx.getDelegator();
String orderId = (String) context.get("orderId");
OrderReadHelper orh = null;
try {
orh = new OrderReadHelper(delegator, orderId);
} catch (IllegalArgumentException e) {
return ServiceUtil.returnError(e.getMessage());
}
// an adjustment value to test
BigDecimal adj = (BigDecimal) context.get("adjustment");
if (adj == null) {
adj = ZERO;
}
Boolean countNewReturnItems = (Boolean) context.get("countNewReturnItems");
if (countNewReturnItems == null) {
countNewReturnItems = Boolean.FALSE;
}
BigDecimal returnTotal = orh.getOrderReturnedTotal(countNewReturnItems.booleanValue());
BigDecimal orderTotal = orh.getOrderGrandTotal();
BigDecimal available = orderTotal.subtract(returnTotal).subtract(adj);
Map<String, Object> result = ServiceUtil.returnSuccess();
result.put("availableReturnTotal", available);
result.put("orderTotal", orderTotal);
result.put("returnTotal", returnTotal);
return result;
}
// worker method which can be used in screen iterations
public static BigDecimal getReturnItemInitialCost(Delegator delegator, String returnId, String returnItemSeqId) {
if (delegator == null || returnId == null || returnItemSeqId == null) {
throw new IllegalArgumentException("Method parameters cannot contain nulls");
}
Debug.logInfo("Finding the initial item cost for return item : " + returnId + " / " + returnItemSeqId, module);
// the cost holder
BigDecimal itemCost = BigDecimal.ZERO;
// get the return item information
GenericValue returnItem = null;
try {
returnItem = EntityQuery.use(delegator).from("ReturnItem").where("returnId", returnId, "returnItemSeqId", returnItemSeqId).queryOne();
} catch (GenericEntityException e) {
Debug.logError(e, module);
throw new GeneralRuntimeException(e.getMessage());
}
Debug.logInfo("Return item value object - " + returnItem, module);
// check for an orderItem association
if (returnItem != null) {
String orderId = returnItem.getString("orderId");
String orderItemSeqId = returnItem.getString("orderItemSeqId");
if (orderItemSeqId != null && orderId != null) {
Debug.logInfo("Found order item reference", module);
// locate the item issuance(s) for this order item
GenericValue issue = null;
try {
issue = EntityQuery.use(delegator).from("ItemIssuance").where("orderId", orderId, "orderItemSeqId", orderItemSeqId).queryFirst();
} catch (GenericEntityException e) {
Debug.logError(e, module);
throw new GeneralRuntimeException(e.getMessage());
}
if (issue != null) {
Debug.logInfo("Found item issuance reference", module);
// just use the first one for now; maybe later we can find a better way to determine which was the
// actual item being returned; maybe by serial number
GenericValue inventoryItem = null;
try {
inventoryItem = issue.getRelatedOne("InventoryItem", false);
} catch (GenericEntityException e) {
Debug.logError(e, module);
throw new GeneralRuntimeException(e.getMessage());
}
if (inventoryItem != null) {
Debug.logInfo("Located inventory item - " + inventoryItem.getString("inventoryItemId"), module);
if (inventoryItem.get("unitCost") != null) {
itemCost = inventoryItem.getBigDecimal("unitCost");
} else {
Debug.logInfo("Found item cost; but cost was null. Returning default amount (0.00)", module);
}
}
}
}
}
Debug.logInfo("Initial item cost - " + itemCost, module);
return itemCost;
}
// helper method for sending return notifications
private static Map<String, Object> sendReturnNotificationScreen(DispatchContext dctx, Map<String, ? extends Object> context, String emailType) {
Delegator delegator = dctx.getDelegator();
LocalDispatcher dispatcher = dctx.getDispatcher();
GenericValue userLogin = (GenericValue) context.get("userLogin");
String returnId = (String) context.get("returnId");
Locale locale = (Locale) context.get("locale");
// get the return header
GenericValue returnHeader = null;
try {
returnHeader = EntityQuery.use(delegator).from("ReturnHeader").where("returnId", returnId).queryOne();
} catch (GenericEntityException e) {
Debug.logError(e, module);
return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,
"OrderErrorUnableToGetReturnHeaderForID", UtilMisc.toMap("returnId",returnId), locale));
}
// get the return items
List<GenericValue> returnItems = null;
List<GenericValue> returnAdjustments = new LinkedList<GenericValue>();
try {
returnItems = returnHeader.getRelated("ReturnItem", null, null, false);
returnAdjustments = EntityQuery.use(delegator).from("ReturnAdjustment")
.where("returnId", returnId, "returnItemSeqId", "_NA_")
.orderBy("returnAdjustmentTypeId")
.cache(true)
.queryList();
} catch (GenericEntityException e) {
Debug.logError(e, module);
return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,
"OrderErrorUnableToGetReturnItemRecordsFromReturnHeader", locale));
}
// get the order header -- the first item will determine which product store to use from the order
String productStoreId = null;
String emailAddress = null;
if (UtilValidate.isNotEmpty(returnItems)) {
GenericValue firstItem = EntityUtil.getFirst(returnItems);
GenericValue orderHeader = null;
try {
orderHeader = firstItem.getRelatedOne("OrderHeader", false);
} catch (GenericEntityException e) {
Debug.logError(e, module);
return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,
"OrderErrorUnableToGetOrderHeaderFromReturnItem", locale));
}
if (orderHeader != null && UtilValidate.isNotEmpty(orderHeader.getString("productStoreId"))) {
OrderReadHelper orh = new OrderReadHelper(orderHeader);
productStoreId = orh.getProductStoreId();
emailAddress = orh.getOrderEmailString();
}
}
// get the email setting and send the mail
if (UtilValidate.isNotEmpty(productStoreId)) {
Map<String, Object> sendMap = new HashMap<String, Object>();
GenericValue productStoreEmail = null;
try {
productStoreEmail = EntityQuery.use(delegator).from("ProductStoreEmailSetting").where("productStoreId", productStoreId, "emailType", emailType).queryOne();
} catch (GenericEntityException e) {
Debug.logError(e, module);
}
if (productStoreEmail != null && emailAddress != null) {
String bodyScreenLocation = productStoreEmail.getString("bodyScreenLocation");
if (UtilValidate.isEmpty(bodyScreenLocation)) {
bodyScreenLocation = ProductStoreWorker.getDefaultProductStoreEmailScreenLocation(emailType);
}
sendMap.put("bodyScreenUri", bodyScreenLocation);
String xslfoAttachScreenLocation = productStoreEmail.getString("xslfoAttachScreenLocation");
sendMap.put("xslfoAttachScreenLocation", xslfoAttachScreenLocation);
Map<String, Object> bodyParameters = UtilMisc.<String, Object>toMap("returnHeader", returnHeader, "returnItems", returnItems, "returnAdjustments", returnAdjustments, "locale", locale, "userLogin", userLogin);
sendMap.put("bodyParameters", bodyParameters);
sendMap.put("subject", productStoreEmail.getString("subject"));
sendMap.put("contentType", productStoreEmail.get("contentType"));
sendMap.put("sendFrom", productStoreEmail.get("fromAddress"));
sendMap.put("sendCc", productStoreEmail.get("ccAddress"));
sendMap.put("sendBcc", productStoreEmail.get("bccAddress"));
sendMap.put("sendTo", emailAddress);
sendMap.put("partyId", returnHeader.getString("fromPartyId"));
sendMap.put("userLogin", userLogin);
Map<String, Object> sendResp = null;
try {
sendResp = dispatcher.runSync("sendMailFromScreen", sendMap);
} catch (GenericServiceException e) {
Debug.logError(e, "Problem sending mail", module);
return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,
"OrderProblemSendingEmail", locale));
}
// check for errors
if (sendResp != null && ServiceUtil.isError(sendResp)) {
sendResp.put("emailType", emailType);
return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,
"OrderProblemSendingEmail", locale), null, null, sendResp);
}
return ServiceUtil.returnSuccess();
}
}
return ServiceUtil.returnFailure(UtilProperties.getMessage(resourceProduct,
"ProductProductStoreEmailSettingsNotValid",
UtilMisc.toMap("productStoreId", productStoreId,
"emailType", emailType), locale));
}
// return request notification
public static Map<String, Object> sendReturnAcceptNotification(DispatchContext dctx, Map<String, ? extends Object> context) {
return sendReturnNotificationScreen(dctx, context, "PRDS_RTN_ACCEPT");
}
// return complete notification
public static Map<String, Object> sendReturnCompleteNotification(DispatchContext dctx, Map<String, ? extends Object> context) {
return sendReturnNotificationScreen(dctx, context, "PRDS_RTN_COMPLETE");
}
// return cancel notification
public static Map<String, Object> sendReturnCancelNotification(DispatchContext dctx, Map<String, ? extends Object> context) {
return sendReturnNotificationScreen(dctx, context, "PRDS_RTN_CANCEL");
}
// cancel replacement order if return not received within 30 days and send notification
public static Map<String,Object> autoCancelReplacementOrders(DispatchContext dctx, Map<String, ? extends Object> context) {
Delegator delegator = dctx.getDelegator();
LocalDispatcher dispatcher = dctx.getDispatcher();
GenericValue userLogin = (GenericValue) context.get("userLogin");
List<GenericValue> returnHeaders = null;
try {
returnHeaders = EntityQuery.use(delegator).from("ReturnHeader")
.where("statusId", "RETURN_ACCEPTED", "returnHeaderTypeId", "CUSTOMER_RETURN")
.orderBy("entryDate")
.queryList();
} catch (GenericEntityException e) {
Debug.logError(e, "Problem getting Return headers", module);
}
for (GenericValue returnHeader : returnHeaders) {
String returnId = returnHeader.getString("returnId");
Timestamp entryDate = returnHeader.getTimestamp("entryDate");
String daysTillCancelStr = EntityUtilProperties.getPropertyValue("order", "daysTillCancelReplacementOrder", "30", delegator);
int daysTillCancel = 0;
try {
daysTillCancel = Integer.parseInt(daysTillCancelStr);
} catch (NumberFormatException e) {
Debug.logError(e, "Unable to get daysTillCancel", module);
}
if (daysTillCancel > 0) {
Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(entryDate.getTime());
cal.add(Calendar.DAY_OF_YEAR, daysTillCancel);
Date cancelDate = cal.getTime();
Date nowDate = new Date();
if (cancelDate.equals(nowDate) || nowDate.after(cancelDate)) {
try {
List<GenericValue> returnItems = EntityQuery.use(delegator).from("ReturnItem")
.where("returnId", returnId, "returnTypeId", "RTN_WAIT_REPLACE_RES")
.orderBy("createdStamp")
.queryList();
for (GenericValue returnItem : returnItems) {
GenericValue returnItemResponse = returnItem.getRelatedOne("ReturnItemResponse", false);
if (returnItemResponse != null) {
String replacementOrderId = returnItemResponse.getString("replacementOrderId");
Map<String, Object> svcCtx = UtilMisc.<String, Object>toMap("orderId", replacementOrderId, "userLogin", userLogin);
GenericValue orderHeader = EntityQuery.use(delegator).from("OrderHeader").where("orderId", replacementOrderId).queryOne();
if ("ORDER_HOLD".equals(orderHeader.getString("statusId"))) {
try {
dispatcher.runSync("cancelOrderItem", svcCtx);
} catch (GenericServiceException e) {
Debug.logError(e, "Problem calling service cancelOrderItem: " + svcCtx, module);
}
}
}
}
} catch (GenericEntityException e) {
Debug.logError(e, module);
}
}
}
}
return ServiceUtil.returnSuccess();
}
// get the returnable quantiy for an order item
public static Map<String, Object> getReturnableQuantity(DispatchContext dctx, Map<String, ? extends Object> context) {
GenericValue orderItem = (GenericValue) context.get("orderItem");
GenericValue product = null;
Locale locale = (Locale) context.get("locale");
if (orderItem.get("productId") != null) {
try {
product = orderItem.getRelatedOne("Product", false);
} catch (GenericEntityException e) {
Debug.logError(e, "ERROR: Unable to get Product from OrderItem", module);
}
}
// check returnable status
boolean returnable = true;
// first check returnable flag
if (product != null && product.get("returnable") != null &&
"N".equalsIgnoreCase(product.getString("returnable"))) {
// the product is not returnable at all
returnable = false;
}
// next check support discontinuation
if (product != null && product.get("supportDiscontinuationDate") != null &&
!UtilDateTime.nowTimestamp().before(product.getTimestamp("supportDiscontinuationDate"))) {
// support discontinued either now or in the past
returnable = false;
}
String itemStatus = orderItem.getString("statusId");
BigDecimal orderQty = orderItem.getBigDecimal("quantity");
if (orderItem.getBigDecimal("cancelQuantity") != null) {
orderQty = orderQty.subtract(orderItem.getBigDecimal("cancelQuantity"));
}
// get the returnable quantity
BigDecimal returnableQuantity = BigDecimal.ZERO;
if (returnable && (itemStatus.equals("ITEM_APPROVED") || itemStatus.equals("ITEM_COMPLETED"))) {
List<GenericValue> returnedItems = null;
try {
returnedItems = orderItem.getRelated("ReturnItem", null, null, false);
} catch (GenericEntityException e) {
Debug.logError(e, module);
return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,
"OrderErrorUnableToGetReturnItemInformation", locale));
}
if (UtilValidate.isEmpty(returnedItems)) {
returnableQuantity = orderQty;
} else {
BigDecimal returnedQty = BigDecimal.ZERO;
for (GenericValue returnItem : returnedItems) {
GenericValue returnHeader = null;
try {
returnHeader = returnItem.getRelatedOne("ReturnHeader", false);
} catch (GenericEntityException e) {
Debug.logError(e, module);
return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,
"OrderErrorUnableToGetReturnHeaderFromItem", locale));
}
String returnStatus = returnHeader.getString("statusId");
if (!returnStatus.equals("RETURN_CANCELLED")) {
if(UtilValidate.isNotEmpty(returnItem.getBigDecimal("returnQuantity"))){
returnedQty = returnedQty.add(returnItem.getBigDecimal("returnQuantity"));
}
}
}
if (returnedQty.compareTo(orderQty) < 0) {
returnableQuantity = orderQty.subtract(returnedQty);
}
}
}
// get the returnable price now equals to orderItem.unitPrice, since adjustments are booked separately
Map<String, Object> result = ServiceUtil.returnSuccess();
result.put("returnableQuantity", returnableQuantity);
result.put("returnablePrice", orderItem.getBigDecimal("unitPrice"));
return result;
}
// get a map of returnable items (items not already returned) and quantities
public static Map<String, Object> getReturnableItems(DispatchContext dctx, Map<String, ? extends Object> context) {
LocalDispatcher dispatcher = dctx.getDispatcher();
Delegator delegator = dctx.getDelegator();
String orderId = (String) context.get("orderId");
Locale locale = (Locale) context.get("locale");
GenericValue orderHeader = null;
try {
orderHeader = EntityQuery.use(delegator).from("OrderHeader").where("orderId", orderId).queryOne();
} catch (GenericEntityException e) {
Debug.logError(e, module);
return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,
"OrderErrorUnableToGetReturnItemInformation", locale));
}
Map<GenericValue, Map<String, Object>> returnable = new LinkedHashMap<GenericValue, Map<String, Object>>();
if (orderHeader != null) {
// OrderReadHelper orh = new OrderReadHelper(orderHeader);
// OrderItems which have been issued may be returned.
EntityConditionList<EntityExpr> whereConditions = EntityCondition.makeCondition(UtilMisc.toList(
EntityCondition.makeCondition("orderId", EntityOperator.EQUALS, orderHeader.getString("orderId")),
EntityCondition.makeCondition("orderItemStatusId", EntityOperator.IN, UtilMisc.toList("ITEM_APPROVED", "ITEM_COMPLETED"))
), EntityOperator.AND);
List<GenericValue> orderItemQuantitiesIssued = null;
try {
orderItemQuantitiesIssued = EntityQuery.use(delegator).select("orderId", "orderItemSeqId", "quantityIssued").from("OrderItemQuantityReportGroupByItem").where(whereConditions).orderBy("orderItemSeqId").queryList();
} catch (GenericEntityException e) {
Debug.logError(e, module);
return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,
"OrderErrorUnableToGetReturnHeaderFromItem", locale));
}
if (orderItemQuantitiesIssued != null) {
for (GenericValue orderItemQuantityIssued : orderItemQuantitiesIssued) {
GenericValue item = null;
try {
item = orderItemQuantityIssued.getRelatedOne("OrderItem", false);
} catch (GenericEntityException e) {
Debug.logError(e, module);
return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,
"OrderErrorUnableToGetOrderItemInformation", locale));
}
// items not issued/shipped are considered as returnable only if they are
// not physical items
if ("SALES_ORDER".equals(orderHeader.getString("orderTypeId"))) {
BigDecimal quantityIssued = orderItemQuantityIssued.getBigDecimal("quantityIssued");
if (UtilValidate.isEmpty(quantityIssued) || quantityIssued.compareTo(BigDecimal.ZERO) == 0) {
try {
GenericValue itemProduct = item.getRelatedOne("Product", false);
if (ProductWorker.isPhysical(itemProduct)) {
continue;
}
} catch (GenericEntityException e) {
Debug.logError(e, "Problems looking up returnable product type information", module);
return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,
"OrderErrorUnableToGetTheItemReturnableProduct", locale));
}
}
}
Map<String, Object> serviceResult = null;
try {
serviceResult = dispatcher.runSync("getReturnableQuantity", UtilMisc.toMap("orderItem", item));
} catch (GenericServiceException e) {
Debug.logError(e, module);
return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,
"OrderErrorUnableToGetTheItemReturnableQuantity", locale));
}
if (serviceResult.containsKey(ModelService.ERROR_MESSAGE)) {
return ServiceUtil.returnError((String) serviceResult.get(ModelService.ERROR_MESSAGE));
} else {
// Don't add the OrderItem to the map of returnable OrderItems if there isn't any returnable quantity.
if (((BigDecimal) serviceResult.get("returnableQuantity")).compareTo(BigDecimal.ZERO) == 0) {
continue;
}
Map<String, Object> returnInfo = new HashMap<String, Object>();
// first the return info (quantity/price)
returnInfo.put("returnableQuantity", serviceResult.get("returnableQuantity"));
returnInfo.put("returnablePrice", serviceResult.get("returnablePrice"));
// now the product type information
String itemTypeKey = "FINISHED_GOOD"; // default item type (same as invoice)
GenericValue product = null;
if (item.get("productId") != null) {
try {
product = item.getRelatedOne("Product", false);
} catch (GenericEntityException e) {
Debug.logError(e, module);
return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,
"OrderErrorUnableToGetOrderItemInformation", locale));
}
}
if (product != null) {
itemTypeKey = product.getString("productTypeId");
} else if (item != null && item.getString("orderItemTypeId") != null) {
itemTypeKey = item.getString("orderItemTypeId");
}
returnInfo.put("itemTypeKey", itemTypeKey);
returnable.put(item, returnInfo);
// Order item adjustments
List<GenericValue> itemAdjustments = null;
try {
itemAdjustments = item.getRelated("OrderAdjustment", null, null, false);
} catch (GenericEntityException e) {
Debug.logError(e, module);
return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,
"OrderErrorUnableToGetOrderAdjustmentsFromItem", locale));
}
if (UtilValidate.isNotEmpty(itemAdjustments)) {
for (GenericValue itemAdjustment : itemAdjustments) {
returnInfo = new HashMap<String, Object>();
returnInfo.put("returnableQuantity", BigDecimal.ONE);
// TODO: the returnablePrice should be set to the amount minus the already returned amount
returnInfo.put("returnablePrice", itemAdjustment.get("amount"));
returnInfo.put("itemTypeKey", itemTypeKey);
returnable.put(itemAdjustment, returnInfo);
}
}
}
}
} else {
return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,
"OrderErrorNoOrderItemsFound", locale));
}
} else {
return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,
"OrderErrorUnableToFindOrderHeader", locale));
}
Map<String, Object> result = ServiceUtil.returnSuccess();
result.put("returnableItems", returnable);
return result;
}
// check return items status and update return header status
public static Map<String, Object> checkReturnComplete(DispatchContext dctx, Map<String, ? extends Object> context) {
Delegator delegator = dctx.getDelegator();
LocalDispatcher dispatcher = dctx.getDispatcher();
GenericValue userLogin = (GenericValue) context.get("userLogin");
String returnId = (String) context.get("returnId");
Locale locale = (Locale) context.get("locale");
GenericValue returnHeader = null;
List<GenericValue> returnItems = null;
try {
returnHeader = EntityQuery.use(delegator).from("ReturnHeader").where("returnId", returnId).queryOne();
if (returnHeader != null) {
returnItems = returnHeader.getRelated("ReturnItem", null, null, false);
}
} catch (GenericEntityException e) {
Debug.logError(e, "Problems looking up return information", module);
return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,
"OrderErrorGettingReturnHeaderItemInformation", locale));
}
// if already completed just return
String currentStatus = null;
if (returnHeader != null && returnHeader.get("statusId") != null) {
currentStatus = returnHeader.getString("statusId");
if ("RETURN_COMPLETED".equals(currentStatus) || "RETURN_CANCELLED".equals(currentStatus)) {
return ServiceUtil.returnSuccess();
}
}
List<GenericValue> completedItems = new LinkedList<GenericValue>();
if (returnHeader != null && UtilValidate.isNotEmpty(returnItems)) {
for (GenericValue item : returnItems) {
String itemStatus = item != null ? item.getString("statusId") : null;
if (itemStatus != null) {
// both completed and cancelled items qualify for completed status change
if ("RETURN_COMPLETED".equals(itemStatus) || "RETURN_CANCELLED".equals(itemStatus)) {
completedItems.add(item);
} else {
// Non-physical items don't need an inventory receive and so are
// considered completed after the return is accepted
if ("RETURN_ACCEPTED".equals(returnHeader.getString("statusId"))) {
try {
GenericValue itemProduct = item.getRelatedOne("Product", false);
if (!ProductWorker.isPhysical(itemProduct)) {
completedItems.add(item);
}
} catch (GenericEntityException e) {
Debug.logError(e, "Problems looking up returned product type information", module);
return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,
"OrderErrorGettingReturnHeaderItemInformation", locale));
}
}
}
}
}
// if all items are completed/cancelled these should match
if (completedItems.size() == returnItems.size()) {
// The return is just moved to its next status by calling the
// updateReturnHeader service; this will trigger all the appropriate ecas
// including this service again, so that the return is moved
// to the final status
if (currentStatus != null && "RETURN_ACCEPTED".equals(currentStatus)) {
try {
dispatcher.runSync("updateReturnHeader", UtilMisc.<String, Object>toMap("returnId", returnId,
"statusId", "RETURN_RECEIVED",
"userLogin", userLogin));
} catch (GenericServiceException e) {
Debug.logError(e, module);
return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,
"OrderErrorUnableToCreateReturnStatusHistory", locale));
}
} else if (currentStatus != null && "RETURN_RECEIVED".equals(currentStatus)) {
try {
dispatcher.runSync("updateReturnHeader", UtilMisc.<String, Object>toMap("returnId", returnId,
"statusId", "RETURN_COMPLETED",
"userLogin", userLogin));
} catch (GenericServiceException e) {
Debug.logError(e, module);
return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,
"OrderErrorUnableToCreateReturnStatusHistory", locale));
}
}
}
}
Map<String, Object> result = ServiceUtil.returnSuccess();
result.put("statusId", returnHeader.get("statusId"));
return result;
}
// credit (billingAccount) return
public static Map<String, Object> processCreditReturn(DispatchContext dctx, Map<String, ? extends Object> context) {
LocalDispatcher dispatcher = dctx.getDispatcher();
Delegator delegator = dctx.getDelegator();
String returnId = (String) context.get("returnId");
GenericValue userLogin = (GenericValue) context.get("userLogin");
Locale locale = (Locale) context.get("locale");
GenericValue returnHeader = null;
List<GenericValue> returnItems = null;
try {
returnHeader = EntityQuery.use(delegator).from("ReturnHeader").where("returnId", returnId).queryOne();
if (returnHeader != null) {
returnItems = returnHeader.getRelated("ReturnItem", UtilMisc.toMap("returnTypeId", "RTN_CREDIT"), null, false);
}
} catch (GenericEntityException e) {
Debug.logError(e, "Problems looking up return information", module);
return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,
"OrderErrorGettingReturnHeaderItemInformation", locale));
}
BigDecimal adjustments = getReturnAdjustmentTotal(delegator, UtilMisc.toMap("returnId", returnId, "returnTypeId", "RTN_CREDIT"));
if (returnHeader != null && (UtilValidate.isNotEmpty(returnItems) || adjustments.compareTo(ZERO) > 0)) {
String finAccountId = returnHeader.getString("finAccountId");
String billingAccountId = returnHeader.getString("billingAccountId");
String fromPartyId = returnHeader.getString("fromPartyId");
String toPartyId = returnHeader.getString("toPartyId");
// make sure total refunds on a return don't exceed amount of returned orders
Map<String, Object> serviceResult = null;
try {
serviceResult = dispatcher.runSync("checkPaymentAmountForRefund", UtilMisc.toMap("returnId", returnId));
} catch (GenericServiceException e) {
Debug.logError(e, "Problem running the checkPaymentAmountForRefund service", module);
return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,
"OrderProblemsWithCheckPaymentAmountForRefund", locale));
}
if (ServiceUtil.isError(serviceResult)) {
return ServiceUtil.returnError(ServiceUtil.getErrorMessage(serviceResult));
}
// Fetch the ProductStore
GenericValue productStore = null;
GenericValue orderHeader = null;
GenericValue returnItem = null;
if (UtilValidate.isNotEmpty(returnItems)) {
returnItem = EntityUtil.getFirst(returnItems);
}
if (returnItem != null) {
try {
orderHeader = returnItem.getRelatedOne("OrderHeader", false);
} catch (GenericEntityException e) {
return ServiceUtil.returnError(e.getMessage());
}
}
if (orderHeader != null) {
OrderReadHelper orderReadHelper = new OrderReadHelper(orderHeader);
productStore = orderReadHelper.getProductStore();
}
// if both billingAccountId and finAccountId are supplied, look for productStore.storeCreditAccountEnumId preference
if (finAccountId != null && billingAccountId != null && productStore != null && productStore.getString("storeCreditAccountEnumId") != null) {
Debug.logWarning("You have entered both financial account and billing account for store credit. Based on the configuration on product store, only one of them will be selected.", module);
if ("BILLING_ACCOUNT".equals(productStore.getString("storeCreditAccountEnumId"))) {
finAccountId = null;
Debug.logWarning("Default setting on product store is billing account. Store credit will goes to billing account [" + billingAccountId + "]", module);
} else {
billingAccountId = null;
Debug.logWarning("Default setting on product store is financial account. Store credit will goes to financial account [" + finAccountId + "]", module);
}
}
if (finAccountId == null && billingAccountId == null) {
// First find a Billing Account with negative balance, and if found store credit to that
List<GenericValue> billingAccounts = new LinkedList<GenericValue>();
try {
billingAccounts = EntityQuery.use(delegator).from("BillingAccountRoleAndAddress")
.where("partyId", fromPartyId, "roleTypeId", "BILL_TO_CUSTOMER")
.filterByDate()
.orderBy("-fromDate")
.queryList();
} catch (GenericEntityException e) {
return ServiceUtil.returnError(e.getMessage());
}
if (UtilValidate.isNotEmpty(billingAccounts)) {
ListIterator<GenericValue> billingAccountItr = billingAccounts.listIterator();
while (billingAccountItr.hasNext() && billingAccountId == null) {
String thisBillingAccountId = billingAccountItr.next().getString("billingAccountId");
BigDecimal billingAccountBalance = ZERO;
try {
GenericValue billingAccount = EntityQuery.use(delegator).from("BillingAccount").where("billingAccountId", thisBillingAccountId).queryOne();
billingAccountBalance = OrderReadHelper.getBillingAccountBalance(billingAccount);
} catch (GenericEntityException e) {
return ServiceUtil.returnError(e.getMessage());
}
if (billingAccountBalance.signum() == -1) {
billingAccountId = thisBillingAccountId;
}
}
}
// if no billing account with negative balance is found, look for productStore.storeCreditAccountEnumId settings
if (billingAccountId == null) {
if (productStore != null && productStore.getString("storeCreditAccountEnumId") != null && "BILLING_ACCOUNT".equals(productStore.getString("storeCreditAccountEnumId"))) {
if (UtilValidate.isNotEmpty(billingAccounts)) {
billingAccountId = EntityUtil.getFirst(billingAccounts).getString("billingAccountId");
} else {
// create new BillingAccount w/ 0 balance
Map<String, Object> results = createBillingAccountFromReturn(returnHeader, returnItems, dctx, context);
if (ServiceUtil.isError(results)) {
Debug.logError("Error creating BillingAccount: " + results.get(ModelService.ERROR_MESSAGE), module);
return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,
"OrderErrorWithCreateBillingAccount", locale) + results.get(ModelService.ERROR_MESSAGE));
}
billingAccountId = (String) results.get("billingAccountId");
// double check; make sure we have a billingAccount
if (billingAccountId == null) {
Debug.logError("No available billing account, none was created", module);
return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,
"OrderNoAvailableBillingAccount", locale));
}
}
} else {
GenericValue finAccount = null;
try {
finAccount = EntityQuery.use(delegator).from("FinAccountAndRole")
.where("partyId", fromPartyId, "finAccountTypeId", "STORE_CREDIT_ACCT", "roleTypeId", "OWNER", "statusId", "FNACT_ACTIVE", "currencyUomId", returnHeader.getString("currencyUomId"))
.filterByDate()
.orderBy("-fromDate")
.queryFirst();
} catch (GenericEntityException e) {
return ServiceUtil.returnError(e.getMessage());
}
if (finAccount != null) {
finAccountId = finAccount.getString("finAccountId");
}
if (finAccountId == null) {
Map<String, Object> createAccountCtx = new HashMap<String, Object>();
createAccountCtx.put("ownerPartyId", fromPartyId);
createAccountCtx.put("finAccountTypeId", "STORE_CREDIT_ACCT");
createAccountCtx.put("productStoreId", productStore.getString("productStoreId"));
createAccountCtx.put("currencyUomId", returnHeader.getString("currencyUomId"));
createAccountCtx.put("finAccountName", "Store Credit Account for party ["+fromPartyId+"]");
createAccountCtx.put("userLogin", userLogin);
Map<String, Object> createAccountResult = null;
try {
createAccountResult = dispatcher.runSync("createFinAccountForStore", createAccountCtx);
} catch (GenericServiceException e) {
Debug.logError(e, "Problems running the createFinAccountForStore service", module);
return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,
"OrderProblemsCreatingFinAccountForStore", locale));
}
if (ServiceUtil.isError(createAccountResult)) {
return ServiceUtil.returnError(ServiceUtil.getErrorMessage(createAccountResult));
}
finAccountId = (String) createAccountResult.get("finAccountId");
// double check; make sure we have a FinAccount
if (finAccountId == null) {
Debug.logError("No available fin account, none was created", module);
return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,
"OrderNoAvailableFinAccount", locale));
}
Map<String, Object> finAccountRoleResult = null;
try {
finAccountRoleResult = dispatcher.runSync("createFinAccountRole", UtilMisc.toMap("finAccountId", finAccountId, "partyId", fromPartyId, "roleTypeId", "OWNER", "userLogin", userLogin));
} catch (GenericServiceException e) {
Debug.logError(e, "Problem running the createFinAccountRole service", module);
return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,
"OrderProblemCreatingFinAccountRoleRecord", locale));
}
if (ServiceUtil.isError(finAccountRoleResult)) {
return ServiceUtil.returnError(ServiceUtil.getErrorMessage(finAccountRoleResult));
}
}
}
}
}
// now; to be used for all timestamps
Timestamp now = UtilDateTime.nowTimestamp();
// first, compute the total credit from the return items
BigDecimal creditTotal = ZERO;
for (GenericValue item : returnItems) {
BigDecimal quantity = item.getBigDecimal("returnQuantity");
BigDecimal price = item.getBigDecimal("returnPrice");
if (quantity == null) quantity = ZERO;
if (price == null) price = ZERO;
creditTotal = creditTotal.add(price.multiply(quantity).setScale(decimals, rounding));
}
// add the adjustments to the total
creditTotal = creditTotal.add(adjustments.setScale(decimals, rounding));
// create finAccountRole and finAccountTrans
String finAccountTransId = null;
if (finAccountId != null) {
Map<String, Object> finAccountTransResult = null;
try {
finAccountTransResult = dispatcher.runSync("createFinAccountTrans", UtilMisc.toMap("finAccountId", finAccountId, "finAccountTransTypeId", "DEPOSIT", "partyId", toPartyId, "amount", creditTotal, "reasonEnumId", "FATR_REFUND", "userLogin", userLogin));
} catch (GenericServiceException e) {
Debug.logError(e, "Problem creating FinAccountTrans record", module);
return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,
"OrderProblemCreatingFinAccountTransRecord", locale));
}
if (ServiceUtil.isError(finAccountTransResult)) {
return ServiceUtil.returnError(ServiceUtil.getErrorMessage(finAccountTransResult));
}
finAccountTransId = (String) finAccountTransResult.get("finAccountTransId");
}
// create a Payment record for this credit; will look just like a normal payment
// However, since this payment is not a DISBURSEMENT or RECEIPT but really a matter of internal record
// it is of type "Other (Non-posting)"
String paymentId = delegator.getNextSeqId("Payment");
GenericValue payment = delegator.makeValue("Payment", UtilMisc.toMap("paymentId", paymentId));
payment.set("paymentTypeId", "CUSTOMER_REFUND");
payment.set("partyIdFrom", toPartyId); // if you receive a return FROM someone, then you'd have to give a return TO that person
payment.set("partyIdTo", fromPartyId);
payment.set("effectiveDate", now);
payment.set("amount", creditTotal);
payment.set("comments", "Return Credit");
payment.set("statusId", "PMNT_CONFIRMED"); // set the status to confirmed so nothing else can happen to the payment
if (billingAccountId != null) {
payment.set("paymentMethodTypeId", "EXT_BILLACT");
} else {
payment.set("paymentMethodTypeId", "FIN_ACCOUNT");
}
try {
delegator.create(payment);
} catch (GenericEntityException e) {
Debug.logError(e, "Problem creating Payment record", module);
return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,
"OrderProblemCreatingPaymentRecord", locale));
}
// create a return item response
Map<String, Object> itemResponse = UtilMisc.<String, Object>toMap("paymentId", paymentId);
itemResponse.put("responseAmount", creditTotal);
itemResponse.put("responseDate", now);
itemResponse.put("userLogin", userLogin);
if (billingAccountId != null) {
itemResponse.put("billingAccountId", billingAccountId);
} else {
itemResponse.put("finAccountTransId", finAccountTransId);
}
Map<String, Object> serviceResults = null;
try {
serviceResults = dispatcher.runSync("createReturnItemResponse", itemResponse);
if (ServiceUtil.isError(serviceResults)) {
return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,
"OrderProblemCreatingReturnItemResponseRecord", locale), null, null, serviceResults);
}
} catch (GenericServiceException e) {
Debug.logError(e, "Problem creating ReturnItemResponse record", module);
return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,
"OrderProblemCreatingReturnItemResponseRecord", locale));
}
// the resulting response ID will be associated with the return items
String itemResponseId = (String) serviceResults.get("returnItemResponseId");
// loop through the items again to update them and store a status change history
for (GenericValue item : returnItems) {
Map<String, Object> returnItemMap = UtilMisc.<String, Object>toMap("returnItemResponseId", itemResponseId, "returnId", item.get("returnId"), "returnItemSeqId", item.get("returnItemSeqId"), "statusId", "RETURN_COMPLETED", "userLogin", userLogin);
// store the item changes (attached responseId)
try {
serviceResults = dispatcher.runSync("updateReturnItem", returnItemMap);
if (ServiceUtil.isError(serviceResults)) {
return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,
"OrderProblemStoringReturnItemUpdates", locale), null, null, serviceResults);
}
} catch (GenericServiceException e) {
Debug.logError(e, "Problem storing ReturnItem updates", module);
return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,
"OrderProblemStoringReturnItemUpdates", locale));
}
}
if (billingAccountId != null) {
// create the PaymentApplication for the billing account
String paId = delegator.getNextSeqId("PaymentApplication");
GenericValue pa = delegator.makeValue("PaymentApplication", UtilMisc.toMap("paymentApplicationId", paId));
pa.set("paymentId", paymentId);
pa.set("billingAccountId", billingAccountId);
pa.set("amountApplied", creditTotal);
try {
delegator.create(pa);
} catch (GenericEntityException e) {
Debug.logError(e, "Problem creating PaymentApplication record for billing account", module);
return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,
"OrderProblemCreatingPaymentApplicationRecord", locale));
}
// create the payment applications for the return invoice in case of billing account
try {
serviceResults = dispatcher.runSync("createPaymentApplicationsFromReturnItemResponse",
UtilMisc.<String, Object>toMap("returnItemResponseId", itemResponseId, "userLogin", userLogin));
if (ServiceUtil.isError(serviceResults)) {
return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,
"OrderProblemCreatingPaymentApplicationRecord", locale), null, null, serviceResults);
}
} catch (GenericServiceException e) {
Debug.logError(e, "Problem creating PaymentApplication records for return invoice", module);
return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,
"OrderProblemCreatingPaymentApplicationRecord", locale));
}
}
}
return ServiceUtil.returnSuccess();
}
/**
* Helper method to generate a BillingAccount (store credit) from a return
* header. This method takes care of all business logic relating to
* the initialization of a Billing Account from the Return data.
*
* The BillingAccount.thruDate will be set to (now +
* ProductStore.storeCreditValidDays + end of day). The product stores
* are obtained via the return orders, and the minimum storeCreditValidDays
* will be used. The default is to set thruDate to null, which implies no
* expiration.
*
* Note that we set BillingAccount.accountLimit to 0.0 for store credits.
* This is because the available balance of BillingAccounts is
* calculated as accountLimit + sum of Payments - sum of Invoices.
*/
private static Map<String, Object> createBillingAccountFromReturn(GenericValue returnHeader, List<GenericValue> returnItems, DispatchContext dctx, Map<String, ? extends Object> context) {
LocalDispatcher dispatcher = dctx.getDispatcher();
GenericValue userLogin = (GenericValue) context.get("userLogin");
Locale locale = (Locale) context.get("locale");
try {
// get the related product stores via the orders related to this return
List<GenericValue> orders = EntityUtil.getRelated("OrderHeader", null, returnItems, false);
List<GenericValue> productStores = EntityUtil.getRelated("ProductStore", null, orders, false);
// find the minimum storeCreditValidDays of all the ProductStores associated with all the Orders on the Return, skipping null ones
Long storeCreditValidDays = null;
for (GenericValue productStore : productStores) {
Long thisStoreValidDays = productStore.getLong("storeCreditValidDays");
if (thisStoreValidDays == null) continue;
if (storeCreditValidDays == null) {
storeCreditValidDays = thisStoreValidDays;
} else if (thisStoreValidDays.compareTo(storeCreditValidDays) < 0) {
// if this store's days < store credit valid days, use this store's days
storeCreditValidDays = thisStoreValidDays;
}
}
// if there is a storeCreditValidDays, set the thruDate to (nowTimestamp + storeCreditValidDays + end of day)
Timestamp thruDate = null;
if (storeCreditValidDays != null) thruDate = UtilDateTime.getDayEnd(UtilDateTime.nowTimestamp(), storeCreditValidDays);
// create the billing account
Map<String, Object> input = UtilMisc.<String, Object>toMap("accountLimit", BigDecimal.ZERO, "description", "Credit Account for Return #" + returnHeader.get("returnId"), "userLogin", userLogin);
input.put("accountCurrencyUomId", returnHeader.get("currencyUomId"));
input.put("thruDate", thruDate);
Map<String, Object> results = dispatcher.runSync("createBillingAccount", input);
if (ServiceUtil.isError(results)) return results;
String billingAccountId = (String) results.get("billingAccountId");
// set the role on the account
input = UtilMisc.toMap("billingAccountId", billingAccountId, "partyId", returnHeader.get("fromPartyId"), "roleTypeId", "BILL_TO_CUSTOMER", "userLogin", userLogin);
Map<String, Object> roleResults = dispatcher.runSync("createBillingAccountRole", input);
if (ServiceUtil.isError(roleResults)) {
Debug.logError("Error with createBillingAccountRole: " + roleResults.get(ModelService.ERROR_MESSAGE), module);
return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,
"OrderErrorWithCreateBillingAccountRole", locale) + roleResults.get(ModelService.ERROR_MESSAGE));
}
return results;
} catch (GenericEntityException e) {
Debug.logError(e, "Entity error when creating BillingAccount: " + e.getMessage(), module);
return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,
"OrderProblemsCreatingBillingAccount", locale));
} catch (GenericServiceException e) {
Debug.logError(e, "Service error when creating BillingAccount: " + e.getMessage(), module);
return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,
"OrderProblemsCreatingBillingAccount", locale));
}
}
public static Map<String, Object> processRefundReturnForReplacement(DispatchContext dctx, Map<String, ? extends Object> context) {
Delegator delegator = dctx.getDelegator();
LocalDispatcher dispatcher = dctx.getDispatcher();
Locale locale = (Locale) context.get("locale");
GenericValue userLogin = (GenericValue) context.get("userLogin");
String orderId = (String) context.get("orderId");
Map<String, Object> serviceResult = new HashMap<String, Object>();
GenericValue orderHeader = null;
List<GenericValue> orderPayPrefs = new LinkedList<GenericValue>();
try {
orderHeader = EntityQuery.use(delegator).from("OrderHeader").where("orderId", orderId).queryOne();
orderPayPrefs = orderHeader.getRelated("OrderPaymentPreference", null, UtilMisc.toList("-maxAmount"), false);
} catch (GenericEntityException e) {
Debug.logError("Problem looking up order information for orderId #" + orderId, module);
return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,
"OrderCannotGetOrderHeader", locale));
}
// Check for replacement order
if (UtilValidate.isEmpty(orderPayPrefs)) {
List<GenericValue> returnItemResponses = new LinkedList<GenericValue>();
try {
returnItemResponses = orderHeader.getRelated("ReplacementReturnItemResponse", null, null, false);
} catch (GenericEntityException e) {
Debug.logError("Problem getting ReturnItemResponses", module);
return ServiceUtil.returnError(e.getMessage());
}
for (GenericValue returnItemResponse : returnItemResponses) {
GenericValue returnItem = null;
GenericValue returnHeader = null;
try {
returnItem = EntityUtil.getFirst(returnItemResponse.getRelated("ReturnItem", null, null, false));
returnHeader = returnItem.getRelatedOne("ReturnHeader", false);
} catch (GenericEntityException e) {
Debug.logError("Problem getting ReturnItem", module);
return ServiceUtil.returnError(e.getMessage());
}
if ("RETURN_RECEIVED".equals(returnHeader.getString("statusId"))) {
String returnId = returnItem.getString("returnId");
String returnTypeId = returnItem.getString("returnTypeId");
try {
serviceResult = dispatcher.runSync("processRefundReturn", UtilMisc.toMap("returnId", returnId, "returnTypeId", returnTypeId, "userLogin", userLogin));
} catch (GenericServiceException e) {
Debug.logError(e, "Problem running the processRefundReturn service", module);
return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,
"OrderProblemsWithTheRefundSeeLogs", locale));
}
if (ServiceUtil.isError(serviceResult)) {
return ServiceUtil.returnError(ServiceUtil.getErrorMessage(serviceResult));
}
}
}
}
return serviceResult;
}
// refund (cash/charge) return
public static Map<String, Object> processRefundReturn(DispatchContext dctx, Map<String, ? extends Object> context) {
Delegator delegator = dctx.getDelegator();
LocalDispatcher dispatcher = dctx.getDispatcher();
String returnId = (String) context.get("returnId");
String returnTypeId = (String) context.get("returnTypeId");
GenericValue userLogin = (GenericValue) context.get("userLogin");
Locale locale = (Locale) context.get("locale");
GenericValue returnHeader = null;
List<GenericValue> returnItems = null;
try {
returnHeader = EntityQuery.use(delegator).from("ReturnHeader").where("returnId", returnId).queryOne();
if (returnHeader != null) {
returnItems = returnHeader.getRelated("ReturnItem", UtilMisc.toMap("returnTypeId", returnTypeId), null, false);
}
} catch (GenericEntityException e) {
Debug.logError(e, "Problems looking up return information", module);
return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,
"OrderErrorGettingReturnHeaderItemInformation", locale));
}
BigDecimal adjustments = getReturnAdjustmentTotal(delegator, UtilMisc.toMap("returnId", returnId, "returnTypeId", returnTypeId));
if (returnHeader != null && (UtilValidate.isNotEmpty(returnItems) || adjustments.compareTo(ZERO) > 0)) {
Map<String, List<GenericValue>> itemsByOrder = new HashMap<String, List<GenericValue>>();
Map<String, BigDecimal> totalByOrder = new HashMap<String, BigDecimal>();
// make sure total refunds on a return don't exceed amount of returned orders
Map<String, Object> serviceResult = null;
try {
serviceResult = dispatcher.runSync("checkPaymentAmountForRefund", UtilMisc.toMap("returnId", returnId));
} catch (GenericServiceException e) {
Debug.logError(e, "Problem running the checkPaymentAmountForRefund service", module);
return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,
"OrderProblemsWithCheckPaymentAmountForRefund", locale));
}
if (ServiceUtil.isError(serviceResult)) {
return ServiceUtil.returnError(ServiceUtil.getErrorMessage(serviceResult));
}
groupReturnItemsByOrder(returnItems, itemsByOrder, totalByOrder, delegator, returnId, returnTypeId);
// process each one by order
for (Map.Entry<String, List<GenericValue>> entry : itemsByOrder.entrySet()) {
String orderId = entry.getKey();
List<GenericValue> items = entry.getValue();
BigDecimal orderTotal = totalByOrder.get(orderId);
// get order header & payment prefs
GenericValue orderHeader = null;
List<GenericValue> orderPayPrefs = null;
try {
orderHeader = EntityQuery.use(delegator).from("OrderHeader").where("orderId", orderId).queryOne();
// sort these desending by maxAmount
orderPayPrefs = orderHeader.getRelated("OrderPaymentPreference", null, UtilMisc.toList("-maxAmount"), false);
List<EntityExpr> exprs = UtilMisc.toList(EntityCondition.makeCondition("statusId", EntityOperator.EQUALS, "PAYMENT_SETTLED"), EntityCondition.makeCondition("statusId", EntityOperator.EQUALS, "PAYMENT_RECEIVED"));
orderPayPrefs = EntityUtil.filterByOr(orderPayPrefs, exprs);
// Check for replacement order
if (UtilValidate.isEmpty(orderPayPrefs)) {
GenericValue orderItemAssoc = EntityQuery.use(delegator).from("OrderItemAssoc")
.where("toOrderId", orderId, "orderItemAssocTypeId", "REPLACEMENT")
.queryFirst();
if (orderItemAssoc != null) {
String originalOrderId = orderItemAssoc.getString("orderId");
orderHeader = EntityQuery.use(delegator).from("OrderHeader").where("orderId", originalOrderId).queryOne();
orderPayPrefs = orderHeader.getRelated("OrderPaymentPreference", null, UtilMisc.toList("-maxAmount"), false);
orderPayPrefs = EntityUtil.filterByOr(orderPayPrefs, exprs);
orderId = originalOrderId;
}
}
} catch (GenericEntityException e) {
Debug.logError(e, "Cannot get Order details for #" + orderId, module);
continue;
}
OrderReadHelper orderReadHelper = new OrderReadHelper(delegator, orderId);
// Determine the fall-through refund paymentMethodId from the PartyAcctgPreference of the owner of the productStore for the order
GenericValue productStore = orderReadHelper.getProductStore();
if (UtilValidate.isEmpty(productStore) || UtilValidate.isEmpty(productStore.get("payToPartyId"))) {
Debug.logError("No payToPartyId found for orderId " + orderId, module);
} else {
GenericValue orgAcctgPref = null;
Map<String, Object> acctgPreferencesResult = null;
try {
acctgPreferencesResult = dispatcher.runSync("getPartyAccountingPreferences", UtilMisc.toMap("organizationPartyId", productStore.get("payToPartyId"), "userLogin", userLogin));
} catch (GenericServiceException e) {
Debug.logError(e, "Error retrieving PartyAcctgPreference for partyId " + productStore.get("payToPartyId"), module);
return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,
"OrderProblemsWithGetPartyAcctgPreferences", locale));
}
orgAcctgPref = (GenericValue) acctgPreferencesResult.get("partyAccountingPreference");
if (orgAcctgPref != null) {
try {
orgAcctgPref.getRelatedOne("PaymentMethod", false);
} catch (GenericEntityException e) {
Debug.logError("Error retrieving related refundPaymentMethod from PartyAcctgPreference for partyId " + productStore.get("payToPartyId"), module);
}
}
}
// now; for all timestamps
Timestamp now = UtilDateTime.nowTimestamp();
// Assemble a map of orderPaymentPreferenceId -> list of maps of (OPP and availableAmountForRefunding)
// where availableAmountForRefunding = receivedAmount - alreadyRefundedAmount
// We break the OPPs down this way because we need to process the refunds to payment methods in a particular order
Map<String, BigDecimal> receivedPaymentTotalsByPaymentMethod = orderReadHelper.getReceivedPaymentTotalsByPaymentMethod() ;
Map<String, BigDecimal> refundedTotalsByPaymentMethod = orderReadHelper.getReturnedTotalsByPaymentMethod() ;
// getOrderPaymentPreferenceTotalByType has been called because getReceivedPaymentTotalsByPaymentMethod does not
// return payments captured from Billing Account.This is because when payment is captured from Billing Account
// then no entry is maintained in Payment entity.
BigDecimal receivedPaymentTotalsByBillingAccount = orderReadHelper.getOrderPaymentPreferenceTotalByType("EXT_BILLACT");
/*
* Go through the OrderPaymentPreferences and determine how much remains to be refunded for each.
* Then group these refund amounts and orderPaymentPreferences by paymentMethodTypeId. That is,
* the intent is to get the refundable amounts per orderPaymentPreference, grouped by payment method type.
*/
Map<String, List<Map<String, Object>>> prefSplitMap = new HashMap<String, List<Map<String,Object>>>();
for (GenericValue orderPayPref : orderPayPrefs) {
String paymentMethodTypeId = orderPayPref.getString("paymentMethodTypeId");
String orderPayPrefKey = orderPayPref.getString("paymentMethodId") != null ? orderPayPref.getString("paymentMethodId") : orderPayPref.getString("paymentMethodTypeId");
// See how much we can refund to the payment method
BigDecimal orderPayPrefReceivedTotal = ZERO;
if (receivedPaymentTotalsByPaymentMethod.containsKey(orderPayPrefKey)) {
orderPayPrefReceivedTotal = orderPayPrefReceivedTotal.add(receivedPaymentTotalsByPaymentMethod.get(orderPayPrefKey)).setScale(decimals, rounding);
}
if (receivedPaymentTotalsByBillingAccount != null) {
orderPayPrefReceivedTotal = orderPayPrefReceivedTotal.add(receivedPaymentTotalsByBillingAccount);
}
BigDecimal orderPayPrefRefundedTotal = ZERO;
if (refundedTotalsByPaymentMethod.containsKey(orderPayPrefKey)) {
orderPayPrefRefundedTotal = orderPayPrefRefundedTotal.add(refundedTotalsByPaymentMethod.get(orderPayPrefKey)).setScale(decimals, rounding);
}
BigDecimal orderPayPrefAvailableTotal = orderPayPrefReceivedTotal.subtract(orderPayPrefRefundedTotal);
// add the refundable amount and orderPaymentPreference to the paymentMethodTypeId map
if (orderPayPrefAvailableTotal.compareTo(ZERO) > 0) {
Map<String, Object> orderPayPrefDetails = new HashMap<String, Object>();
orderPayPrefDetails.put("orderPaymentPreference", orderPayPref);
orderPayPrefDetails.put("availableTotal", orderPayPrefAvailableTotal);
if (prefSplitMap.containsKey(paymentMethodTypeId)) {
(prefSplitMap.get(paymentMethodTypeId)).add(orderPayPrefDetails);
} else {
prefSplitMap.put(paymentMethodTypeId, UtilMisc.toList(orderPayPrefDetails));
}
}
}
// Keep a decreasing total of the amount remaining to refund
BigDecimal amountLeftToRefund = orderTotal.setScale(decimals, rounding);
// This can be extended to support additional electronic types
List<String> electronicTypes = UtilMisc.<String>toList("CREDIT_CARD", "EFT_ACCOUNT", "FIN_ACCOUNT", "GIFT_CARD");
// Figure out if EXT_PAYPAL should be considered as an electronic type
if (productStore != null) {
ExpressCheckoutEvents.CheckoutType payPalType = ExpressCheckoutEvents.determineCheckoutType(delegator, productStore.getString("productStoreId"));
if (!payPalType.equals(ExpressCheckoutEvents.CheckoutType.NONE)) {
electronicTypes.add("EXT_PAYPAL");
}
}
// This defines the ordered part of the sequence of refund processing
List<String> orderedRefundPaymentMethodTypes = new LinkedList<String>();
orderedRefundPaymentMethodTypes.add("EXT_BILLACT");
orderedRefundPaymentMethodTypes.add("FIN_ACCOUNT");
orderedRefundPaymentMethodTypes.add("GIFT_CARD");
orderedRefundPaymentMethodTypes.add("CREDIT_CARD");
orderedRefundPaymentMethodTypes.add("EFT_ACCOUNT");
// Add all the other paymentMethodTypes, in no particular order
List<GenericValue> otherPaymentMethodTypes = new LinkedList<GenericValue>();
try {
otherPaymentMethodTypes = EntityQuery.use(delegator).from("PaymentMethodType")
.where(EntityCondition.makeCondition("paymentMethodTypeId", EntityOperator.NOT_IN, orderedRefundPaymentMethodTypes))
.cache(true).queryList();
} catch (GenericEntityException e) {
Debug.logError(e, "Cannot get PaymentMethodTypes", module);
return ServiceUtil.returnError(UtilProperties.getMessage(resource,
"OrderOrderPaymentPreferencesCannotGetPaymentMethodTypes",
UtilMisc.toMap("errorString", e.toString()), locale));
}
List<String> fieldList = EntityUtil.getFieldListFromEntityList(otherPaymentMethodTypes, "paymentMethodTypeId", true);
orderedRefundPaymentMethodTypes.addAll(fieldList);
// Iterate through the specified sequence of paymentMethodTypes, refunding to the correct OrderPaymentPreferences
// as long as there's a positive amount remaining to refund
Iterator<String> orpmtit = orderedRefundPaymentMethodTypes.iterator();
while (orpmtit.hasNext() && amountLeftToRefund.compareTo(ZERO) == 1) {
String paymentMethodTypeId = orpmtit.next();
if (prefSplitMap.containsKey(paymentMethodTypeId)) {
List<Map<String, Object>> paymentMethodDetails = prefSplitMap.get(paymentMethodTypeId);
// Iterate through the OrderPaymentPreferences of this type
Iterator<Map<String, Object>> pmtppit = paymentMethodDetails.iterator();
while (pmtppit.hasNext() && amountLeftToRefund.compareTo(ZERO) == 1) {
Map<String, Object> orderPaymentPrefDetails = pmtppit.next();
GenericValue orderPaymentPreference = (GenericValue) orderPaymentPrefDetails.get("orderPaymentPreference");
BigDecimal orderPaymentPreferenceAvailable = (BigDecimal) orderPaymentPrefDetails.get("availableTotal");
GenericValue refundOrderPaymentPreference=null;
// Refund up to the maxAmount for the paymentPref, or whatever is left to refund if that's less than the maxAmount
BigDecimal amountToRefund = orderPaymentPreferenceAvailable.min(amountLeftToRefund);
// The amount actually refunded for the paymentPref, default to requested amount
BigDecimal amountRefunded = amountToRefund;
String paymentId = null;
String returnItemStatusId = "RETURN_COMPLETED"; // generally, the return item will be considered complete after this
// Call the refund service to refund the payment
if (electronicTypes.contains(paymentMethodTypeId)) {
try {
Map<String, Object> serviceContext = UtilMisc.toMap("orderId", orderId,"userLogin", context.get("userLogin"));
serviceContext.put("paymentMethodId", orderPaymentPreference.getString("paymentMethodId"));
serviceContext.put("paymentMethodTypeId", orderPaymentPreference.getString("paymentMethodTypeId"));
serviceContext.put("statusId", orderPaymentPreference.getString("statusId"));
serviceContext.put("maxAmount", amountToRefund.setScale(decimals, rounding));
String orderPaymentPreferenceNewId = null;
Map<String, Object> result = dispatcher.runSync("createOrderPaymentPreference", serviceContext);
orderPaymentPreferenceNewId = (String) result.get("orderPaymentPreferenceId");
try {
refundOrderPaymentPreference = EntityQuery.use(delegator).from("OrderPaymentPreference").where("orderPaymentPreferenceId", orderPaymentPreferenceNewId).queryOne();
} catch (GenericEntityException e) {
return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,"OrderProblemsWithTheRefundSeeLogs", locale));
}
serviceResult = dispatcher.runSync("refundPayment", UtilMisc.<String, Object>toMap("orderPaymentPreference", refundOrderPaymentPreference, "refundAmount", amountToRefund.setScale(decimals, rounding), "userLogin", userLogin));
if (ServiceUtil.isError(serviceResult) || ServiceUtil.isFailure(serviceResult)) {
Debug.logError("Error in refund payment: " + ServiceUtil.getErrorMessage(serviceResult), module);
continue;
}
// for electronic types such as CREDIT_CARD and EFT_ACCOUNT, use refundPayment service
paymentId = (String) serviceResult.get("paymentId");
amountRefunded = (BigDecimal) serviceResult.get("refundAmount");
} catch (GenericServiceException e) {
return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,"OrderProblemsWithTheRefundSeeLogs", locale));
}
} else if (paymentMethodTypeId.equals("EXT_BILLACT")) {
try {
// for Billing Account refunds
serviceResult = dispatcher.runSync("refundBillingAccountPayment",
UtilMisc.<String, Object> toMap("orderPaymentPreference", orderPaymentPreference, "refundAmount",
amountToRefund.setScale(decimals, rounding), "userLogin", userLogin));
if (ServiceUtil.isError(serviceResult) || ServiceUtil.isFailure(serviceResult)) {
Debug.logError("Error in refund payment: " + ServiceUtil.getErrorMessage(serviceResult), module);
continue;
}
paymentId = (String) serviceResult.get("paymentId");
} catch (GenericServiceException e) {
Debug.logError(e, "Problem running the refundPayment service", module);
return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,
"OrderProblemsWithTheRefundSeeLogs", locale));
}
} else {
// handle manual refunds
try {
Map<String, Object> input = UtilMisc.<String, Object>toMap("userLogin", userLogin, "amount", amountLeftToRefund, "statusId", "PMNT_NOT_PAID");
input.put("partyIdTo", returnHeader.get("fromPartyId"));
input.put("partyIdFrom", returnHeader.get("toPartyId"));
input.put("paymentTypeId", "CUSTOMER_REFUND");
input.put("paymentMethodId", orderPaymentPreference.get("paymentMethodId"));
input.put("paymentMethodTypeId", orderPaymentPreference.get("paymentMethodTypeId"));
input.put("paymentPreferenceId", orderPaymentPreference.get("orderPaymentPreferenceId"));
serviceResult = dispatcher.runSync("createPayment", input);
if (ServiceUtil.isError(serviceResult) || ServiceUtil.isFailure(serviceResult)) {
Debug.logError("Error in refund payment: " + ServiceUtil.getErrorMessage(serviceResult), module);
continue;
}
paymentId = (String) serviceResult.get("paymentId");
returnItemStatusId = "RETURN_MAN_REFUND"; // however, in this case we should flag it as a manual refund
} catch (GenericServiceException e) {
return ServiceUtil.returnError(e.getMessage());
}
}
// Fill out the data for the new ReturnItemResponse
Map<String, Object> response = new HashMap<String, Object>();
if (refundOrderPaymentPreference != null) {
response.put("orderPaymentPreferenceId", refundOrderPaymentPreference.getString("orderPaymentPreferenceId"));
} else {
response.put("orderPaymentPreferenceId", orderPaymentPreference.getString("orderPaymentPreferenceId"));
}
response.put("responseAmount", amountRefunded.setScale(decimals, rounding));
response.put("responseDate", now);
response.put("userLogin", userLogin);
response.put("paymentId", paymentId);
if (paymentMethodTypeId.equals("EXT_BILLACT")) {
response.put("billingAccountId", orderReadHelper.getBillingAccount().getString("billingAccountId"));
}
Map<String, Object> serviceResults = null;
try {
serviceResults = dispatcher.runSync("createReturnItemResponse", response);
if (ServiceUtil.isError(serviceResults)) {
return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,
"OrderProblemsCreatingReturnItemResponseEntity", locale), null, null, serviceResults);
}
} catch (GenericServiceException e) {
Debug.logError(e, "Problems creating new ReturnItemResponse entity", module);
return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,
"OrderProblemsCreatingReturnItemResponseEntity", locale));
}
String responseId = (String) serviceResults.get("returnItemResponseId");
// Set the response on each item
for (GenericValue item : items) {
Map<String, Object> returnItemMap = UtilMisc.<String, Object>toMap("returnItemResponseId", responseId, "returnId", item.get("returnId"), "returnItemSeqId", item.get("returnItemSeqId"), "statusId", returnItemStatusId, "userLogin", userLogin);
try {
serviceResults = dispatcher.runSync("updateReturnItem", returnItemMap);
if (ServiceUtil.isError(serviceResults)) {
return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,
"OrderProblemUpdatingReturnItemReturnItemResponseId", locale), null, null, serviceResults);
}
} catch (GenericServiceException e) {
Debug.logError("Problem updating the ReturnItem entity", module);
return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,
"OrderProblemUpdatingReturnItemReturnItemResponseId", locale));
}
}
// Create the payment applications for the return invoice
try {
serviceResults = dispatcher.runSync("createPaymentApplicationsFromReturnItemResponse",
UtilMisc.<String, Object>toMap("returnItemResponseId", responseId, "userLogin", userLogin));
if (ServiceUtil.isError(serviceResults)) {
return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,
"OrderProblemUpdatingReturnItemReturnItemResponseId", locale), null, null, serviceResults);
}
} catch (GenericServiceException e) {
Debug.logError(e, "Problem creating PaymentApplication records for return invoice", module);
return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,
"OrderProblemUpdatingReturnItemReturnItemResponseId", locale));
}
// Update the amount necessary to refund
amountLeftToRefund = amountLeftToRefund.subtract(amountRefunded);
}
}
}
}
}
return ServiceUtil.returnSuccess();
}
public static Map<String, Object> refundBillingAccountPayment(DispatchContext dctx, Map<String, ? extends Object> context) {
Delegator delegator = dctx.getDelegator();
LocalDispatcher dispatcher = dctx.getDispatcher();
GenericValue userLogin = (GenericValue) context.get("userLogin");
GenericValue paymentPref = (GenericValue) context.get("orderPaymentPreference");
BigDecimal refundAmount = (BigDecimal) context.get("refundAmount");
Locale locale = (Locale) context.get("locale");
GenericValue orderHeader = null;
try {
orderHeader = paymentPref.getRelatedOne("OrderHeader", false);
} catch (GenericEntityException e) {
Debug.logError(e, "Cannot get OrderHeader from OrderPaymentPreference", module);
return ServiceUtil.returnError(UtilProperties.getMessage(resource,
"OrderOrderPaymentCannotBeCreatedWithRelatedOrderHeader", locale) + e.toString());
}
OrderReadHelper orh = new OrderReadHelper(orderHeader);
String payFromPartyId = orh.getBillFromParty().getString("partyId");
String payToPartyId = orh.getBillToParty().getString("partyId");
// Create the PaymentGatewayResponse record
String responseId = delegator.getNextSeqId("PaymentGatewayResponse");
GenericValue response = delegator.makeValue("PaymentGatewayResponse");
response.set("paymentGatewayResponseId", responseId);
response.set("paymentServiceTypeEnumId", "PRDS_PAY_REFUND");
response.set("orderPaymentPreferenceId", paymentPref.get("orderPaymentPreferenceId"));
response.set("paymentMethodTypeId", paymentPref.get("paymentMethodTypeId"));
response.set("transCodeEnumId", "PGT_REFUND");
response.set("amount", refundAmount);
response.set("transactionDate", UtilDateTime.nowTimestamp());
response.set("currencyUomId", orh.getCurrency());
try {
delegator.create(response);
} catch (GenericEntityException e) {
Debug.logError(e, module);
return ServiceUtil.returnError(UtilProperties.getMessage(resource,
"OrderOrderPaymentGatewayResponseCannotBeCreated", locale));
}
// Create the Payment record (parties reversed)
Map<String, Object> paymentCtx = UtilMisc.<String, Object>toMap("paymentTypeId", "CUSTOMER_REFUND");
paymentCtx.put("paymentMethodTypeId", paymentPref.get("paymentMethodTypeId"));
paymentCtx.put("paymentGatewayResponseId", responseId);
paymentCtx.put("partyIdTo", payToPartyId);
paymentCtx.put("partyIdFrom", payFromPartyId);
paymentCtx.put("statusId", "PMNT_CONFIRMED");
paymentCtx.put("paymentPreferenceId", paymentPref.get("orderPaymentPreferenceId"));
paymentCtx.put("currencyUomId", orh.getCurrency());
paymentCtx.put("amount", refundAmount);
paymentCtx.put("userLogin", userLogin);
paymentCtx.put("comments", "Refund");
String paymentId = null;
try {
Map<String, Object> paymentCreationResult = dispatcher.runSync("createPayment", paymentCtx);
if (ServiceUtil.isError(paymentCreationResult)) {
return paymentCreationResult;
} else {
paymentId = (String) paymentCreationResult.get("paymentId");
}
} catch (GenericServiceException e) {
return ServiceUtil.returnError(UtilProperties.getMessage(resource,
"OrderOrderPaymentFailed",
UtilMisc.toMap("errorString", e.getMessage()), locale));
}
if (paymentId == null) {
return ServiceUtil.returnError(UtilProperties.getMessage(resource,
"OrderOrderPaymentFailed", UtilMisc.toMap("errorString", ""), locale));
}
// if the original order was paid with a billing account, then go find the billing account from the order and associate this refund with that billing account
// thus returning value to the billing account
if ("EXT_BILLACT".equals(paymentPref.getString("paymentMethodTypeId"))) {
GenericValue billingAccount = orh.getBillingAccount();
if (UtilValidate.isNotEmpty(billingAccount.getString("billingAccountId"))) {
try {
Map<String, Object> paymentApplResult = dispatcher.runSync("createPaymentApplication", UtilMisc.<String, Object>toMap("paymentId", paymentId, "billingAccountId", billingAccount.getString("billingAccountId"),
"amountApplied", refundAmount, "userLogin", userLogin));
if (ServiceUtil.isError(paymentApplResult)) {
return paymentApplResult;
}
} catch (GenericServiceException e) {
return ServiceUtil.returnError(UtilProperties.getMessage(resource,
"OrderOrderPaymentApplicationFailed",
UtilMisc.toMap("errorString", e.getMessage()), locale));
}
}
}
Map<String, Object> result = ServiceUtil.returnSuccess();
result.put("paymentId", paymentId);
return result;
}
public static Map<String, Object> createPaymentApplicationsFromReturnItemResponse(DispatchContext dctx, Map<String, ? extends Object> context) {
LocalDispatcher dispatcher = dctx.getDispatcher();
Delegator delegator = dctx.getDelegator();
Locale locale = (Locale) context.get("locale");
GenericValue userLogin = (GenericValue) context.get("userLogin");
// the strategy for this service is to get a list of return invoices via the return items -> return item billing relationships
// then split up the responseAmount among the invoices evenly
String responseId = (String) context.get("returnItemResponseId");
String errorMsg = "Failed to create payment applications for return item response [" + responseId + "]. ";
try {
GenericValue response = EntityQuery.use(delegator).from("ReturnItemResponse").where("returnItemResponseId", responseId).queryOne();
if (response == null) {
return ServiceUtil.returnError(UtilProperties.getMessage(resource_error, "OrderReturnItemResponseNotFound", UtilMisc.toMap("errorMsg", errorMsg,"responseId", responseId), locale));
}
BigDecimal responseAmount = response.getBigDecimal("responseAmount").setScale(decimals, rounding);
String paymentId = response.getString("paymentId");
// for each return item in the response, get the list of return item billings and then a list of invoices
Map<String, GenericValue> returnInvoices = new HashMap<String, GenericValue>(); // key is invoiceId, value is Invoice GenericValue
List<GenericValue> items = response.getRelated("ReturnItem", null, null, false);
for (GenericValue item : items) {
List<GenericValue> billings = item.getRelated("ReturnItemBilling", null, null, false);
for (GenericValue billing : billings) {
GenericValue invoice = billing.getRelatedOne("Invoice", false);
// put the invoice in the map if it doesn't already exist (a very loopy way of doing group by invoiceId without creating a view)
if (returnInvoices.get(invoice.getString("invoiceId")) == null) {
returnInvoices.put(invoice.getString("invoiceId"), invoice);
}
}
}
// for each return invoice found, sum up the related billings
Map<String, BigDecimal> invoiceTotals = new HashMap<String, BigDecimal>(); // key is invoiceId, value is the sum of all billings for that invoice
BigDecimal grandTotal = ZERO; // The sum of all return invoice totals
for (GenericValue invoice : returnInvoices.values()) {
List<GenericValue> billings = invoice.getRelated("ReturnItemBilling", null, null, false);
BigDecimal runningTotal = ZERO;
for (GenericValue billing : billings) {
runningTotal = runningTotal.add(billing.getBigDecimal("amount").multiply(billing.getBigDecimal("quantity")).setScale(decimals, rounding));
}
invoiceTotals.put(invoice.getString("invoiceId"), runningTotal);
grandTotal = grandTotal.add(runningTotal);
}
// now allocate responseAmount * invoiceTotal / grandTotal to each invoice
for (GenericValue invoice : returnInvoices.values()) {
String invoiceId = invoice.getString("invoiceId");
BigDecimal invoiceTotal = invoiceTotals.get(invoiceId);
BigDecimal amountApplied = responseAmount.multiply(invoiceTotal).divide(grandTotal, decimals, rounding).setScale(decimals, rounding);
if (paymentId != null) {
// create a payment application for the invoice
Map<String, Object> input = UtilMisc.<String, Object>toMap("paymentId", paymentId, "invoiceId", invoice.getString("invoiceId"));
input.put("amountApplied", amountApplied);
input.put("userLogin", userLogin);
if (response.get("billingAccountId") != null) {
GenericValue billingAccount = response.getRelatedOne("BillingAccount", false);
if (billingAccount != null) {
input.put("billingAccountId", response.get("billingAccountId"));
}
}
Map<String, Object> serviceResults = dispatcher.runSync("createPaymentApplication", input);
if (ServiceUtil.isError(serviceResults)) {
return ServiceUtil.returnError(errorMsg, null, null, serviceResults);
}
if (Debug.verboseOn()) { Debug.logInfo("Created PaymentApplication for response with amountApplied " + amountApplied.toString(), module); }
}
}
} catch (GenericServiceException e) {
Debug.logError(e, errorMsg + e.getMessage(), module);
return ServiceUtil.returnError(errorMsg + e.getMessage());
} catch (GenericEntityException e) {
Debug.logError(e, errorMsg + e.getMessage(), module);
return ServiceUtil.returnError(errorMsg + e.getMessage());
}
return ServiceUtil.returnSuccess();
}
// replacement return (create new order adjusted to be at no charge)
public static Map<String, Object> processReplacementReturn(DispatchContext dctx, Map<String, ? extends Object> context) {
LocalDispatcher dispatcher = dctx.getDispatcher();
Delegator delegator = dctx.getDelegator();
String returnId = (String) context.get("returnId");
String returnTypeId = (String) context.get("returnTypeId");
GenericValue userLogin = (GenericValue) context.get("userLogin");
Locale locale = (Locale) context.get("locale");
Timestamp nowTimestamp = UtilDateTime.nowTimestamp();
GenericValue returnHeader = null;
List<GenericValue> returnItems = null;
try {
returnHeader = EntityQuery.use(delegator).from("ReturnHeader").where("returnId", returnId).queryOne();
if (returnHeader != null) {
returnItems = returnHeader.getRelated("ReturnItem", UtilMisc.toMap("returnTypeId", returnTypeId), null, false);
}
} catch (GenericEntityException e) {
Debug.logError(e, "Problems looking up return information", module);
return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,
"OrderErrorGettingReturnHeaderItemInformation", locale));
}
String returnHeaderTypeId = returnHeader.getString("returnHeaderTypeId");
List<String> createdOrderIds = new LinkedList<String>();
if (returnHeader != null && UtilValidate.isNotEmpty(returnItems)) {
Map<String, List<GenericValue>> returnItemsByOrderId = new HashMap<String, List<GenericValue>>();
Map<String, BigDecimal> totalByOrder = new HashMap<String, BigDecimal>();
groupReturnItemsByOrder(returnItems, returnItemsByOrderId, totalByOrder, delegator, returnId, returnTypeId);
// process each one by order
for (Map.Entry<String, List<GenericValue>> entry : returnItemsByOrderId.entrySet()) {
String orderId = entry.getKey();
List<GenericValue> returnItemList = entry.getValue();
// get order header & payment prefs
GenericValue orderHeader = null;
try {
orderHeader = EntityQuery.use(delegator).from("OrderHeader").where("orderId", orderId).queryOne();
} catch (GenericEntityException e) {
Debug.logError(e, "Cannot get Order details for #" + orderId, module);
continue;
}
OrderReadHelper orh = new OrderReadHelper(orderHeader);
// create the replacement order
Map<String, Object> orderMap = UtilMisc.<String, Object>toMap("userLogin", userLogin);
String placingPartyId = null;
GenericValue placingParty = null;
if ("CUSTOMER_RETURN".equals(returnHeaderTypeId)) {
placingParty = orh.getPlacingParty();
if (placingParty != null) {
placingPartyId = placingParty.getString("partyId");
}
orderMap.put("orderTypeId", "SALES_ORDER");
} else {
placingParty = orh.getSupplierAgent();
if (placingParty != null) {
placingPartyId = placingParty.getString("partyId");
}
orderMap.put("orderTypeId", "PURCHASE_ORDER");
}
orderMap.put("partyId", placingPartyId);
orderMap.put("productStoreId", orderHeader.get("productStoreId"));
orderMap.put("webSiteId", orderHeader.get("webSiteId"));
orderMap.put("visitId", orderHeader.get("visitId"));
orderMap.put("currencyUom", orderHeader.get("currencyUom"));
orderMap.put("grandTotal", BigDecimal.ZERO);
// make the contact mechs
List<GenericValue> contactMechs = new LinkedList<GenericValue>();
List<GenericValue> orderCm = null;
try {
orderCm = orderHeader.getRelated("OrderContactMech", null, null, false);
} catch (GenericEntityException e) {
Debug.logError(e, module);
}
if (orderCm != null) {
for (GenericValue v : orderCm) {
contactMechs.add(GenericValue.create(v));
}
orderMap.put("orderContactMechs", contactMechs);
}
// make the order items
BigDecimal orderPriceTotal = BigDecimal.ZERO;
BigDecimal additionalItemTotal = BigDecimal.ZERO;
List<GenericValue> orderItems = new LinkedList<GenericValue>();
List<GenericValue> orderItemShipGroupInfo = new LinkedList<GenericValue>();
List<String> orderItemShipGroupIds = new LinkedList<String>(); // this is used to store the ship group ids of the groups already added to the orderItemShipGroupInfo list
List<GenericValue> orderItemAssocs = new LinkedList<GenericValue>();
if (returnItemList != null) {
int itemCount = 1;
for (GenericValue returnItem : returnItemList) {
GenericValue orderItem = null;
GenericValue product = null;
try {
orderItem = returnItem.getRelatedOne("OrderItem", false);
product = orderItem.getRelatedOne("Product", false);
} catch (GenericEntityException e) {
Debug.logError(e, module);
continue;
}
if (orderItem != null) {
BigDecimal quantity = returnItem.getBigDecimal("returnQuantity");
BigDecimal unitPrice = returnItem.getBigDecimal("returnPrice");
if (quantity != null && unitPrice != null) {
orderPriceTotal = orderPriceTotal.add(quantity.multiply(unitPrice));
// Check if the product being returned has a Refurbished Equivalent and if so
// (and there is inventory for the assoc product) use that product instead
GenericValue refurbItem = null;
if ("CUSTOMER_RETURN".equals(returnHeaderTypeId)) {
try {
if (product != null) {
GenericValue refurbItemAssoc = EntityUtil.getFirst(EntityUtil.filterByDate(product.getRelated("MainProductAssoc", UtilMisc.toMap("productAssocTypeId", "PRODUCT_REFURB"), UtilMisc.toList("sequenceNum"), false)));
if (refurbItemAssoc != null) {
refurbItem = refurbItemAssoc.getRelatedOne("AssocProduct", false);
}
}
} catch (GenericEntityException e) {
Debug.logError(e, module);
}
if (refurbItem != null) {
boolean inventoryAvailable = false;
try {
Map<String, Object> invReqResult = dispatcher.runSync("isStoreInventoryAvailable", UtilMisc.toMap("productStoreId", orderHeader.get("productStoreId"),
"productId", refurbItem.getString("productId"),
"product", refurbItem, "quantity", quantity));
if (ServiceUtil.isError(invReqResult)) {
Debug.logError("Error calling isStoreInventoryAvailable service, result is: " + invReqResult, module);
} else {
inventoryAvailable = "Y".equals(invReqResult.get("available"));
}
} catch (GenericServiceException e) {
Debug.logError(e, "Fatal error calling inventory checking services: " + e.toString(), module);
}
if (!inventoryAvailable) {
// If the Refurbished Equivalent is not available,
// then use the original product.
refurbItem = null;
}
}
}
GenericValue newItem = delegator.makeValue("OrderItem", UtilMisc.toMap("orderItemSeqId", UtilFormatOut.formatPaddedNumber(itemCount++, 5)));
if (UtilValidate.isEmpty(refurbItem)) {
newItem.set("productId", orderItem.get("productId"));
newItem.set("itemDescription", orderItem.get("itemDescription"));
} else {
newItem.set("productId", refurbItem.get("productId"));
newItem.set("itemDescription", ProductContentWrapper.getProductContentAsText(refurbItem, "PRODUCT_NAME", locale, null, "html"));
}
newItem.set("orderItemTypeId", orderItem.get("orderItemTypeId"));
newItem.set("productFeatureId", orderItem.get("productFeatureId"));
newItem.set("prodCatalogId", orderItem.get("prodCatalogId"));
newItem.set("productCategoryId", orderItem.get("productCategoryId"));
newItem.set("quantity", quantity);
newItem.set("unitPrice", unitPrice);
newItem.set("unitListPrice", orderItem.get("unitListPrice"));
newItem.set("comments", orderItem.get("comments"));
newItem.set("correspondingPoId", orderItem.get("correspondingPoId"));
newItem.set("statusId", "ITEM_CREATED");
orderItems.add(newItem);
// Set the order item ship group information
// TODO: only the first ship group associated to the item
// of the original order is considered and cloned,
// anIs there a better way to handle this?d the returned units are assigned to it.
//
GenericValue orderItemShipGroupAssoc = null;
try {
orderItemShipGroupAssoc = EntityUtil.getFirst(orderItem.getRelated("OrderItemShipGroupAssoc", null, null, false));
if (orderItemShipGroupAssoc != null) {
if (!orderItemShipGroupIds.contains(orderItemShipGroupAssoc.getString("shipGroupSeqId"))) {
GenericValue orderItemShipGroup = orderItemShipGroupAssoc.getRelatedOne("OrderItemShipGroup", false);
GenericValue newOrderItemShipGroup = (GenericValue)orderItemShipGroup.clone();
newOrderItemShipGroup.set("orderId", null);
orderItemShipGroupInfo.add(newOrderItemShipGroup);
orderItemShipGroupIds.add(orderItemShipGroupAssoc.getString("shipGroupSeqId"));
}
GenericValue newOrderItemShipGroupAssoc = delegator.makeValue("OrderItemShipGroupAssoc", UtilMisc.toMap("orderItemSeqId", newItem.getString("orderItemSeqId"), "shipGroupSeqId", orderItemShipGroupAssoc.getString("shipGroupSeqId"), "quantity", quantity));
orderItemShipGroupInfo.add(newOrderItemShipGroupAssoc);
}
} catch (GenericEntityException e) {
String errMsg = "Problem calling the approveRequirement service";
Debug.logError(e, errMsg, module);
return ServiceUtil.returnError(errMsg);
}
// Create an association between the replacement order item and the order item of the original order
GenericValue newOrderItemAssoc = delegator.makeValue("OrderItemAssoc", UtilMisc.toMap("orderId", orderHeader.getString("orderId"),
"orderItemSeqId", orderItem.getString("orderItemSeqId"), "shipGroupSeqId", "_NA_",
"toOrderItemSeqId", newItem.getString("orderItemSeqId"), "toShipGroupSeqId", "_NA_", "orderItemAssocTypeId", "REPLACEMENT"));
orderItemAssocs.add(newOrderItemAssoc);
// For repair replacement orders, add to the order also the repair items
if ("RTN_REPAIR_REPLACE".equals(returnTypeId)) {
List<GenericValue> repairItems = null;
try {
if (product != null) {
repairItems = EntityUtil.filterByDate(product.getRelated("MainProductAssoc", UtilMisc.toMap("productAssocTypeId", "PRODUCT_REPAIR_SRV"), UtilMisc.toList("sequenceNum"), false));
}
} catch (GenericEntityException e) {
Debug.logError(e, module);
continue;
}
if (UtilValidate.isNotEmpty(repairItems)) {
for (GenericValue repairItem : repairItems) {
GenericValue repairItemProduct = null;
try {
repairItemProduct = repairItem.getRelatedOne("AssocProduct", false);
} catch (GenericEntityException e) {
Debug.logError(e, module);
continue;
}
if (repairItemProduct != null) {
BigDecimal repairUnitQuantity = repairItem.getBigDecimal("quantity");
if (UtilValidate.isEmpty(repairUnitQuantity)) {
repairUnitQuantity = BigDecimal.ONE;
}
BigDecimal repairQuantity = quantity.multiply(repairUnitQuantity);
newItem = delegator.makeValue("OrderItem", UtilMisc.toMap("orderItemSeqId", UtilFormatOut.formatPaddedNumber(itemCount++, 5)));
// price
Map<String, Object> priceContext = new HashMap<String, Object>();
priceContext.put("currencyUomId", orderHeader.get("currencyUom"));
if (placingPartyId != null) {
priceContext.put("partyId", placingPartyId);
}
priceContext.put("quantity", repairUnitQuantity);
priceContext.put("product", repairItemProduct);
priceContext.put("webSiteId", orderHeader.get("webSiteId"));
priceContext.put("productStoreId", orderHeader.get("productStoreId"));
// TODO: prodCatalogId, agreementId
priceContext.put("productPricePurposeId", "PURCHASE");
priceContext.put("checkIncludeVat", "Y");
Map<String, Object> priceResult = null;
try {
priceResult = dispatcher.runSync("calculateProductPrice", priceContext);
} catch (GenericServiceException gse) {
Debug.logError(gse, module);
continue;
}
if (ServiceUtil.isError(priceResult)) {
Debug.logError(ServiceUtil.getErrorMessage(priceResult), module);
continue;
}
Boolean validPriceFound = (Boolean) priceResult.get("validPriceFound");
if (Boolean.FALSE.equals(validPriceFound)) {
Debug.logError("Could not find a valid price for the product with ID [" + repairItemProduct.get("productId") + "].", module);
continue;
}
if (priceResult.get("listPrice") != null) {
newItem.set("unitListPrice", priceResult.get("listPrice"));
}
BigDecimal repairUnitPrice = null;
if (priceResult.get("basePrice") != null) {
repairUnitPrice = (BigDecimal)priceResult.get("basePrice");
} else {
repairUnitPrice = BigDecimal.ZERO;
}
newItem.set("unitPrice", repairUnitPrice);
newItem.set("productId", repairItemProduct.get("productId"));
// TODO: orderItemTypeId, prodCatalogId, productCategoryId
newItem.set("quantity", repairQuantity);
newItem.set("itemDescription", ProductContentWrapper.getProductContentAsText(repairItemProduct, "PRODUCT_NAME", locale, null, "html"));
newItem.set("statusId", "ITEM_CREATED");
orderItems.add(newItem);
additionalItemTotal = additionalItemTotal.add(repairQuantity.multiply(repairUnitPrice));
if (orderItemShipGroupAssoc != null) {
GenericValue newOrderItemShipGroupAssoc = delegator.makeValue("OrderItemShipGroupAssoc", UtilMisc.toMap("orderItemSeqId", newItem.getString("orderItemSeqId"), "shipGroupSeqId", orderItemShipGroupAssoc.getString("shipGroupSeqId"), "quantity", repairQuantity));
orderItemShipGroupInfo.add(newOrderItemShipGroupAssoc);
}
// Create an association between the repair order item and the order item of the original order
newOrderItemAssoc = delegator.makeValue("OrderItemAssoc", UtilMisc.toMap("orderId", orderHeader.getString("orderId"),
"orderItemSeqId", orderItem.getString("orderItemSeqId"), "shipGroupSeqId", "_NA_",
"toOrderItemSeqId", newItem.getString("orderItemSeqId"), "toShipGroupSeqId", "_NA_", "orderItemAssocTypeId", "REPLACEMENT"));
orderItemAssocs.add(newOrderItemAssoc);
}
}
}
}
}
}
}
orderMap.put("orderItems", orderItems);
if (orderItemShipGroupInfo.size() > 0) {
orderMap.put("orderItemShipGroupInfo", orderItemShipGroupInfo);
}
if (orderItemAssocs.size() > 0) {
orderMap.put("orderItemAssociations", orderItemAssocs);
}
} else {
Debug.logError("No return items found??", module);
continue;
}
// create the replacement adjustment
GenericValue adj = delegator.makeValue("OrderAdjustment");
adj.set("orderAdjustmentTypeId", "REPLACE_ADJUSTMENT");
adj.set("amount", orderPriceTotal.negate());
adj.set("comments", "Replacement Item Return #" + returnId);
adj.set("createdDate", nowTimestamp);
adj.set("createdByUserLogin", userLogin.getString("userLoginId"));
orderMap.put("orderAdjustments", UtilMisc.toList(adj));
// Payment preference
if ((additionalItemTotal.compareTo(BigDecimal.ZERO) > 0) || ("RTN_CSREPLACE".equals(returnTypeId) && orderPriceTotal.compareTo(ZERO) > 0)) {
GenericValue paymentMethod = null;
try {
paymentMethod = returnHeader.getRelatedOne("PaymentMethod", false);
} catch (GenericEntityException e) {
Debug.logError(e, module);
}
if (paymentMethod != null) {
String paymentMethodId = paymentMethod.getString("paymentMethodId");
String paymentMethodTypeId = paymentMethod.getString("paymentMethodTypeId");
GenericValue opp = delegator.makeValue("OrderPaymentPreference");
opp.set("paymentMethodTypeId", paymentMethodTypeId);
opp.set("paymentMethodId", paymentMethodId);
// TODO: manualRefNum, manualAuthCode, securityCode, presentFlag, overflowFlag
if (paymentMethodId != null || "FIN_ACCOUNT".equals(paymentMethodTypeId)) {
opp.set("statusId", "PAYMENT_NOT_AUTH");
} else if (paymentMethodTypeId != null) {
// external payment method types require notification when received
// internal payment method types are assumed to be in-hand
if (paymentMethodTypeId.startsWith("EXT_")) {
opp.set("statusId", "PAYMENT_NOT_RECEIVED");
} else {
opp.set("statusId", "PAYMENT_RECEIVED");
}
}
if ("RTN_CSREPLACE".equals(returnTypeId)) {
opp.set("maxAmount", orderPriceTotal);
}
orderMap.put("orderPaymentInfo", UtilMisc.toList(opp));
}
}
// we'll assume new order is under same terms as original. note orderTerms is a required parameter of storeOrder
try {
orderMap.put("orderTerms", orderHeader.getRelated("OrderTerm", null, null, false));
} catch (GenericEntityException e) {
Debug.logError(e, "Cannot create replacement order because order terms for original order are not available", module);
}
// we'll assume the new order has the same order roles of the original one
try {
List<GenericValue> orderRoles = orderHeader.getRelated("OrderRole", null, null, false);
Map<String, List<String>> orderRolesMap = new HashMap<String, List<String>>();
if (orderRoles != null) {
for (GenericValue orderRole : orderRoles) {
List<String> parties = orderRolesMap.get(orderRole.getString("roleTypeId"));
if (parties == null) {
parties = new LinkedList<String>();
orderRolesMap.put(orderRole.getString("roleTypeId"), parties);
}
parties.add(orderRole.getString("partyId"));
}
}
if (orderRolesMap.size() > 0) {
orderMap.put("orderAdditionalPartyRoleMap", orderRolesMap);
}
} catch (GenericEntityException e) {
Debug.logError(e, "Cannot create replacement order because order roles for original order are not available", module);
}
// create the order
String createdOrderId = null;
Map<String, Object> orderResult = null;
try {
orderResult = dispatcher.runSync("storeOrder", orderMap);
} catch (GenericServiceException e) {
Debug.logInfo(e, "Problem creating the order!", module);
}
if (orderResult != null) {
createdOrderId = (String) orderResult.get("orderId");
createdOrderIds.add(createdOrderId);
}
// since there is no payments required; order is ready for processing/shipment
if (createdOrderId != null) {
if ("RETURN_ACCEPTED".equals(returnHeader.get("statusId")) && "RTN_WAIT_REPLACE_RES".equals(returnTypeId)) {
Map<String, Object> serviceResult = null;
try {
serviceResult = dispatcher.runSync("changeOrderStatus", UtilMisc.toMap("orderId", createdOrderId, "statusId", "ORDER_HOLD", "userLogin", userLogin));
} catch (GenericServiceException e) {
Debug.logError(e, "Service invocation error, status changes were not updated for order #" + createdOrderId, module);
return ServiceUtil.returnError(e.getMessage());
}
if (ServiceUtil.isError(serviceResult)) {
return ServiceUtil.returnError(ServiceUtil.getErrorMessage(serviceResult));
}
} else {
if ("CUSTOMER_RETURN".equals(returnHeaderTypeId)) {
OrderChangeHelper.approveOrder(dispatcher, userLogin, createdOrderId);
} else {
try {
OrderChangeHelper.orderStatusChanges(dispatcher, userLogin, createdOrderId, "ORDER_APPROVED", null, "ITEM_APPROVED", null);
} catch (GenericServiceException e) {
Debug.logError(e, "Service invocation error, status changes were not updated for order #" + createdOrderId, module);
return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,
"OrderErrorCannotStoreStatusChanges", locale) + createdOrderId);
}
}
}
// create a ReturnItemResponse and attach to each ReturnItem
Map<String, Object> itemResponse = new HashMap<String, Object>();
itemResponse.put("replacementOrderId", createdOrderId);
itemResponse.put("responseAmount", orderPriceTotal);
itemResponse.put("responseDate", nowTimestamp);
itemResponse.put("userLogin", userLogin);
String returnItemResponseId = null;
try {
Map<String, Object> createReturnItemResponseResult = dispatcher.runSync("createReturnItemResponse", itemResponse);
if (ServiceUtil.isError(createReturnItemResponseResult)) {
return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,
"OrderProblemCreatingReturnItemResponseRecord", locale),
null, null, createReturnItemResponseResult);
}
returnItemResponseId = (String) createReturnItemResponseResult.get("returnItemResponseId");
} catch (GenericServiceException e) {
Debug.logError(e, "Problem creating ReturnItemResponse record", module);
return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,
"OrderProblemCreatingReturnItemResponseRecord", locale));
}
for (GenericValue returnItem : returnItemList) {
Map<String, Object> updateReturnItemCtx = new HashMap<String, Object>();
updateReturnItemCtx.put("returnId", returnId);
updateReturnItemCtx.put("returnItemSeqId", returnItem.get("returnItemSeqId"));
updateReturnItemCtx.put("returnItemResponseId", returnItemResponseId);
updateReturnItemCtx.put("userLogin", userLogin);
try {
Map<String, Object> updateReturnItemResult = dispatcher.runSync("updateReturnItem", updateReturnItemCtx);
if (ServiceUtil.isError(updateReturnItemResult)) {
return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,
"OrderProblemStoringReturnItemUpdates", locale), null, null, updateReturnItemResult);
}
} catch (GenericServiceException e) {
Debug.logError(e, "Could not update ReturnItem record", module);
return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,
"OrderProblemStoringReturnItemUpdates", locale));
}
}
}
}
}
// create a return message AND create ReturnItemResponse record(s)
StringBuilder successMessage = new StringBuilder();
if (createdOrderIds.size() > 0) {
successMessage.append("The following new orders have been created : ");
Iterator<String> i = createdOrderIds.iterator();
while (i.hasNext()) {
successMessage.append(i.next());
if (i.hasNext()) {
successMessage.append(", ");
}
}
} else {
successMessage.append("No orders were created.");
}
return ServiceUtil.returnSuccess(successMessage.toString());
}
public static Map<String, Object> processSubscriptionReturn(DispatchContext dctx, Map<String, ? extends Object> context) {
Delegator delegator = dctx.getDelegator();
String returnId = (String) context.get("returnId");
Timestamp now = UtilDateTime.nowTimestamp();
GenericValue returnHeader;
List<GenericValue> returnItems = null;
try {
returnHeader = EntityQuery.use(delegator).from("ReturnHeader").where("returnId", returnId).queryOne();
if (returnHeader != null) {
returnItems = returnHeader.getRelated("ReturnItem", UtilMisc.toMap("returnTypeId", "RTN_REFUND"), null, false);
}
} catch (GenericEntityException e) {
Debug.logError(e, module);
return ServiceUtil.returnError(e.getMessage());
}
if (returnItems != null) {
for (GenericValue returnItem : returnItems) {
String orderItemSeqId = returnItem.getString("orderItemSeqId");
String orderId = returnItem.getString("orderId");
// lookup subscriptions
List<GenericValue> subscriptions;
try {
subscriptions = EntityQuery.use(delegator).from("Subscription").where("orderId", orderId, "orderItemSeqId", orderItemSeqId).queryList();
} catch (GenericEntityException e) {
Debug.logError(e, module);
return ServiceUtil.returnError(e.getMessage());
}
// cancel all current subscriptions
if (subscriptions != null) {
for (GenericValue subscription : subscriptions) {
Timestamp thruDate = subscription.getTimestamp("thruDate");
if (thruDate == null || thruDate.after(now)) {
subscription.set("thruDate", now);
try {
delegator.store(subscription);
} catch (GenericEntityException e) {
Debug.logError(e, module);
return ServiceUtil.returnError(e.getMessage());
}
}
}
}
}
}
return ServiceUtil.returnSuccess();
}
/**
* Takes a List of returnItems and returns a Map of orderId -> items and a Map of orderId -> orderTotal
* @param returnItems a List of return items
* @param returnItemsByOrderId the return items by order id
* @param totalByOrder the total by order id
* @param delegator the delegator
* @param returnId the return id
* @param returnTypeId the return type id
*/
public static void groupReturnItemsByOrder(List<GenericValue> returnItems, Map<String, List<GenericValue>> returnItemsByOrderId, Map<String, BigDecimal> totalByOrder, Delegator delegator, String returnId, String returnTypeId) {
for (GenericValue returnItem : returnItems) {
String orderId = returnItem.getString("orderId");
if (orderId != null) {
if (returnItemsByOrderId != null) {
BigDecimal totalForOrder = null;
if (totalByOrder != null) {
totalForOrder = totalByOrder.get(orderId);
}
List<GenericValue> returnItemList = returnItemsByOrderId.get(orderId);
if (returnItemList == null) {
returnItemList = new LinkedList<GenericValue>();
}
if (totalForOrder == null) {
totalForOrder = BigDecimal.ZERO;
}
// add to the items list
returnItemList.add(returnItem);
returnItemsByOrderId.put(orderId, returnItemList);
if (totalByOrder != null) {
// add on the total for this line
BigDecimal quantity = returnItem.getBigDecimal("returnQuantity");
BigDecimal amount = returnItem.getBigDecimal("returnPrice");
if (quantity == null) {
quantity = BigDecimal.ZERO;
}
if (amount == null) {
amount = BigDecimal.ZERO;
}
BigDecimal thisTotal = amount.multiply(quantity);
BigDecimal existingTotal = totalForOrder;
Map<String, Object> condition = UtilMisc.toMap("returnId", returnItem.get("returnId"), "returnItemSeqId", returnItem.get("returnItemSeqId"));
BigDecimal newTotal = existingTotal.add(thisTotal).add(getReturnAdjustmentTotal(delegator, condition));
totalByOrder.put(orderId, newTotal);
}
}
}
}
// We may also have some order-level adjustments, so we need to go through each order again and add those as well
if ((totalByOrder != null) && (totalByOrder.keySet() != null)) {
for (String orderId : totalByOrder.keySet()) {
// find returnAdjustment for returnHeader
Map<String, Object> condition = UtilMisc.<String, Object>toMap("returnId", returnId,
"returnItemSeqId", org.apache.ofbiz.common.DataModelConstants.SEQ_ID_NA,
"returnTypeId", returnTypeId);
BigDecimal existingTotal = (totalByOrder.get(orderId)).add(getReturnAdjustmentTotal(delegator, condition));
totalByOrder.put(orderId, existingTotal);
}
}
}
public static Map<String, Object> getReturnAmountByOrder(DispatchContext dctx, Map<String, ? extends Object> context) {
Delegator delegator = dctx.getDelegator();
String returnId = (String) context.get("returnId");
Locale locale = (Locale) context.get("locale");
List<GenericValue> returnItems = null;
Map<String, Object> returnAmountByOrder = new HashMap<String, Object>();
try {
returnItems = EntityQuery.use(delegator).from("ReturnItem").where("returnId", returnId).queryList();
} catch (GenericEntityException e) {
Debug.logError(e, "Problems looking up return information", module);
return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,
"OrderErrorGettingReturnHeaderItemInformation", locale));
}
if ((returnItems != null) && (returnItems.size() > 0)) {
List<String> paymentList = new LinkedList<String>();
for (GenericValue returnItem : returnItems) {
String orderId = returnItem.getString("orderId");
try {
GenericValue returnItemResponse = returnItem.getRelatedOne("ReturnItemResponse", false);
if ((returnItemResponse != null) && (orderId != null)) {
// TODO should we filter on payment's status (PMNT_SENT,PMNT_RECEIVED)
GenericValue payment = returnItemResponse.getRelatedOne("Payment", false);
if ((payment != null) && (payment.getBigDecimal("amount") != null) &&
!paymentList.contains(payment.get("paymentId"))) {
UtilMisc.addToBigDecimalInMap(returnAmountByOrder, orderId, payment.getBigDecimal("amount"));
paymentList.add(payment.getString("paymentId")); // make sure we don't add duplicated payment amount
}
}
} catch (GenericEntityException e) {
Debug.logError(e, "Problems looking up return item related information", module);
return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,
"OrderErrorGettingReturnHeaderItemInformation", locale));
}
}
}
return UtilMisc.<String, Object>toMap("orderReturnAmountMap", returnAmountByOrder);
}
public static Map<String, Object> checkPaymentAmountForRefund(DispatchContext dctx, Map<String, ? extends Object> context) {
LocalDispatcher dispatcher = dctx.getDispatcher();
Delegator delegator = dctx.getDelegator();
String returnId = (String) context.get("returnId");
Locale locale = (Locale) context.get("locale");
Map<String, BigDecimal> returnAmountByOrder = null;
Map<String, Object> serviceResult = null;
try {
serviceResult = dispatcher.runSync("getReturnAmountByOrder", org.apache.ofbiz.base.util.UtilMisc.toMap("returnId", returnId));
} catch (GenericServiceException e) {
Debug.logError(e, "Problem running the getReturnAmountByOrder service", module);
return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,
"OrderProblemsWithGetReturnAmountByOrder", locale));
}
if (ServiceUtil.isError(serviceResult)) {
return ServiceUtil.returnError((String) serviceResult.get(ModelService.ERROR_MESSAGE));
} else {
returnAmountByOrder = UtilGenerics.checkMap(serviceResult.get("orderReturnAmountMap"));
}
if ((returnAmountByOrder != null) && (returnAmountByOrder.keySet() != null)) {
for (String orderId : returnAmountByOrder.keySet()) {
BigDecimal returnAmount = returnAmountByOrder.get(orderId);
if (returnAmount == null) {
return ServiceUtil.returnError(UtilProperties.getMessage(resource_error, "OrderNoReturnAmountFound", UtilMisc.toMap("orderId", orderId), locale));
}
if (returnAmount.abs().compareTo(new BigDecimal("0.000001")) < 0) {
Debug.logError("Order [" + orderId + "] refund amount[ " + returnAmount + "] less than zero", module);
return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,
"OrderReturnTotalCannotLessThanZero", locale));
}
OrderReadHelper helper = new OrderReadHelper(delegator, orderId);
BigDecimal grandTotal = helper.getOrderGrandTotal();
if (returnAmount.subtract(grandTotal).compareTo(new BigDecimal("0.01")) > 0) {
Debug.logError("Order [" + orderId + "] refund amount[ " + returnAmount + "] exceeds order total [" + grandTotal + "]", module);
return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,
"OrderRefundAmountExceedsOrderTotal", locale));
}
}
}
return ServiceUtil.returnSuccess();
}
public static Map<String, Object> createReturnAdjustment(DispatchContext dctx, Map<String, Object> context) {
Delegator delegator = dctx.getDelegator();
String orderAdjustmentId = (String) context.get("orderAdjustmentId");
String returnAdjustmentTypeId = (String) context.get("returnAdjustmentTypeId");
String returnId = (String) context.get("returnId");
String returnItemSeqId = (String) context.get("returnItemSeqId");
String description = (String) context.get("description");
BigDecimal amount = (BigDecimal) context.get("amount");
Locale locale = (Locale) context.get("locale");
GenericValue returnItemTypeMap = null;
GenericValue orderAdjustment = null;
GenericValue returnAdjustmentType = null;
GenericValue orderItem = null;
GenericValue returnItem = null;
GenericValue returnHeader = null;
// if orderAdjustment is not empty, then copy most return adjustment information from orderAdjustment's
if (orderAdjustmentId != null) {
try {
orderAdjustment = EntityQuery.use(delegator).from("OrderAdjustment").where("orderAdjustmentId", orderAdjustmentId).queryOne();
if (orderAdjustment == null) {
return ServiceUtil.returnError(UtilProperties.getMessage(resource,
"OrderCreateReturnAdjustmentNotFoundOrderAdjustment",
UtilMisc.toMap("orderAdjustmentId", orderAdjustmentId), locale));
}
// get returnHeaderTypeId from ReturnHeader and then use it to figure out return item type mapping
returnHeader = EntityQuery.use(delegator).from("ReturnHeader").where("returnId", returnId).queryOne();
String returnHeaderTypeId = ((returnHeader != null) && (returnHeader.getString("returnHeaderTypeId") != null)) ? returnHeader.getString("returnHeaderTypeId") : "CUSTOMER_RETURN";
returnItemTypeMap = EntityQuery.use(delegator).from("ReturnItemTypeMap").where("returnHeaderTypeId", returnHeaderTypeId, "returnItemMapKey", orderAdjustment.get("orderAdjustmentTypeId")).queryOne();
returnAdjustmentType = returnItemTypeMap.getRelatedOne("ReturnAdjustmentType", false);
if (returnAdjustmentType != null && UtilValidate.isEmpty(description)) {
description = returnAdjustmentType.getString("description");
}
if ((returnItemSeqId != null) && !("_NA_".equals(returnItemSeqId))) {
returnItem = EntityQuery.use(delegator).from("ReturnItem").where("returnId", returnId, "returnItemSeqId", returnItemSeqId).queryOne();
Debug.logInfo("returnId:" + returnId + ",returnItemSeqId:" + returnItemSeqId, module);
orderItem = returnItem.getRelatedOne("OrderItem", false);
} else {
// we don't have the returnItemSeqId but before we consider this
// an header adjustment we try to get a return item in this return
// associated to the same order item to which the adjustments refers (if any)
if (UtilValidate.isNotEmpty(orderAdjustment.getString("orderItemSeqId")) &&
!"_NA_".equals(orderAdjustment.getString("orderItemSeqId"))) {
returnItem = EntityQuery.use(delegator).from("ReturnItem")
.where("returnId", returnId, "orderId", orderAdjustment.getString("orderId"), "orderItemSeqId", orderAdjustment.getString("orderItemSeqId"))
.queryFirst();
if (returnItem != null) {
orderItem = returnItem.getRelatedOne("OrderItem", false);
}
}
}
} catch (GenericEntityException e) {
Debug.logError(e, module);
throw new GeneralRuntimeException(e.getMessage());
}
context.putAll(orderAdjustment.getAllFields());
if (UtilValidate.isNotEmpty(amount)) {
context.put("amount", amount);
}
}
// if orderAdjustmentTypeId is empty, ie not found from orderAdjustmentId, then try to get returnAdjustmentTypeId from returnItemTypeMap,
// if still empty, use default RET_MAN_ADJ
if (returnAdjustmentTypeId == null) {
String mappingTypeId = returnItemTypeMap != null ? returnItemTypeMap.get("returnItemTypeId").toString() : null;
returnAdjustmentTypeId = mappingTypeId != null ? mappingTypeId : "RET_MAN_ADJ";
}
// calculate the returnAdjustment amount
if (returnItem != null) { // returnAdjustment for returnItem
if (needRecalculate(returnAdjustmentTypeId)) {
Debug.logInfo("returnPrice:" + returnItem.getBigDecimal("returnPrice") + ",returnQuantity:" + returnItem.getBigDecimal("returnQuantity") + ",sourcePercentage:" + orderAdjustment.getBigDecimal("sourcePercentage"), module);
BigDecimal returnTotal = returnItem.getBigDecimal("returnPrice").multiply(returnItem.getBigDecimal("returnQuantity"));
BigDecimal orderTotal = orderItem.getBigDecimal("quantity").multiply(orderItem.getBigDecimal("unitPrice"));
amount = getAdjustmentAmount("RET_SALES_TAX_ADJ".equals(returnAdjustmentTypeId), returnTotal, orderTotal, orderAdjustment.getBigDecimal("amount"));
} else {
amount = (BigDecimal) context.get("amount");
}
} else { // returnAdjustment for returnHeader
amount = (BigDecimal) context.get("amount");
}
// store the return adjustment
String seqId = delegator.getNextSeqId("ReturnAdjustment");
GenericValue newReturnAdjustment = delegator.makeValue("ReturnAdjustment",
UtilMisc.toMap("returnAdjustmentId", seqId));
try {
newReturnAdjustment.setNonPKFields(context);
if (orderAdjustment != null && orderAdjustment.get("taxAuthorityRateSeqId") != null) {
newReturnAdjustment.set("taxAuthorityRateSeqId", orderAdjustment.getString("taxAuthorityRateSeqId"));
}
newReturnAdjustment.set("amount", amount == null ? BigDecimal.ZERO : amount);
newReturnAdjustment.set("returnAdjustmentTypeId", returnAdjustmentTypeId);
newReturnAdjustment.set("description", description);
newReturnAdjustment.set("returnItemSeqId", UtilValidate.isEmpty(returnItemSeqId) ? "_NA_" : returnItemSeqId);
delegator.create(newReturnAdjustment);
Map<String, Object> result = ServiceUtil.returnSuccess(UtilProperties.getMessage(resource,
"OrderCreateReturnAdjustment", UtilMisc.toMap("seqId", seqId), locale));
result.put("returnAdjustmentId", seqId);
return result;
} catch (GenericEntityException e) {
Debug.logError(e, "Failed to store returnAdjustment", module);
return ServiceUtil.returnError(UtilProperties.getMessage(resource,
"OrderCreateReturnAdjustmentFailed", locale));
}
}
public static Map<String, Object> updateReturnAdjustment(DispatchContext dctx, Map<String, Object> context) {
Delegator delegator = dctx.getDelegator();
Locale locale = (Locale) context.get("locale");
GenericValue returnItem = null;
GenericValue returnAdjustment = null;
String returnAdjustmentTypeId = null;
BigDecimal amount;
try {
returnAdjustment = EntityQuery.use(delegator).from("ReturnAdjustment").where("returnAdjustmentId", context.get("returnAdjustmentId")).queryOne();
if (returnAdjustment != null) {
returnItem = EntityQuery.use(delegator).from("ReturnItem").where("returnId", returnAdjustment.get("returnId"), "returnItemSeqId", returnAdjustment.get("returnItemSeqId")).queryOne();
returnAdjustmentTypeId = returnAdjustment.getString("returnAdjustmentTypeId");
}
// calculate the returnAdjustment amount
if (returnItem != null) { // returnAdjustment for returnItem
BigDecimal originalReturnPrice = (context.get("originalReturnPrice") != null) ? ((BigDecimal) context.get("originalReturnPrice")) : returnItem.getBigDecimal("returnPrice");
BigDecimal originalReturnQuantity = (context.get("originalReturnQuantity") != null) ? ((BigDecimal) context.get("originalReturnQuantity")) : returnItem.getBigDecimal("returnQuantity");
if (needRecalculate(returnAdjustmentTypeId)) {
BigDecimal returnTotal = returnItem.getBigDecimal("returnPrice").multiply(returnItem.getBigDecimal("returnQuantity"));
BigDecimal originalReturnTotal = originalReturnPrice.multiply(originalReturnQuantity);
amount = getAdjustmentAmount("RET_SALES_TAX_ADJ".equals(returnAdjustmentTypeId), returnTotal, originalReturnTotal, returnAdjustment.getBigDecimal("amount"));
} else {
amount = (BigDecimal) context.get("amount");
}
} else { // returnAdjustment for returnHeader
amount = (BigDecimal) context.get("amount");
}
Map<String, Object> result = null;
if (UtilValidate.isNotEmpty(amount)) {
returnAdjustment.setNonPKFields(context);
returnAdjustment.set("amount", amount);
delegator.store(returnAdjustment);
Debug.logInfo("Update ReturnAdjustment with Id:" + context.get("returnAdjustmentId") + " to amount " + amount +" successfully.", module);
result = ServiceUtil.returnSuccess(UtilProperties.getMessage(resource,
"OrderUpdateReturnAdjustment",
UtilMisc.toMap("returnAdjustmentId", context.get("returnAdjustmentId"), "amount", amount), locale));
} else {
result = ServiceUtil.returnSuccess();
}
return result;
} catch (GenericEntityException e) {
Debug.logError(e, "Failed to store returnAdjustment", module);
return ServiceUtil.returnError(UtilProperties.getMessage(resource,
"OrderCreateReturnAdjustmentFailed", locale));
}
}
// used as a dispatch service, invoke different service based on the parameters passed in
public static Map<String, Object> createReturnItemOrAdjustment(DispatchContext dctx, Map<String, ? extends Object> context) {
Debug.logInfo("createReturnItemOrAdjustment's context:" + context, module);
String orderItemSeqId = (String) context.get("orderItemSeqId");
Debug.logInfo("orderItemSeqId:" + orderItemSeqId +"#", module);
LocalDispatcher dispatcher = dctx.getDispatcher();
//if the request is to create returnItem, orderItemSeqId should not be empty
String serviceName = UtilValidate.isNotEmpty(orderItemSeqId) ? "createReturnItem" : "createReturnAdjustment";
Debug.logInfo("serviceName:" + serviceName, module);
try {
Map<String, Object> inMap = dctx.makeValidContext(serviceName, "IN", context);
if ("createReturnItem".equals(serviceName)) {
// we don't want to automatically include the adjustments
// when the return item is created because they are selectable by the user
inMap.put("includeAdjustments", "N");
}
return dispatcher.runSync(serviceName, inMap);
} catch (org.apache.ofbiz.service.GenericServiceException e) {
Debug.logError(e, module);
return ServiceUtil.returnError(e.getMessage());
}
}
// used as a dispatch service, invoke different service based on the parameters passed in
public static Map<String, Object> updateReturnItemOrAdjustment(DispatchContext dctx, Map<String, ? extends Object> context) {
Debug.logInfo("updateReturnItemOrAdjustment's context:" + context, module);
String returnAdjustmentId = (String) context.get("returnAdjustmentId");
Debug.logInfo("returnAdjustmentId:" + returnAdjustmentId +"#", module);
LocalDispatcher dispatcher = dctx.getDispatcher();
//if the request is to create returnItem, orderItemSeqId should not be empty
String serviceName = UtilValidate.isEmpty(returnAdjustmentId) ? "updateReturnItem" : "updateReturnAdjustment";
Debug.logInfo("serviceName:" + serviceName, module);
try {
return dispatcher.runSync(serviceName, dctx.makeValidContext(serviceName, "IN", context));
} catch (org.apache.ofbiz.service.GenericServiceException e) {
Debug.logError(e, module);
return ServiceUtil.returnError(e.getMessage());
}
}
/**
* These return adjustment types need to be recalculated when the return item is updated
* @param returnAdjustmentTypeId the return adjustment type id
* @return returns if the returnn adjustment need to be recalculated
*/
public static boolean needRecalculate(String returnAdjustmentTypeId) {
return "RET_PROMOTION_ADJ".equals(returnAdjustmentTypeId) ||
"RET_DISCOUNT_ADJ".equals(returnAdjustmentTypeId) ||
"RET_SALES_TAX_ADJ".equals(returnAdjustmentTypeId);
}
/**
* Get the total return adjustments for a set of key -> value condition pairs. Done for code efficiency.
* @param delegator the delegator
* @param condition the conditions to use
* @return return the total return adjustments
*/
public static BigDecimal getReturnAdjustmentTotal(Delegator delegator, Map<String, ? extends Object> condition) {
BigDecimal total = BigDecimal.ZERO;
List<GenericValue> adjustments;
try {
// TODO: find on a view-entity with a sum is probably more efficient
adjustments = EntityQuery.use(delegator).from("ReturnAdjustment").where(condition).queryList();
if (adjustments != null) {
for (GenericValue returnAdjustment : adjustments) {
if ((returnAdjustment != null) && (returnAdjustment.get("amount") != null)) {
total = total.add(returnAdjustment.getBigDecimal("amount"));
}
}
}
} catch (org.apache.ofbiz.entity.GenericEntityException e) {
Debug.logError(e, module);
}
return total;
}
/**
* Get rid of unnecessary parameters based on the given service name
* @param dctx Service DispatchContext
* @param serviceName the service name
* @param context context before clean up
* @return filtered context
* @throws GenericServiceException
*
* @deprecated - Use DispatchContext.makeValidContext(String, String, Map) instead
*/
@Deprecated
public static Map<String, Object> filterServiceContext(DispatchContext dctx, String serviceName, Map<String, ? extends Object> context) throws GenericServiceException {
return dctx.makeValidContext(serviceName, "IN", context);
}
/**
* Calculate new returnAdjustment amount and set scale and rounding mode based on returnAdjustmentType: RET_SALES_TAX_ADJ use sales.tax._ and others use order._
* @param isSalesTax if returnAdjustmentType is SaleTax
* @param returnTotal
* @param originalTotal
* @param amount
* @return new returnAdjustment amount
*/
public static BigDecimal getAdjustmentAmount(boolean isSalesTax, BigDecimal returnTotal, BigDecimal originalTotal, BigDecimal amount) {
String settingPrefix = isSalesTax ? "salestax" : "order";
String decimalsPrefix = isSalesTax ? ".calc" : "";
int decimals = UtilNumber.getBigDecimalScale(settingPrefix + decimalsPrefix + ".decimals");
int rounding = UtilNumber.getBigDecimalRoundingMode(settingPrefix + ".rounding");
returnTotal = returnTotal.setScale(decimals, rounding);
originalTotal = originalTotal.setScale(decimals, rounding);
BigDecimal newAmount = null;
if (ZERO.compareTo(originalTotal) != 0) {
newAmount = returnTotal.multiply(amount).divide(originalTotal, decimals, rounding);
} else {
newAmount = ZERO;
}
return newAmount;
}
}