0001: /*******************************************************************************
0002: * Licensed to the Apache Software Foundation (ASF) under one
0003: * or more contributor license agreements. See the NOTICE file
0004: * distributed with this work for additional information
0005: * regarding copyright ownership. The ASF licenses this file
0006: * to you under the Apache License, Version 2.0 (the
0007: * "License"); you may not use this file except in compliance
0008: * with the License. You may obtain a copy of the License at
0009: *
0010: * http://www.apache.org/licenses/LICENSE-2.0
0011: *
0012: * Unless required by applicable law or agreed to in writing,
0013: * software distributed under the License is distributed on an
0014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
0015: * KIND, either express or implied. See the License for the
0016: * specific language governing permissions and limitations
0017: * under the License.
0018: *******************************************************************************/package org.ofbiz.order.order;
0019:
0020: import java.math.BigDecimal;
0021: import java.sql.Timestamp;
0022: import java.util.ArrayList;
0023: import java.util.HashMap;
0024: import java.util.Iterator;
0025: import java.util.LinkedList;
0026: import java.util.List;
0027: import java.util.Locale;
0028: import java.util.Map;
0029: import java.util.Set;
0030:
0031: import javolution.util.FastMap;
0032: import javolution.util.FastList;
0033:
0034: import org.ofbiz.base.util.Debug;
0035: import org.ofbiz.base.util.GeneralRuntimeException;
0036: import org.ofbiz.base.util.UtilDateTime;
0037: import org.ofbiz.base.util.UtilFormatOut;
0038: import org.ofbiz.base.util.UtilMisc;
0039: import org.ofbiz.base.util.UtilNumber;
0040: import org.ofbiz.base.util.UtilProperties;
0041: import org.ofbiz.base.util.UtilValidate;
0042: import org.ofbiz.base.util.collections.ResourceBundleMapWrapper;
0043: import org.ofbiz.entity.GenericDelegator;
0044: import org.ofbiz.entity.GenericEntityException;
0045: import org.ofbiz.entity.GenericValue;
0046: import org.ofbiz.entity.condition.EntityConditionList;
0047: import org.ofbiz.entity.condition.EntityExpr;
0048: import org.ofbiz.entity.condition.EntityOperator;
0049: import org.ofbiz.entity.util.EntityUtil;
0050: import org.ofbiz.product.store.ProductStoreWorker;
0051: import org.ofbiz.service.DispatchContext;
0052: import org.ofbiz.service.GenericServiceException;
0053: import org.ofbiz.service.LocalDispatcher;
0054: import org.ofbiz.service.ModelParam;
0055: import org.ofbiz.service.ModelService;
0056: import org.ofbiz.service.ServiceUtil;
0057:
0058: /**
0059: * OrderReturnServices
0060: */
0061: public class OrderReturnServices {
0062:
0063: public static final String module = OrderReturnServices.class
0064: .getName();
0065: public static final String resource = "OrderUiLabels";
0066: public static final String resource_error = "OrderErrorUiLabels";
0067:
0068: // set some BigDecimal properties
0069: private static BigDecimal ZERO = new BigDecimal("0");
0070: private static int decimals = -1;
0071: private static int rounding = -1;
0072: static {
0073: decimals = UtilNumber.getBigDecimalScale("invoice.decimals");
0074: rounding = UtilNumber
0075: .getBigDecimalRoundingMode("invoice.rounding");
0076:
0077: // set zero to the proper scale
0078: if (decimals != -1)
0079: ZERO = ZERO.setScale(decimals);
0080: }
0081:
0082: // locate the return item's initial inventory item cost
0083: public static Map getReturnItemInitialCost(DispatchContext dctx,
0084: Map context) {
0085: GenericDelegator delegator = dctx.getDelegator();
0086: String returnId = (String) context.get("returnId");
0087: String returnItemSeqId = (String) context
0088: .get("returnItemSeqId");
0089:
0090: Map result = ServiceUtil.returnSuccess();
0091: result.put("initialItemCost", getReturnItemInitialCost(
0092: delegator, returnId, returnItemSeqId));
0093: return result;
0094: }
0095:
0096: // obtain order/return total information
0097: public static Map getOrderAvailableReturnedTotal(
0098: DispatchContext dctx, Map context) {
0099: GenericDelegator delegator = dctx.getDelegator();
0100: String orderId = (String) context.get("orderId");
0101: OrderReadHelper orh = null;
0102: try {
0103: orh = new OrderReadHelper(delegator, orderId);
0104: } catch (IllegalArgumentException e) {
0105: return ServiceUtil.returnError(e.getMessage());
0106: }
0107:
0108: // an adjustment value to test
0109: BigDecimal adj = (BigDecimal) context.get("adjustment");
0110: if (adj == null) {
0111: adj = ZERO;
0112: }
0113:
0114: Boolean countNewReturnItems = (Boolean) context
0115: .get("countNewReturnItems");
0116: if (countNewReturnItems == null) {
0117: countNewReturnItems = Boolean.FALSE;
0118: }
0119: BigDecimal returnTotal = orh
0120: .getOrderReturnedTotalBd(countNewReturnItems
0121: .booleanValue());
0122: BigDecimal orderTotal = orh.getOrderGrandTotalBd();
0123: BigDecimal available = orderTotal.subtract(returnTotal)
0124: .subtract(adj);
0125:
0126: Map result = ServiceUtil.returnSuccess();
0127: result.put("availableReturnTotal", available);
0128: result.put("orderTotal", orderTotal);
0129: result.put("returnTotal", returnTotal);
0130: return result;
0131: }
0132:
0133: // worker method which can be used in screen iterations
0134: public static Double getReturnItemInitialCost(
0135: GenericDelegator delegator, String returnId,
0136: String returnItemSeqId) {
0137: if (delegator == null || returnId == null
0138: || returnItemSeqId == null) {
0139: throw new IllegalArgumentException(
0140: "Method parameters cannot contain nulls");
0141: }
0142: Debug.log("Finding the initial item cost for return item : "
0143: + returnId + " / " + returnItemSeqId, module);
0144:
0145: // the cost holder
0146: Double itemCost = new Double(0.00);
0147:
0148: // get the return item information
0149: GenericValue returnItem = null;
0150: try {
0151: returnItem = delegator.findByPrimaryKey("ReturnItem",
0152: UtilMisc.toMap("returnId", returnId,
0153: "returnItemSeqId", returnItemSeqId));
0154: } catch (GenericEntityException e) {
0155: Debug.logError(e, module);
0156: throw new GeneralRuntimeException(e.getMessage());
0157: }
0158: Debug.log("Return item value object - " + returnItem, module);
0159:
0160: // check for an orderItem association
0161: if (returnItem != null) {
0162: String orderId = returnItem.getString("orderId");
0163: String orderItemSeqId = returnItem
0164: .getString("orderItemSeqId");
0165: if (orderItemSeqId != null && orderId != null) {
0166: Debug.log("Found order item reference", module);
0167: // locate the item issuance(s) for this order item
0168: List itemIssue = null;
0169: try {
0170: itemIssue = delegator.findByAnd("ItemIssuance",
0171: UtilMisc.toMap("orderId", orderId,
0172: "orderItemSeqId", orderItemSeqId));
0173: } catch (GenericEntityException e) {
0174: Debug.logError(e, module);
0175: throw new GeneralRuntimeException(e.getMessage());
0176: }
0177: if (itemIssue != null && itemIssue.size() > 0) {
0178: Debug.log("Found item issuance referece", module);
0179: // just use the first one for now; maybe later we can find a better way to determine which was the
0180: // actual item being returned; maybe by serial number
0181: GenericValue issue = EntityUtil.getFirst(itemIssue);
0182: GenericValue inventoryItem = null;
0183: try {
0184: inventoryItem = issue
0185: .getRelatedOne("InventoryItem");
0186: } catch (GenericEntityException e) {
0187: Debug.logError(e, module);
0188: throw new GeneralRuntimeException(e
0189: .getMessage());
0190: }
0191: if (inventoryItem != null) {
0192: Debug.log("Located inventory item - "
0193: + inventoryItem
0194: .getString("inventoryItemId"),
0195: module);
0196: if (inventoryItem.get("unitCost") != null) {
0197: itemCost = inventoryItem
0198: .getDouble("unitCost");
0199: } else {
0200: Debug
0201: .logInfo(
0202: "Found item cost; but cost was null. Returning default amount (0.00)",
0203: module);
0204: }
0205: }
0206: }
0207: }
0208: }
0209:
0210: Debug.log("Initial item cost - " + itemCost, module);
0211: return itemCost;
0212: }
0213:
0214: // helper method for sending return notifications
0215: private static Map sendReturnNotificationScreen(
0216: DispatchContext dctx, Map context, String emailType) {
0217: GenericDelegator delegator = dctx.getDelegator();
0218: LocalDispatcher dispatcher = dctx.getDispatcher();
0219: GenericValue userLogin = (GenericValue) context
0220: .get("userLogin");
0221: String returnId = (String) context.get("returnId");
0222: Locale locale = (Locale) context.get("locale");
0223:
0224: // get the return header
0225: GenericValue returnHeader = null;
0226: try {
0227: returnHeader = delegator.findByPrimaryKey("ReturnHeader",
0228: UtilMisc.toMap("returnId", returnId));
0229: } catch (GenericEntityException e) {
0230: Debug.logError(e, module);
0231: return ServiceUtil.returnError(UtilProperties.getMessage(
0232: resource_error,
0233: "OrderErrorUnableToGetReturnHeaderForID", UtilMisc
0234: .toMap("returnId", returnId), locale));
0235: }
0236:
0237: // get the return items
0238: List returnItems = null;
0239: try {
0240: returnItems = returnHeader.getRelated("ReturnItem");
0241: } catch (GenericEntityException e) {
0242: Debug.logError(e, module);
0243: return ServiceUtil
0244: .returnError(UtilProperties
0245: .getMessage(
0246: resource_error,
0247: "OrderErrorUnableToGetReturnItemRecordsFromReturnHeader",
0248: locale));
0249: }
0250:
0251: // get the order header -- the first item will determine which product store to use from the order
0252: String productStoreId = null;
0253: String emailAddress = null;
0254: if (returnItems != null && returnItems.size() > 0) {
0255: GenericValue firstItem = EntityUtil.getFirst(returnItems);
0256: GenericValue orderHeader = null;
0257: try {
0258: orderHeader = firstItem.getRelatedOne("OrderHeader");
0259: } catch (GenericEntityException e) {
0260: Debug.logError(e, module);
0261: return ServiceUtil
0262: .returnError(UtilProperties
0263: .getMessage(
0264: resource_error,
0265: "OrderErrorUnableToGetOrderHeaderFromReturnItem",
0266: locale));
0267: }
0268:
0269: if (orderHeader != null
0270: && UtilValidate.isNotEmpty(orderHeader
0271: .getString("productStoreId"))) {
0272: OrderReadHelper orh = new OrderReadHelper(orderHeader);
0273: productStoreId = orh.getProductStoreId();
0274: emailAddress = orh.getOrderEmailString();
0275: }
0276: }
0277:
0278: // get the email setting and send the mail
0279: if (productStoreId != null && productStoreId.length() > 0) {
0280: Map sendMap = FastMap.newInstance();
0281:
0282: GenericValue productStoreEmail = null;
0283: try {
0284: productStoreEmail = delegator.findByPrimaryKey(
0285: "ProductStoreEmailSetting", UtilMisc.toMap(
0286: "productStoreId", productStoreId,
0287: "emailType", emailType));
0288: } catch (GenericEntityException e) {
0289: Debug.logError(e, module);
0290: }
0291:
0292: if (productStoreEmail != null && emailAddress != null) {
0293: String bodyScreenLocation = productStoreEmail
0294: .getString("bodyScreenLocation");
0295: if (UtilValidate.isEmpty(bodyScreenLocation)) {
0296: bodyScreenLocation = ProductStoreWorker
0297: .getDefaultProductStoreEmailScreenLocation(emailType);
0298: }
0299: sendMap.put("bodyScreenUri", bodyScreenLocation);
0300:
0301: ResourceBundleMapWrapper uiLabelMap = (ResourceBundleMapWrapper) UtilProperties
0302: .getResourceBundleMap("EcommerceUiLabels",
0303: locale);
0304: uiLabelMap.addBottomResourceBundle("OrderUiLabels");
0305: uiLabelMap.addBottomResourceBundle("CommonUiLabels");
0306:
0307: Map bodyParameters = UtilMisc.toMap("returnHeader",
0308: returnHeader, "returnItems", returnItems,
0309: "uiLabelMap", uiLabelMap, "locale", locale);
0310: sendMap.put("bodyParameters", bodyParameters);
0311:
0312: sendMap.put("subject", productStoreEmail
0313: .getString("subject"));
0314: sendMap.put("contentType", productStoreEmail
0315: .get("contentType"));
0316: sendMap.put("sendFrom", productStoreEmail
0317: .get("fromAddress"));
0318: sendMap.put("sendCc", productStoreEmail
0319: .get("ccAddress"));
0320: sendMap.put("sendBcc", productStoreEmail
0321: .get("bccAddress"));
0322: sendMap.put("sendTo", emailAddress);
0323:
0324: sendMap.put("userLogin", userLogin);
0325:
0326: Map sendResp = null;
0327: try {
0328: sendResp = dispatcher.runSync("sendMailFromScreen",
0329: sendMap);
0330: } catch (GenericServiceException e) {
0331: Debug.logError(e, "Problem sending mail", module);
0332: return ServiceUtil
0333: .returnError(UtilProperties.getMessage(
0334: resource_error,
0335: "OrderProblemSendingEmail", locale));
0336: }
0337:
0338: // check for errors
0339: if (sendResp != null && ServiceUtil.isError(sendResp)) {
0340: sendResp.put("emailType", emailType);
0341: return ServiceUtil
0342: .returnError(
0343: UtilProperties.getMessage(
0344: resource_error,
0345: "OrderProblemSendingEmail",
0346: locale), null, null,
0347: sendResp);
0348: }
0349: return ServiceUtil.returnSuccess();
0350: }
0351: }
0352:
0353: return ServiceUtil
0354: .returnFailure("No valid email setting for store");
0355: }
0356:
0357: // return request notification
0358: public static Map sendReturnAcceptNotification(
0359: DispatchContext dctx, Map context) {
0360: return sendReturnNotificationScreen(dctx, context,
0361: "PRDS_RTN_ACCEPT");
0362: }
0363:
0364: // return complete notification
0365: public static Map sendReturnCompleteNotification(
0366: DispatchContext dctx, Map context) {
0367: return sendReturnNotificationScreen(dctx, context,
0368: "PRDS_RTN_COMPLETE");
0369: }
0370:
0371: // return cancel notification
0372: public static Map sendReturnCancelNotification(
0373: DispatchContext dctx, Map context) {
0374: return sendReturnNotificationScreen(dctx, context,
0375: "PRDS_RTN_CANCEL");
0376: }
0377:
0378: // get the returnable quantiy for an order item
0379: public static Map getReturnableQuantity(DispatchContext dctx,
0380: Map context) {
0381: GenericValue orderItem = (GenericValue) context
0382: .get("orderItem");
0383: GenericValue product = null;
0384: Locale locale = (Locale) context.get("locale");
0385: if (orderItem.get("productId") != null) {
0386: try {
0387: product = orderItem.getRelatedOne("Product");
0388: } catch (GenericEntityException e) {
0389: Debug.logError(e,
0390: "ERROR: Unable to get Product from OrderItem",
0391: module);
0392: }
0393: }
0394:
0395: // check returnable status
0396: boolean returnable = true;
0397:
0398: // first check returnable flag
0399: if (product != null
0400: && product.get("returnable") != null
0401: && "N"
0402: .equalsIgnoreCase(product
0403: .getString("returnable"))) {
0404: // the product is not returnable at all
0405: returnable = false;
0406: }
0407:
0408: // next check support discontinuation
0409: if (product != null
0410: && product.get("supportDiscontinuationDate") != null
0411: && !UtilDateTime
0412: .nowTimestamp()
0413: .before(
0414: product
0415: .getTimestamp("supportDiscontinuationDate"))) {
0416: // support discontinued either now or in the past
0417: returnable = false;
0418: }
0419:
0420: String itemStatus = orderItem.getString("statusId");
0421: double orderQty = orderItem.getDouble("quantity").doubleValue();
0422: if (orderItem.getDouble("cancelQuantity") != null) {
0423: orderQty -= orderItem.getDouble("cancelQuantity")
0424: .doubleValue();
0425: }
0426:
0427: // get the returnable quantity
0428: double returnableQuantity = 0.00;
0429: if (returnable
0430: && (itemStatus.equals("ITEM_APPROVED") || itemStatus
0431: .equals("ITEM_COMPLETED"))) {
0432: List returnedItems = null;
0433: try {
0434: returnedItems = orderItem.getRelated("ReturnItem");
0435: } catch (GenericEntityException e) {
0436: Debug.logError(e, module);
0437: return ServiceUtil
0438: .returnError(UtilProperties
0439: .getMessage(
0440: resource_error,
0441: "OrderErrorUnableToGetReturnItemInformation",
0442: locale));
0443: }
0444: if (returnedItems == null || returnedItems.size() == 0) {
0445: returnableQuantity = orderQty;
0446: } else {
0447: double returnedQty = 0.00;
0448: Iterator ri = returnedItems.iterator();
0449: while (ri.hasNext()) {
0450: GenericValue returnItem = (GenericValue) ri.next();
0451: GenericValue returnHeader = null;
0452: try {
0453: returnHeader = returnItem
0454: .getRelatedOne("ReturnHeader");
0455: } catch (GenericEntityException e) {
0456: Debug.logError(e, module);
0457: return ServiceUtil
0458: .returnError(UtilProperties
0459: .getMessage(
0460: resource_error,
0461: "OrderErrorUnableToGetReturnHeaderFromItem",
0462: locale));
0463: }
0464: String returnStatus = returnHeader
0465: .getString("statusId");
0466: if (!returnStatus.equals("RETURN_CANCELLED")) {
0467: returnedQty += returnItem.getDouble(
0468: "returnQuantity").doubleValue();
0469: }
0470: }
0471: if (returnedQty < orderQty) {
0472: returnableQuantity = orderQty - returnedQty;
0473: }
0474: }
0475: }
0476:
0477: // get the returnable price now equals to orderItem.unitPrice, since adjustments are booked separately
0478:
0479: Map result = ServiceUtil.returnSuccess();
0480: result
0481: .put("returnableQuantity", new Double(
0482: returnableQuantity));
0483: result.put("returnablePrice", orderItem.get("unitPrice"));
0484: return result;
0485: }
0486:
0487: // get a map of returnable items (items not already returned) and quantities
0488: public static Map getReturnableItems(DispatchContext dctx,
0489: Map context) {
0490: LocalDispatcher dispatcher = dctx.getDispatcher();
0491: GenericDelegator delegator = dctx.getDelegator();
0492: String orderId = (String) context.get("orderId");
0493: Locale locale = (Locale) context.get("locale");
0494:
0495: GenericValue orderHeader = null;
0496: try {
0497: orderHeader = delegator.findByPrimaryKey("OrderHeader",
0498: UtilMisc.toMap("orderId", orderId));
0499: } catch (GenericEntityException e) {
0500: Debug.logError(e, module);
0501: return ServiceUtil.returnError(UtilProperties.getMessage(
0502: resource_error,
0503: "OrderErrorUnableToGetReturnItemInformation",
0504: locale));
0505: }
0506:
0507: Map returnable = new HashMap();
0508: if (orderHeader != null) {
0509:
0510: // OrderItems which have been issued may be returned.
0511: EntityConditionList whereConditions = new EntityConditionList(
0512: UtilMisc.toList(new EntityExpr("orderId",
0513: EntityOperator.EQUALS, orderHeader
0514: .getString("orderId")),
0515: new EntityExpr("orderItemStatusId",
0516: EntityOperator.IN, UtilMisc.toList(
0517: "ITEM_APPROVED",
0518: "ITEM_COMPLETED"))),
0519: EntityOperator.AND);
0520: EntityConditionList havingConditions = new EntityConditionList(
0521: UtilMisc
0522: .toList(new EntityExpr("quantityIssued",
0523: EntityOperator.GREATER_THAN,
0524: new Double(0))), EntityOperator.AND);
0525: List orderItemQuantitiesIssued = null;
0526: try {
0527: orderItemQuantitiesIssued = delegator.findByCondition(
0528: "OrderItemQuantityReportGroupByItem",
0529: whereConditions, havingConditions, UtilMisc
0530: .toList("orderId", "orderItemSeqId"),
0531: UtilMisc.toList("orderItemSeqId"), null);
0532: } catch (GenericEntityException e) {
0533: Debug.logError(e, module);
0534: return ServiceUtil
0535: .returnError(UtilProperties
0536: .getMessage(
0537: resource_error,
0538: "OrderErrorUnableToGetReturnHeaderFromItem",
0539: locale));
0540: }
0541: if (orderItemQuantitiesIssued != null) {
0542: Iterator i = orderItemQuantitiesIssued.iterator();
0543: while (i.hasNext()) {
0544: GenericValue orderItemQuantityIssued = (GenericValue) i
0545: .next();
0546: GenericValue item = null;
0547: try {
0548: item = orderItemQuantityIssued
0549: .getRelatedOne("OrderItem");
0550: } catch (GenericEntityException e) {
0551: Debug.logError(e, module);
0552: return ServiceUtil
0553: .returnError(UtilProperties
0554: .getMessage(
0555: resource_error,
0556: "OrderErrorUnableToGetOrderItemInformation",
0557: locale));
0558: }
0559: Map serviceResult = null;
0560: try {
0561: serviceResult = dispatcher.runSync(
0562: "getReturnableQuantity", UtilMisc
0563: .toMap("orderItem", item));
0564: } catch (GenericServiceException e) {
0565: Debug.logError(e, module);
0566: return ServiceUtil
0567: .returnError(UtilProperties
0568: .getMessage(
0569: resource_error,
0570: "OrderErrorUnableToGetTheItemReturnableQuantity",
0571: locale));
0572: }
0573: if (serviceResult
0574: .containsKey(ModelService.ERROR_MESSAGE)) {
0575: return ServiceUtil
0576: .returnError((String) serviceResult
0577: .get(ModelService.ERROR_MESSAGE));
0578: } else {
0579:
0580: // Don't add the OrderItem to the map of returnable OrderItems if there isn't any returnable quantity.
0581: if (((Double) serviceResult
0582: .get("returnableQuantity"))
0583: .doubleValue() == 0) {
0584: continue;
0585: }
0586: Map returnInfo = new HashMap();
0587: // first the return info (quantity/price)
0588: returnInfo
0589: .put(
0590: "returnableQuantity",
0591: serviceResult
0592: .get("returnableQuantity"));
0593: returnInfo.put("returnablePrice", serviceResult
0594: .get("returnablePrice"));
0595:
0596: // now the product type information
0597: String itemTypeKey = "FINISHED_GOOD"; // default item type (same as invoice)
0598: GenericValue product = null;
0599: if (item.get("productId") != null) {
0600: try {
0601: product = item.getRelatedOne("Product");
0602: } catch (GenericEntityException e) {
0603: Debug.logError(e, module);
0604: return ServiceUtil
0605: .returnError("Unable to obtain order item information!");
0606: }
0607: }
0608: if (product != null) {
0609: itemTypeKey = product
0610: .getString("productTypeId");
0611: } else if (item != null
0612: && item.getString("orderItemTypeId") != null) {
0613: itemTypeKey = item
0614: .getString("orderItemTypeId");
0615: }
0616: returnInfo.put("itemTypeKey", itemTypeKey);
0617:
0618: returnable.put(item, returnInfo);
0619: }
0620: }
0621: } else {
0622: return ServiceUtil.returnError(UtilProperties
0623: .getMessage(resource_error,
0624: "OrderErrorNoOrderItemsFound", locale));
0625: }
0626: } else {
0627: return ServiceUtil.returnError(UtilProperties.getMessage(
0628: resource_error,
0629: "OrderErrorUnableToFindOrderHeader", locale));
0630: }
0631:
0632: Map result = ServiceUtil.returnSuccess();
0633: result.put("returnableItems", returnable);
0634: return result;
0635: }
0636:
0637: // check return items status and update return header status
0638: public static Map checkReturnComplete(DispatchContext dctx,
0639: Map context) {
0640: LocalDispatcher dispatcher = dctx.getDispatcher();
0641: GenericDelegator delegator = dctx.getDelegator();
0642: String returnId = (String) context.get("returnId");
0643: Locale locale = (Locale) context.get("locale");
0644: GenericValue userLogin = (GenericValue) context
0645: .get("userLogin");
0646:
0647: GenericValue returnHeader = null;
0648: List returnItems = null;
0649:
0650: try {
0651: returnHeader = delegator.findByPrimaryKey("ReturnHeader",
0652: UtilMisc.toMap("returnId", returnId));
0653: if (returnHeader != null) {
0654: returnItems = returnHeader.getRelated("ReturnItem");
0655: }
0656: } catch (GenericEntityException e) {
0657: Debug.logError(e, "Problems looking up return information",
0658: module);
0659: return ServiceUtil.returnError(UtilProperties.getMessage(
0660: resource_error,
0661: "OrderErrorGettingReturnHeaderItemInformation",
0662: locale));
0663: }
0664:
0665: // if already completed just return
0666: if (returnHeader != null
0667: && returnHeader.get("statusId") != null) {
0668: String currentStatus = returnHeader.getString("statusId");
0669: if ("RETURN_COMPLETED".equals(currentStatus)
0670: || "RETURN_CANCELLED".equals(currentStatus)) {
0671: return ServiceUtil.returnSuccess();
0672: }
0673: }
0674:
0675: // statusId resulting from the run of this service, which will be modified if we set it to completed
0676: String resultStatusId = returnHeader.getString("statusId");
0677:
0678: // now; to be used for all timestamps
0679: Timestamp now = UtilDateTime.nowTimestamp();
0680:
0681: List completedItems = new ArrayList();
0682: if (returnHeader != null && returnItems != null
0683: && returnItems.size() > 0) {
0684: Iterator itemsIter = returnItems.iterator();
0685: while (itemsIter.hasNext()) {
0686: GenericValue item = (GenericValue) itemsIter.next();
0687: String itemStatus = item != null ? item
0688: .getString("statusId") : null;
0689: if (itemStatus != null) {
0690: // both completed and cancelled items qualify for completed status change
0691: if ("RETURN_COMPLETED".equals(itemStatus)
0692: || "RETURN_CANCELLED".equals(itemStatus)) {
0693: completedItems.add(item);
0694: }
0695: }
0696: }
0697:
0698: // if all items are completed/cancelled these should match
0699: if (completedItems.size() == returnItems.size()) {
0700: resultStatusId = "RETURN_COMPLETED";
0701:
0702: // create the status change history and set it to be stored
0703: String returnStatusId = delegator
0704: .getNextSeqId("ReturnStatus");
0705: GenericValue returnStatus = delegator.makeValue(
0706: "ReturnStatus", UtilMisc.toMap(
0707: "returnStatusId", returnStatusId));
0708: returnStatus.set("statusId", resultStatusId);
0709: returnStatus.set("returnId", returnId);
0710: returnStatus.set("statusDatetime", now);
0711: try {
0712: returnStatus.create();
0713: } catch (GenericEntityException e) {
0714: Debug.logError(e, module);
0715: return ServiceUtil
0716: .returnError(UtilProperties
0717: .getMessage(
0718: resource_error,
0719: "OrderErrorUnableToCreateReturnStatusHistory",
0720: locale));
0721: }
0722:
0723: // update the return header to completed
0724: try {
0725: Map tmpResult = dispatcher.runSync(
0726: "updateReturnHeader", UtilMisc.toMap(
0727: "returnId", returnId, "statusId",
0728: resultStatusId, "userLogin",
0729: userLogin));
0730:
0731: if (ServiceUtil.isError(tmpResult)) {
0732: return tmpResult;
0733: }
0734: } catch (GenericServiceException ex) {
0735: return ServiceUtil.returnError(ex.getMessage());
0736: }
0737: }
0738:
0739: }
0740:
0741: Map result = ServiceUtil.returnSuccess();
0742: result.put("statusId", resultStatusId);
0743: return result;
0744: }
0745:
0746: // credit (billingAccount) return
0747: public static Map processCreditReturn(DispatchContext dctx,
0748: Map context) {
0749: LocalDispatcher dispatcher = dctx.getDispatcher();
0750: GenericDelegator delegator = dctx.getDelegator();
0751: String returnId = (String) context.get("returnId");
0752: GenericValue userLogin = (GenericValue) context
0753: .get("userLogin");
0754: Locale locale = (Locale) context.get("locale");
0755:
0756: GenericValue returnHeader = null;
0757: List returnItems = null;
0758: try {
0759: returnHeader = delegator.findByPrimaryKey("ReturnHeader",
0760: UtilMisc.toMap("returnId", returnId));
0761: if (returnHeader != null) {
0762: returnItems = returnHeader.getRelatedByAnd(
0763: "ReturnItem", UtilMisc.toMap("returnTypeId",
0764: "RTN_CREDIT"));
0765: }
0766: } catch (GenericEntityException e) {
0767: Debug.logError(e, "Problems looking up return information",
0768: module);
0769: return ServiceUtil.returnError(UtilProperties.getMessage(
0770: resource_error,
0771: "OrderErrorGettingReturnHeaderItemInformation",
0772: locale));
0773: }
0774:
0775: if (returnHeader != null && returnItems != null
0776: && returnItems.size() > 0) {
0777: String billingAccountId = returnHeader
0778: .getString("billingAccountId");
0779: String fromPartyId = returnHeader.getString("fromPartyId");
0780: String toPartyId = returnHeader.getString("toPartyId");
0781:
0782: // make sure total refunds on a return don't exceed amount of returned orders
0783: Map serviceResult = null;
0784: try {
0785: serviceResult = dispatcher.runSync(
0786: "checkPaymentAmountForRefund", UtilMisc.toMap(
0787: "returnId", returnId));
0788: } catch (GenericServiceException e) {
0789: Debug
0790: .logError(
0791: e,
0792: "Problem running the checkPaymentAmountForRefund service",
0793: module);
0794: return ServiceUtil
0795: .returnError(UtilProperties
0796: .getMessage(
0797: resource_error,
0798: "OrderProblemsWithCheckPaymentAmountForRefund",
0799: locale));
0800: }
0801: if (ServiceUtil.isError(serviceResult)) {
0802: return ServiceUtil.returnError(ServiceUtil
0803: .getErrorMessage(serviceResult));
0804: }
0805: if (billingAccountId == null) {
0806: // create new BillingAccount w/ 0 balance
0807: Map results = createBillingAccountFromReturn(
0808: returnHeader, returnItems, dctx, context);
0809: if (ServiceUtil.isError(results)) {
0810: Debug.logError("Error creating BillingAccount: "
0811: + results.get(ModelService.ERROR_MESSAGE),
0812: module);
0813: return ServiceUtil
0814: .returnError(UtilProperties
0815: .getMessage(
0816: resource_error,
0817: "OrderErrorWithCreateBillingAccount",
0818: locale)
0819: + results
0820: .get(ModelService.ERROR_MESSAGE));
0821: }
0822: billingAccountId = (String) results
0823: .get("billingAccountId");
0824: }
0825:
0826: // double check; make sure we have a billingAccount
0827: if (billingAccountId == null) {
0828: Debug
0829: .logError(
0830: "No available billing account, none was created",
0831: module);
0832: return ServiceUtil.returnError(UtilProperties
0833: .getMessage(resource_error,
0834: "OrderNoAvailableBillingAccount",
0835: locale));
0836: }
0837:
0838: // now; to be used for all timestamps
0839: Timestamp now = UtilDateTime.nowTimestamp();
0840:
0841: // first, compute the total credit from the return items
0842: BigDecimal creditTotal = ZERO;
0843: for (Iterator itemsIter = returnItems.iterator(); itemsIter
0844: .hasNext();) {
0845: GenericValue item = (GenericValue) itemsIter.next();
0846: BigDecimal quantity = item
0847: .getBigDecimal("returnQuantity");
0848: BigDecimal price = item.getBigDecimal("returnPrice");
0849: if (quantity == null)
0850: quantity = ZERO;
0851: if (price == null)
0852: price = ZERO;
0853: creditTotal = creditTotal.add(price.multiply(quantity)
0854: .setScale(decimals, rounding));
0855: }
0856:
0857: // add the adjustments to the total
0858: BigDecimal adjustments = new BigDecimal(
0859: getReturnAdjustmentTotal(delegator, UtilMisc.toMap(
0860: "returnId", returnId)));
0861: creditTotal = creditTotal.add(adjustments.setScale(
0862: decimals, rounding));
0863:
0864: // create a Payment record for this credit; will look just like a normal payment
0865: // However, since this payment is not a DISBURSEMENT or RECEIPT but really a matter of internal record
0866: // it is of type "Other (Non-posting)"
0867: String paymentId = delegator.getNextSeqId("Payment");
0868: GenericValue payment = delegator.makeValue("Payment",
0869: UtilMisc.toMap("paymentId", paymentId));
0870: payment.set("paymentTypeId", "CUSTOMER_REFUND");
0871: payment.set("paymentMethodTypeId", "EXT_BILLACT");
0872: payment.set("partyIdFrom", toPartyId); // if you receive a return FROM someone, then you'd have to give a return TO that person
0873: payment.set("partyIdTo", fromPartyId);
0874: payment.set("effectiveDate", now);
0875: payment.set("amount", creditTotal);
0876: payment.set("comments", "Return Credit");
0877: payment.set("statusId", "PMNT_CONFIRMED"); // set the status to confirmed so nothing else can happen to the payment
0878: try {
0879: delegator.create(payment);
0880: } catch (GenericEntityException e) {
0881: Debug.logError(e, "Problem creating Payment record",
0882: module);
0883: return ServiceUtil.returnError(UtilProperties
0884: .getMessage(resource_error,
0885: "OrderProblemCreatingPaymentRecord",
0886: locale));
0887: }
0888:
0889: // create a return item response
0890: Map itemResponse = UtilMisc.toMap("paymentId", paymentId);
0891: itemResponse.put("billingAccountId", billingAccountId);
0892: itemResponse.put("responseAmount", new Double(creditTotal
0893: .doubleValue()));
0894: itemResponse.put("responseDate", now);
0895: itemResponse.put("userLogin", userLogin);
0896: Map serviceResults = null;
0897: try {
0898: serviceResults = dispatcher.runSync(
0899: "createReturnItemResponse", itemResponse);
0900: if (ServiceUtil.isError(serviceResults)) {
0901: return ServiceUtil
0902: .returnError(
0903: "Could not create ReturnItemResponse record",
0904: null, null, serviceResults);
0905: }
0906: } catch (GenericServiceException e) {
0907: Debug.logError(e,
0908: "Problem creating ReturnItemResponse record",
0909: module);
0910: return ServiceUtil
0911: .returnError(UtilProperties
0912: .getMessage(
0913: resource_error,
0914: "OrderProblemCreatingReturnItemResponseRecord",
0915: locale));
0916: }
0917:
0918: // the resulting response ID will be associated with the return items
0919: String itemResponseId = (String) serviceResults
0920: .get("returnItemResponseId");
0921:
0922: // loop through the items again to update them and store a status change history
0923: List toBeStored = new ArrayList();
0924: for (Iterator itemsIter = returnItems.iterator(); itemsIter
0925: .hasNext();) {
0926: GenericValue item = (GenericValue) itemsIter.next();
0927:
0928: // set the response on the item and flag the item to be stored
0929: item.set("returnItemResponseId", itemResponseId);
0930: item.set("statusId", "RETURN_COMPLETED");
0931: toBeStored.add(item);
0932:
0933: // create the status change history and set it to be stored
0934: String returnStatusId = delegator
0935: .getNextSeqId("ReturnStatus");
0936: GenericValue returnStatus = delegator.makeValue(
0937: "ReturnStatus", UtilMisc.toMap(
0938: "returnStatusId", returnStatusId));
0939: returnStatus.set("statusId", item.get("statusId"));
0940: returnStatus.set("returnId", item.get("returnId"));
0941: returnStatus.set("returnItemSeqId", item
0942: .get("returnItemSeqId"));
0943: returnStatus.set("statusDatetime", now);
0944: toBeStored.add(returnStatus);
0945: }
0946:
0947: // store the item changes (attached responseId)
0948: try {
0949: delegator.storeAll(toBeStored);
0950: } catch (GenericEntityException e) {
0951: Debug.logError(e, "Problem storing ReturnItem updates",
0952: module);
0953: return ServiceUtil.returnError(UtilProperties
0954: .getMessage(resource_error,
0955: "OrderProblemStoringReturnItemUpdates",
0956: locale));
0957: }
0958:
0959: // create the PaymentApplication for the billing account
0960: String paId = delegator.getNextSeqId("PaymentApplication");
0961: GenericValue pa = delegator.makeValue("PaymentApplication",
0962: UtilMisc.toMap("paymentApplicationId", paId));
0963: pa.set("paymentId", paymentId);
0964: pa.set("billingAccountId", billingAccountId);
0965: pa.set("amountApplied", creditTotal);
0966: try {
0967: delegator.create(pa);
0968: } catch (GenericEntityException e) {
0969: Debug
0970: .logError(
0971: e,
0972: "Problem creating PaymentApplication record for billing account",
0973: module);
0974: return ServiceUtil
0975: .returnError(UtilProperties
0976: .getMessage(
0977: resource_error,
0978: "OrderProblemCreatingPaymentApplicationRecord",
0979: locale));
0980: }
0981:
0982: // create the payment applications for the return invoice
0983: try {
0984: serviceResults = dispatcher
0985: .runSync(
0986: "createPaymentApplicationsFromReturnItemResponse",
0987: UtilMisc.toMap("returnItemResponseId",
0988: itemResponseId, "userLogin",
0989: userLogin));
0990: if (ServiceUtil.isError(serviceResults)) {
0991: return ServiceUtil
0992: .returnError(
0993: UtilProperties
0994: .getMessage(
0995: resource_error,
0996: "OrderProblemCreatingPaymentApplicationRecord",
0997: locale), null,
0998: null, serviceResults);
0999: }
1000: } catch (GenericServiceException e) {
1001: Debug
1002: .logError(
1003: e,
1004: "Problem creating PaymentApplication records for return invoice",
1005: module);
1006: return ServiceUtil
1007: .returnError(UtilProperties
1008: .getMessage(
1009: resource_error,
1010: "OrderProblemCreatingPaymentApplicationRecord",
1011: locale));
1012: }
1013: }
1014:
1015: return ServiceUtil.returnSuccess();
1016: }
1017:
1018: /**
1019: * Helper method to generate a BillingAccount (store credit) from a return
1020: * header. This method takes care of all business logic relating to
1021: * the initialization of a Billing Account from the Return data.
1022: *
1023: * The BillingAccount.thruDate will be set to (now +
1024: * ProductStore.storeCreditValidDays + end of day). The product stores
1025: * are obtained via the return orders, and the minimum storeCreditValidDays
1026: * will be used. The default is to set thruDate to null, which implies no
1027: * expiration.
1028: *
1029: * Note that we set BillingAccount.accountLimit to 0.0 for store credits.
1030: * This is because the available balance of BillingAccounts is
1031: * calculated as accountLimit + sum of Payments - sum of Invoices.
1032: */
1033: private static Map createBillingAccountFromReturn(
1034: GenericValue returnHeader, List returnItems,
1035: DispatchContext dctx, Map context) {
1036: LocalDispatcher dispatcher = dctx.getDispatcher();
1037: GenericValue userLogin = (GenericValue) context
1038: .get("userLogin");
1039: Locale locale = (Locale) context.get("locale");
1040:
1041: try {
1042: // get the related product stores via the orders related to this return
1043: List orders = EntityUtil.getRelated("OrderHeader",
1044: returnItems);
1045: List productStores = EntityUtil.getRelated("ProductStore",
1046: orders);
1047:
1048: // find the minimum storeCreditValidDays of all the ProductStores associated with all the Orders on the Return, skipping null ones
1049: Long storeCreditValidDays = null;
1050: for (Iterator iter = productStores.iterator(); iter
1051: .hasNext();) {
1052: GenericValue productStore = (GenericValue) iter.next();
1053: Long this StoreValidDays = productStore
1054: .getLong("storeCreditValidDays");
1055: if (this StoreValidDays == null)
1056: continue;
1057:
1058: if (storeCreditValidDays == null) {
1059: storeCreditValidDays = this StoreValidDays;
1060: } else if (this StoreValidDays
1061: .compareTo(storeCreditValidDays) < 0) {
1062: // if this store's days < store credit valid days, use this store's days
1063: storeCreditValidDays = this StoreValidDays;
1064: }
1065: }
1066:
1067: // if there is a storeCreditValidDays, set the thruDate to (nowTimestamp + storeCreditValidDays + end of day)
1068: Timestamp thruDate = null;
1069: if (storeCreditValidDays != null)
1070: thruDate = UtilDateTime.getDayEnd(UtilDateTime
1071: .nowTimestamp(), storeCreditValidDays
1072: .intValue());
1073:
1074: // create the billing account
1075: Map input = UtilMisc.toMap("accountLimit",
1076: new Double(0.00), "description",
1077: "Credit Account for Return #"
1078: + returnHeader.get("returnId"),
1079: "userLogin", userLogin);
1080: input.put("accountCurrencyUomId", returnHeader
1081: .get("currencyUomId"));
1082: input.put("thruDate", thruDate);
1083: Map results = dispatcher.runSync("createBillingAccount",
1084: input);
1085: if (ServiceUtil.isError(results))
1086: return results;
1087: String billingAccountId = (String) results
1088: .get("billingAccountId");
1089:
1090: // set the role on the account
1091: input = UtilMisc.toMap("billingAccountId",
1092: billingAccountId, "partyId", returnHeader
1093: .get("fromPartyId"), "roleTypeId",
1094: "BILL_TO_CUSTOMER", "userLogin", userLogin);
1095: Map roleResults = dispatcher.runSync(
1096: "createBillingAccountRole", input);
1097: if (ServiceUtil.isError(roleResults)) {
1098: Debug.logError("Error with createBillingAccountRole: "
1099: + roleResults.get(ModelService.ERROR_MESSAGE),
1100: module);
1101: return ServiceUtil
1102: .returnError(UtilProperties
1103: .getMessage(
1104: resource_error,
1105: "OrderErrorWithCreateBillingAccountRole",
1106: locale)
1107: + roleResults
1108: .get(ModelService.ERROR_MESSAGE));
1109: }
1110:
1111: return results;
1112: } catch (GenericEntityException e) {
1113: Debug.logError(e,
1114: "Entity error when creating BillingAccount: "
1115: + e.getMessage(), module);
1116: return ServiceUtil.returnError(UtilProperties.getMessage(
1117: resource_error,
1118: "OrderProblemsCreatingBillingAccount", locale));
1119: } catch (GenericServiceException e) {
1120: Debug.logError(e,
1121: "Service error when creating BillingAccount: "
1122: + e.getMessage(), module);
1123: return ServiceUtil.returnError(UtilProperties.getMessage(
1124: resource_error,
1125: "OrderProblemsCreatingBillingAccount", locale));
1126: }
1127: }
1128:
1129: // refund (cash/charge) return
1130: //TODO add adjustment total
1131: public static Map processRefundReturn(DispatchContext dctx,
1132: Map context) {
1133: GenericDelegator delegator = dctx.getDelegator();
1134: LocalDispatcher dispatcher = dctx.getDispatcher();
1135: String returnId = (String) context.get("returnId");
1136: GenericValue userLogin = (GenericValue) context
1137: .get("userLogin");
1138: Locale locale = (Locale) context.get("locale");
1139:
1140: GenericValue returnHeader = null;
1141: List returnItems = null;
1142: try {
1143: returnHeader = delegator.findByPrimaryKey("ReturnHeader",
1144: UtilMisc.toMap("returnId", returnId));
1145: if (returnHeader != null) {
1146: returnItems = returnHeader.getRelatedByAnd(
1147: "ReturnItem", UtilMisc.toMap("returnTypeId",
1148: "RTN_REFUND"));
1149: }
1150: } catch (GenericEntityException e) {
1151: Debug.logError(e, "Problems looking up return information",
1152: module);
1153: return ServiceUtil.returnError(UtilProperties.getMessage(
1154: resource_error,
1155: "OrderErrorGettingReturnHeaderItemInformation",
1156: locale));
1157: }
1158:
1159: if (returnHeader != null && returnItems != null
1160: && returnItems.size() > 0) {
1161: Map itemsByOrder = new HashMap();
1162: Map totalByOrder = new HashMap();
1163:
1164: // make sure total refunds on a return don't exceed amount of returned orders
1165: Map serviceResult = null;
1166: try {
1167: serviceResult = dispatcher.runSync(
1168: "checkPaymentAmountForRefund", UtilMisc.toMap(
1169: "returnId", returnId));
1170: } catch (GenericServiceException e) {
1171: Debug
1172: .logError(
1173: e,
1174: "Problem running the checkPaymentAmountForRefund service",
1175: module);
1176: return ServiceUtil
1177: .returnError(UtilProperties
1178: .getMessage(
1179: resource_error,
1180: "OrderProblemsWithCheckPaymentAmountForRefund",
1181: locale));
1182: }
1183: if (ServiceUtil.isError(serviceResult)) {
1184: return ServiceUtil.returnError(ServiceUtil
1185: .getErrorMessage(serviceResult));
1186: }
1187:
1188: groupReturnItemsByOrder(returnItems, itemsByOrder,
1189: totalByOrder, delegator, returnId);
1190:
1191: // process each one by order
1192: Set itemSet = itemsByOrder.entrySet();
1193: Iterator itemByOrderIt = itemSet.iterator();
1194: while (itemByOrderIt.hasNext()) {
1195: Map.Entry entry = (Map.Entry) itemByOrderIt.next();
1196: String orderId = (String) entry.getKey();
1197: List items = (List) entry.getValue();
1198: Double orderTotal = (Double) totalByOrder.get(orderId);
1199:
1200: // get order header & payment prefs
1201: GenericValue orderHeader = null;
1202: List orderPayPrefs = null;
1203: try {
1204: orderHeader = delegator.findByPrimaryKey(
1205: "OrderHeader", UtilMisc.toMap("orderId",
1206: orderId));
1207: // sort these desending by maxAmount
1208: orderPayPrefs = orderHeader.getRelated(
1209: "OrderPaymentPreference", UtilMisc.toMap(
1210: "statusId", "PAYMENT_SETTLED"),
1211: UtilMisc.toList("-maxAmount"));
1212: } catch (GenericEntityException e) {
1213: Debug.logError(e, "Cannot get Order details for #"
1214: + orderId, module);
1215: continue;
1216: }
1217: OrderReadHelper orderReadHelper = new OrderReadHelper(
1218: delegator, orderId);
1219:
1220: // now; for all timestamps
1221: Timestamp now = UtilDateTime.nowTimestamp();
1222:
1223: // Assemble a map of orderPaymentPreferenceId -> list of maps of ( OPP and availableAmountForRefunding )
1224: // where availableAmountForRefunding = receivedAmount - alreadyRefundedAmount
1225: // We break the OPPs down this way because we need to process the refunds to payment methods in a particular order
1226: Map receivedPaymentTotalsByPaymentMethod = orderReadHelper
1227: .getReceivedPaymentTotalsByPaymentMethod();
1228: Map refundedTotalsByPaymentMethod = orderReadHelper
1229: .getReturnedTotalsByPaymentMethod();
1230:
1231: /*
1232: * Go through the OrderPaymentPreferences and determine how much remains to be refunded for each.
1233: * Then group these refund amounts and orderPaymentPreferences by paymentMethodTypeId. That is,
1234: * the intent is to get the refundable amounts per orderPaymentPreference, grouped by payment method type.
1235: */
1236: Map prefSplitMap = new HashMap();
1237: Iterator oppit = orderPayPrefs.iterator();
1238: while (oppit.hasNext()) {
1239: GenericValue orderPayPref = (GenericValue) oppit
1240: .next();
1241: String paymentMethodTypeId = orderPayPref
1242: .getString("paymentMethodTypeId");
1243: String orderPayPrefKey = orderPayPref
1244: .getString("paymentMethodId") != null ? orderPayPref
1245: .getString("paymentMethodId")
1246: : orderPayPref
1247: .getString("paymentMethodTypeId");
1248:
1249: // See how much we can refund to the payment method
1250: BigDecimal orderPayPrefReceivedTotal = ZERO;
1251: if (receivedPaymentTotalsByPaymentMethod
1252: .containsKey(orderPayPrefKey)) {
1253: orderPayPrefReceivedTotal = orderPayPrefReceivedTotal
1254: .add(new BigDecimal(
1255: ((Double) receivedPaymentTotalsByPaymentMethod
1256: .get(orderPayPrefKey))
1257: .doubleValue())
1258: .setScale(decimals, rounding));
1259: }
1260: BigDecimal orderPayPrefRefundedTotal = ZERO;
1261: if (refundedTotalsByPaymentMethod
1262: .containsKey(orderPayPrefKey)) {
1263: orderPayPrefRefundedTotal = orderPayPrefRefundedTotal
1264: .add(new BigDecimal(
1265: ((Double) refundedTotalsByPaymentMethod
1266: .get(orderPayPrefKey))
1267: .doubleValue())
1268: .setScale(decimals, rounding));
1269: }
1270: BigDecimal orderPayPrefAvailableTotal = orderPayPrefReceivedTotal
1271: .subtract(orderPayPrefRefundedTotal);
1272:
1273: // add the refundable amount and orderPaymentPreference to the paymentMethodTypeId map
1274: if (orderPayPrefAvailableTotal.compareTo(ZERO) == 1) {
1275: Map orderPayPrefDetails = new HashMap();
1276: orderPayPrefDetails.put(
1277: "orderPaymentPreference", orderPayPref);
1278: orderPayPrefDetails.put("availableTotal",
1279: orderPayPrefAvailableTotal);
1280: if (prefSplitMap
1281: .containsKey(paymentMethodTypeId)) {
1282: ((List) prefSplitMap
1283: .get(paymentMethodTypeId))
1284: .add(orderPayPrefDetails);
1285: } else {
1286: prefSplitMap
1287: .put(
1288: paymentMethodTypeId,
1289: UtilMisc
1290: .toList(orderPayPrefDetails));
1291: }
1292: }
1293: }
1294:
1295: // Keep a decreasing total of the amount remaining to refund
1296: BigDecimal amountLeftToRefund = new BigDecimal(
1297: orderTotal.doubleValue()).setScale(decimals,
1298: rounding);
1299:
1300: // This can be extended to support additional electronic types
1301: List electronicTypes = UtilMisc.toList("CREDIT_CARD",
1302: "EFT_ACCOUNT", "GIFT_CARD");
1303:
1304: // This defines the ordered part of the sequence of refund processing
1305: List orderedRefundPaymentMethodTypes = new ArrayList();
1306: orderedRefundPaymentMethodTypes.add("EXT_BILLACT");
1307: orderedRefundPaymentMethodTypes.add("GIFT_CARD");
1308: orderedRefundPaymentMethodTypes.add("CREDIT_CARD");
1309:
1310: // Add all the other paymentMethodTypes, in no particular order
1311: EntityConditionList pmtConditionList = new EntityConditionList(
1312: UtilMisc.toList(new EntityExpr(
1313: "paymentMethodTypeId",
1314: EntityOperator.NOT_IN,
1315: orderedRefundPaymentMethodTypes)),
1316: EntityOperator.AND);
1317: List otherPaymentMethodTypes = new ArrayList();
1318: try {
1319: otherPaymentMethodTypes = delegator
1320: .findByConditionCache("PaymentMethodType",
1321: pmtConditionList, null, null);
1322: } catch (GenericEntityException e) {
1323: Debug.logError(e, "Cannot get PaymentMethodTypes",
1324: module);
1325: return ServiceUtil
1326: .returnError("Problems getting PaymentMethodTypes: "
1327: + e.toString());
1328: }
1329: orderedRefundPaymentMethodTypes.addAll(EntityUtil
1330: .getFieldListFromEntityList(
1331: otherPaymentMethodTypes,
1332: "paymentMethodTypeId", true));
1333:
1334: // Iterate through the specified sequence of paymentMethodTypes, refunding to the correct OrderPaymentPreferences
1335: // as long as there's a positive amount remaining to refund
1336: Iterator orpmtit = orderedRefundPaymentMethodTypes
1337: .iterator();
1338: while (orpmtit.hasNext()
1339: && amountLeftToRefund.compareTo(ZERO) == 1) {
1340: String paymentMethodTypeId = (String) orpmtit
1341: .next();
1342: if (prefSplitMap.containsKey(paymentMethodTypeId)) {
1343: List paymentMethodDetails = (List) prefSplitMap
1344: .get(paymentMethodTypeId);
1345:
1346: // Iterate through the OrderPaymentPreferences of this type
1347: Iterator pmtppit = paymentMethodDetails
1348: .iterator();
1349: while (pmtppit.hasNext()
1350: && amountLeftToRefund.compareTo(ZERO) == 1) {
1351: Map orderPaymentPrefDetails = (Map) pmtppit
1352: .next();
1353: GenericValue orderPaymentPreference = (GenericValue) orderPaymentPrefDetails
1354: .get("orderPaymentPreference");
1355: BigDecimal orderPaymentPreferenceAvailable = (BigDecimal) orderPaymentPrefDetails
1356: .get("availableTotal");
1357:
1358: // Refund up to the maxAmount for the paymentPref, or whatever is left to refund if that's less than the maxAmount
1359: BigDecimal amountToRefund = orderPaymentPreferenceAvailable
1360: .min(amountLeftToRefund);
1361:
1362: String paymentId = null;
1363:
1364: // Call the refund service to refund the payment
1365: if (electronicTypes
1366: .contains(paymentMethodTypeId)) {
1367: try {
1368: // for electronic types such as CREDIT_CARD and EFT_ACCOUNT, use refundPayment service
1369: serviceResult = dispatcher
1370: .runSync(
1371: "refundPayment",
1372: UtilMisc
1373: .toMap(
1374: "orderPaymentPreference",
1375: orderPaymentPreference,
1376: "refundAmount",
1377: new Double(
1378: amountToRefund
1379: .setScale(
1380: decimals,
1381: rounding)
1382: .doubleValue()),
1383: "userLogin",
1384: userLogin));
1385: if (ServiceUtil
1386: .isError(serviceResult)
1387: || ServiceUtil
1388: .isFailure(serviceResult)) {
1389: Debug
1390: .logError(
1391: "Error in refund payment: "
1392: + ServiceUtil
1393: .getErrorMessage(serviceResult),
1394: module);
1395: continue;
1396: }
1397: paymentId = (String) serviceResult
1398: .get("paymentId");
1399: } catch (GenericServiceException e) {
1400: Debug
1401: .logError(
1402: e,
1403: "Problem running the refundPayment service",
1404: module);
1405: return ServiceUtil
1406: .returnError(UtilProperties
1407: .getMessage(
1408: resource_error,
1409: "OrderProblemsWithTheRefundSeeLogs",
1410: locale));
1411: }
1412: } else if (paymentMethodTypeId
1413: .equals("EXT_BILLACT")) {
1414: try {
1415: // for Billing Account refunds
1416: serviceResult = dispatcher
1417: .runSync(
1418: "refundBillingAccountPayment",
1419: UtilMisc
1420: .toMap(
1421: "orderPaymentPreference",
1422: orderPaymentPreference,
1423: "refundAmount",
1424: new Double(
1425: amountToRefund
1426: .setScale(
1427: decimals,
1428: rounding)
1429: .doubleValue()),
1430: "userLogin",
1431: userLogin));
1432: if (ServiceUtil
1433: .isError(serviceResult)
1434: || ServiceUtil
1435: .isFailure(serviceResult)) {
1436: Debug
1437: .logError(
1438: "Error in refund payment: "
1439: + ServiceUtil
1440: .getErrorMessage(serviceResult),
1441: module);
1442: continue;
1443: }
1444: paymentId = (String) serviceResult
1445: .get("paymentId");
1446: } catch (GenericServiceException e) {
1447: Debug
1448: .logError(
1449: e,
1450: "Problem running the refundPayment service",
1451: module);
1452: return ServiceUtil
1453: .returnError(UtilProperties
1454: .getMessage(
1455: resource_error,
1456: "OrderProblemsWithTheRefundSeeLogs",
1457: locale));
1458: }
1459: } else {
1460: // TODO: handle manual refunds (accounts payable)
1461: }
1462:
1463: // Fill out the data for the new ReturnItemResponse
1464: Map response = FastMap.newInstance();
1465: response
1466: .put(
1467: "orderPaymentPreferenceId",
1468: orderPaymentPreference
1469: .getString("orderPaymentPreferenceId"));
1470: response.put("responseAmount", new Double(
1471: amountToRefund.setScale(decimals,
1472: rounding).doubleValue()));
1473: response.put("responseDate", now);
1474: response.put("userLogin", userLogin);
1475: if (paymentId != null) {
1476: // A null payment ID means no electronic refund was available; manual refund needed
1477: response.put("paymentId", paymentId);
1478: }
1479: if (paymentMethodTypeId
1480: .equals("EXT_BILLACT")) {
1481: response
1482: .put(
1483: "billingAccountId",
1484: orderReadHelper
1485: .getBillingAccount()
1486: .getString(
1487: "billingAccountId"));
1488: }
1489: Map serviceResults = null;
1490: try {
1491: serviceResults = dispatcher.runSync(
1492: "createReturnItemResponse",
1493: response);
1494: if (ServiceUtil.isError(serviceResults)) {
1495: return ServiceUtil
1496: .returnError(
1497: UtilProperties
1498: .getMessage(
1499: resource_error,
1500: "OrderProblemsCreatingReturnItemResponseEntity",
1501: locale),
1502: null, null,
1503: serviceResults);
1504: }
1505: } catch (GenericServiceException e) {
1506: Debug
1507: .logError(
1508: e,
1509: "Problems creating new ReturnItemResponse entity",
1510: module);
1511: return ServiceUtil
1512: .returnError(UtilProperties
1513: .getMessage(
1514: resource_error,
1515: "OrderProblemsCreatingReturnItemResponseEntity",
1516: locale));
1517: }
1518: String responseId = (String) serviceResults
1519: .get("returnItemResponseId");
1520:
1521: // Set the response on each item
1522: Iterator itemsIter = items.iterator();
1523: while (itemsIter.hasNext()) {
1524: GenericValue item = (GenericValue) itemsIter
1525: .next();
1526: item.set("returnItemResponseId",
1527: responseId);
1528: item
1529: .set("statusId",
1530: "RETURN_COMPLETED");
1531:
1532: // Create the status history
1533: String returnStatusId = delegator
1534: .getNextSeqId("ReturnStatus");
1535: GenericValue returnStatus = delegator
1536: .makeValue(
1537: "ReturnStatus",
1538: UtilMisc
1539: .toMap(
1540: "returnStatusId",
1541: returnStatusId));
1542: returnStatus.set("statusId", item
1543: .get("statusId"));
1544: returnStatus.set("returnId", item
1545: .get("returnId"));
1546: returnStatus.set("returnItemSeqId",
1547: item.get("returnItemSeqId"));
1548: returnStatus.set("statusDatetime", now);
1549:
1550: //Debug.log("Updating item status", module);
1551: try {
1552: item.store();
1553: delegator.create(returnStatus);
1554: } catch (GenericEntityException e) {
1555: Debug
1556: .logError(
1557: "Problem updating the ReturnItem entity",
1558: module);
1559: return ServiceUtil
1560: .returnError(UtilProperties
1561: .getMessage(
1562: resource_error,
1563: "OrderProblemUpdatingReturnItemReturnItemResponseId",
1564: locale));
1565: }
1566:
1567: //Debug.log("Item status and return status history created", module);
1568: }
1569:
1570: // Create the payment applications for the return invoice
1571: try {
1572: serviceResults = dispatcher
1573: .runSync(
1574: "createPaymentApplicationsFromReturnItemResponse",
1575: UtilMisc
1576: .toMap(
1577: "returnItemResponseId",
1578: responseId,
1579: "userLogin",
1580: userLogin));
1581: if (ServiceUtil.isError(serviceResults)) {
1582: return ServiceUtil
1583: .returnError(
1584: UtilProperties
1585: .getMessage(
1586: resource_error,
1587: "OrderProblemUpdatingReturnItemReturnItemResponseId",
1588: locale),
1589: null, null,
1590: serviceResults);
1591: }
1592: } catch (GenericServiceException e) {
1593: Debug
1594: .logError(
1595: e,
1596: "Problem creating PaymentApplication records for return invoice",
1597: module);
1598: return ServiceUtil
1599: .returnError(UtilProperties
1600: .getMessage(
1601: resource_error,
1602: "OrderProblemUpdatingReturnItemReturnItemResponseId",
1603: locale));
1604: }
1605:
1606: // Update the amount necessary to refund
1607: amountLeftToRefund = amountLeftToRefund
1608: .subtract(amountToRefund);
1609: }
1610: }
1611: }
1612:
1613: // OFBIZ-459: Create a "filler" payment and return item response by hand for the remaining amount, note that this won't be applied to the invoice
1614: if (amountLeftToRefund.compareTo(ZERO) == 1) {
1615: try {
1616: Map input = UtilMisc.toMap("userLogin",
1617: userLogin, "amount", new Double(
1618: amountLeftToRefund
1619: .doubleValue()),
1620: "statusId", "PMNT_NOT_PAID");
1621: input.put("partyIdTo", returnHeader
1622: .get("fromPartyId"));
1623: input.put("partyIdFrom", returnHeader
1624: .get("toPartyId"));
1625: input.put("paymentTypeId", "CUSTOMER_REFUND");
1626: Map results = dispatcher.runSync(
1627: "createPayment", input);
1628: if (ServiceUtil.isError(results))
1629: return results;
1630:
1631: input = UtilMisc.toMap("userLogin", userLogin,
1632: "paymentId", results.get("paymentId"),
1633: "responseAmount", new Double(
1634: amountLeftToRefund
1635: .doubleValue()));
1636: results = dispatcher.runSync(
1637: "createReturnItemResponse", input);
1638: if (ServiceUtil.isError(results))
1639: return results;
1640: } catch (GenericServiceException e) {
1641: return ServiceUtil.returnError(e.getMessage());
1642: }
1643: }
1644: }
1645: }
1646:
1647: return ServiceUtil.returnSuccess();
1648: }
1649:
1650: public static Map refundBillingAccountPayment(DispatchContext dctx,
1651: Map context) {
1652: GenericDelegator delegator = dctx.getDelegator();
1653: LocalDispatcher dispatcher = dctx.getDispatcher();
1654: GenericValue userLogin = (GenericValue) context
1655: .get("userLogin");
1656:
1657: GenericValue paymentPref = (GenericValue) context
1658: .get("orderPaymentPreference");
1659: Double refundAmount = (Double) context.get("refundAmount");
1660:
1661: GenericValue orderHeader = null;
1662: try {
1663: orderHeader = paymentPref.getRelatedOne("OrderHeader");
1664: } catch (GenericEntityException e) {
1665: Debug
1666: .logError(
1667: e,
1668: "Cannot get OrderHeader from OrderPaymentPreference",
1669: module);
1670: return ServiceUtil
1671: .returnError("Problems getting OrderHeader from OrderPaymentPreference: "
1672: + e.toString());
1673: }
1674:
1675: OrderReadHelper orh = new OrderReadHelper(orderHeader);
1676:
1677: String payFromPartyId = orh.getBillFromParty().getString(
1678: "partyId");
1679: String payToPartyId = orh.getBillToParty().getString("partyId");
1680:
1681: // Create the PaymentGatewayResponse record
1682: String responseId = delegator
1683: .getNextSeqId("PaymentGatewayResponse");
1684: GenericValue response = delegator.makeValue(
1685: "PaymentGatewayResponse", null);
1686: response.set("paymentGatewayResponseId", responseId);
1687: response.set("paymentServiceTypeEnumId", "PRDS_PAY_REFUND");
1688: response.set("orderPaymentPreferenceId", paymentPref
1689: .get("orderPaymentPreferenceId"));
1690: response.set("paymentMethodTypeId", paymentPref
1691: .get("paymentMethodTypeId"));
1692: response.set("transCodeEnumId", "PGT_REFUND");
1693: response.set("amount", refundAmount);
1694: response.set("transactionDate", UtilDateTime.nowTimestamp());
1695: response.set("currencyUomId", orh.getCurrency());
1696: try {
1697: delegator.create(response);
1698: } catch (GenericEntityException e) {
1699: Debug.logError(e, module);
1700: return ServiceUtil
1701: .returnError("Unable to create PaymentGatewayResponse record");
1702: }
1703:
1704: // Create the Payment record (parties reversed)
1705: Map paymentCtx = UtilMisc.toMap("paymentTypeId",
1706: "CUSTOMER_REFUND");
1707: paymentCtx.put("paymentMethodTypeId", paymentPref
1708: .get("paymentMethodTypeId"));
1709: paymentCtx.put("paymentGatewayResponseId", responseId);
1710: paymentCtx.put("partyIdTo", payToPartyId);
1711: paymentCtx.put("partyIdFrom", payFromPartyId);
1712: paymentCtx.put("statusId", "PMNT_CONFIRMED");
1713: paymentCtx.put("paymentPreferenceId", paymentPref
1714: .get("orderPaymentPreferenceId"));
1715: paymentCtx.put("currencyUomId", orh.getCurrency());
1716: paymentCtx.put("amount", refundAmount);
1717: paymentCtx.put("userLogin", userLogin);
1718: paymentCtx.put("comments", "Refund");
1719:
1720: String paymentId = null;
1721: try {
1722: Map paymentCreationResult = dispatcher.runSync(
1723: "createPayment", paymentCtx);
1724: if (ServiceUtil.isError(paymentCreationResult)) {
1725: return paymentCreationResult;
1726: } else {
1727: paymentId = (String) paymentCreationResult
1728: .get("paymentId");
1729: }
1730: } catch (GenericServiceException e) {
1731: return ServiceUtil.returnError("Problem creating Payment "
1732: + e.getMessage());
1733: }
1734:
1735: if (paymentId == null) {
1736: return ServiceUtil.returnError("Create payment failed");
1737: }
1738:
1739: // 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
1740: // thus returning value to the billing account
1741: if ("EXT_BILLACT".equals(paymentPref
1742: .getString("paymentMethodTypeId"))) {
1743: GenericValue billingAccount = orh.getBillingAccount();
1744: if (UtilValidate.isNotEmpty(billingAccount
1745: .getString("billingAccountId"))) {
1746: try {
1747: Map paymentApplResult = dispatcher
1748: .runSync(
1749: "createPaymentApplication",
1750: UtilMisc
1751: .toMap(
1752: "paymentId",
1753: paymentId,
1754: "billingAccountId",
1755: billingAccount
1756: .getString("billingAccountId"),
1757: "amountApplied",
1758: refundAmount,
1759: "userLogin",
1760: userLogin));
1761: if (ServiceUtil.isError(paymentApplResult)) {
1762: return paymentApplResult;
1763: }
1764: } catch (GenericServiceException e) {
1765: return ServiceUtil
1766: .returnError("Problem creating PaymentApplication: "
1767: + e.getMessage());
1768: }
1769: }
1770: }
1771:
1772: Map result = ServiceUtil.returnSuccess();
1773: result.put("paymentId", paymentId);
1774: return result;
1775: }
1776:
1777: public static Map createPaymentApplicationsFromReturnItemResponse(
1778: DispatchContext dctx, Map context) {
1779: LocalDispatcher dispatcher = dctx.getDispatcher();
1780: GenericDelegator delegator = dctx.getDelegator();
1781: GenericValue userLogin = (GenericValue) context
1782: .get("userLogin");
1783:
1784: // the strategy for this service is to get a list of return invoices via the return items -> return item billing relationships
1785: // then split up the responseAmount among the invoices evenly
1786: String responseId = (String) context
1787: .get("returnItemResponseId");
1788: String errorMsg = "Failed to create payment applications for return item response ["
1789: + responseId + "]. ";
1790: try {
1791: GenericValue response = delegator.findByPrimaryKey(
1792: "ReturnItemResponse", UtilMisc.toMap(
1793: "returnItemResponseId", responseId));
1794: if (response == null) {
1795: return ServiceUtil.returnError(errorMsg
1796: + "Return Item Response not found with ID ["
1797: + responseId + "].");
1798: }
1799: BigDecimal responseAmount = response.getBigDecimal(
1800: "responseAmount").setScale(decimals, rounding);
1801: String paymentId = response.getString("paymentId");
1802:
1803: // for each return item in the response, get the list of return item billings and then a list of invoices
1804: Map returnInvoices = FastMap.newInstance(); // key is invoiceId, value is Invoice GenericValue
1805: List items = response.getRelated("ReturnItem");
1806: for (Iterator itemIter = items.iterator(); itemIter
1807: .hasNext();) {
1808: GenericValue item = (GenericValue) itemIter.next();
1809: List billings = item.getRelated("ReturnItemBilling");
1810: for (Iterator billIter = billings.iterator(); billIter
1811: .hasNext();) {
1812: GenericValue billing = (GenericValue) billIter
1813: .next();
1814: GenericValue invoice = billing
1815: .getRelatedOne("Invoice");
1816:
1817: // 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)
1818: if (returnInvoices.get(invoice
1819: .getString("invoiceId")) == null) {
1820: returnInvoices.put(invoice
1821: .getString("invoiceId"), invoice);
1822: }
1823: }
1824: }
1825:
1826: // for each return invoice found, sum up the related billings
1827: Map invoiceTotals = FastMap.newInstance(); // key is invoiceId, value is the sum of all billings for that invoice
1828: BigDecimal grandTotal = ZERO; // The sum of all return invoice totals
1829: for (Iterator iter = returnInvoices.values().iterator(); iter
1830: .hasNext();) {
1831: GenericValue invoice = (GenericValue) iter.next();
1832:
1833: List billings = invoice.getRelated("ReturnItemBilling");
1834: BigDecimal runningTotal = ZERO;
1835: for (Iterator billIter = billings.iterator(); billIter
1836: .hasNext();) {
1837: GenericValue billing = (GenericValue) billIter
1838: .next();
1839: runningTotal = runningTotal.add(billing
1840: .getBigDecimal("amount").multiply(
1841: billing.getBigDecimal("quantity"))
1842: .setScale(decimals, rounding));
1843: }
1844:
1845: invoiceTotals.put(invoice.getString("invoiceId"),
1846: runningTotal);
1847: grandTotal = grandTotal.add(runningTotal);
1848: }
1849:
1850: // now allocate responseAmount * invoiceTotal / grandTotal to each invoice
1851: for (Iterator iter = returnInvoices.values().iterator(); iter
1852: .hasNext();) {
1853: GenericValue invoice = (GenericValue) iter.next();
1854: String invoiceId = invoice.getString("invoiceId");
1855: BigDecimal invoiceTotal = (BigDecimal) invoiceTotals
1856: .get(invoiceId);
1857:
1858: BigDecimal amountApplied = responseAmount.multiply(
1859: invoiceTotal).divide(grandTotal, decimals,
1860: rounding).setScale(decimals, rounding);
1861:
1862: if (paymentId != null) {
1863: // create a payment application for the invoice
1864: Map input = UtilMisc
1865: .toMap("paymentId", paymentId, "invoiceId",
1866: invoice.getString("invoiceId"));
1867: input.put("amountApplied", new Double(amountApplied
1868: .doubleValue()));
1869: input.put("userLogin", userLogin);
1870: if (response.get("billingAccountId") != null) {
1871: GenericValue billingAccount = response
1872: .getRelatedOne("BillingAccount");
1873: if (billingAccount != null) {
1874: input.put("billingAccountId", response
1875: .get("billingAccountId"));
1876: }
1877: }
1878: Map serviceResults = dispatcher.runSync(
1879: "createPaymentApplication", input);
1880: if (ServiceUtil.isError(serviceResults)) {
1881: return ServiceUtil.returnError(errorMsg, null,
1882: null, serviceResults);
1883: }
1884: if (Debug.verboseOn()) {
1885: Debug.logInfo(
1886: "Created PaymentApplication for response with amountApplied "
1887: + amountApplied.toString(),
1888: module);
1889: }
1890: }
1891: }
1892: } catch (GenericServiceException e) {
1893: Debug.logError(e, errorMsg + e.getMessage(), module);
1894: return ServiceUtil.returnError(errorMsg + e.getMessage());
1895: } catch (GenericEntityException e) {
1896: Debug.logError(e, errorMsg + e.getMessage(), module);
1897: return ServiceUtil.returnError(errorMsg + e.getMessage());
1898: }
1899: return ServiceUtil.returnSuccess();
1900: }
1901:
1902: // replacement return (create new order adjusted to be at no charge)
1903: public static Map processReplacementReturn(DispatchContext dctx,
1904: Map context) {
1905: LocalDispatcher dispatcher = dctx.getDispatcher();
1906: GenericDelegator delegator = dctx.getDelegator();
1907: String returnId = (String) context.get("returnId");
1908: GenericValue userLogin = (GenericValue) context
1909: .get("userLogin");
1910: Locale locale = (Locale) context.get("locale");
1911:
1912: GenericValue returnHeader = null;
1913: List returnItems = null;
1914: try {
1915: returnHeader = delegator.findByPrimaryKey("ReturnHeader",
1916: UtilMisc.toMap("returnId", returnId));
1917: if (returnHeader != null) {
1918: returnItems = returnHeader.getRelatedByAnd(
1919: "ReturnItem", UtilMisc.toMap("returnTypeId",
1920: "RTN_REPLACE"));
1921: }
1922: } catch (GenericEntityException e) {
1923: Debug.logError(e, "Problems looking up return information",
1924: module);
1925: return ServiceUtil.returnError(UtilProperties.getMessage(
1926: resource_error,
1927: "OrderErrorGettingReturnHeaderItemInformation",
1928: locale));
1929: }
1930:
1931: List createdOrderIds = new ArrayList();
1932: if (returnHeader != null && returnItems != null
1933: && returnItems.size() > 0) {
1934: Map itemsByOrder = new HashMap();
1935: Map totalByOrder = new HashMap();
1936: groupReturnItemsByOrder(returnItems, itemsByOrder,
1937: totalByOrder, delegator, returnId);
1938:
1939: // process each one by order
1940: Set itemSet = itemsByOrder.entrySet();
1941: Iterator itemByOrderIt = itemSet.iterator();
1942: while (itemByOrderIt.hasNext()) {
1943: Map.Entry entry = (Map.Entry) itemByOrderIt.next();
1944: String orderId = (String) entry.getKey();
1945: List items = (List) entry.getValue();
1946:
1947: // get order header & payment prefs
1948: GenericValue orderHeader = null;
1949: try {
1950: orderHeader = delegator.findByPrimaryKey(
1951: "OrderHeader", UtilMisc.toMap("orderId",
1952: orderId));
1953: } catch (GenericEntityException e) {
1954: Debug.logError(e, "Cannot get Order details for #"
1955: + orderId, module);
1956: continue;
1957: }
1958:
1959: OrderReadHelper orh = new OrderReadHelper(orderHeader);
1960:
1961: // create the replacement order
1962: Map orderMap = UtilMisc.toMap("userLogin", userLogin);
1963: GenericValue placingParty = orh.getPlacingParty();
1964: String placingPartyId = null;
1965: if (placingParty != null) {
1966: placingPartyId = placingParty.getString("partyId");
1967: }
1968:
1969: orderMap.put("orderTypeId", "SALES_ORDER");
1970: orderMap.put("partyId", placingPartyId);
1971: orderMap.put("productStoreId", orderHeader
1972: .get("productStoreId"));
1973: orderMap.put("webSiteId", orderHeader.get("webSiteId"));
1974: orderMap.put("visitId", orderHeader.get("visitId"));
1975: orderMap.put("currencyUom", orderHeader
1976: .get("currencyUom"));
1977: orderMap.put("grandTotal", new Double(0.00));
1978:
1979: // make the contact mechs
1980: List contactMechs = new ArrayList();
1981: List orderCm = null;
1982: try {
1983: orderCm = orderHeader
1984: .getRelated("OrderContactMech");
1985: } catch (GenericEntityException e) {
1986: Debug.logError(e, module);
1987: }
1988: if (orderCm != null) {
1989: Iterator orderCmi = orderCm.iterator();
1990: while (orderCmi.hasNext()) {
1991: GenericValue v = (GenericValue) orderCmi.next();
1992: contactMechs.add(GenericValue.create(v));
1993: }
1994: orderMap.put("orderContactMechs", contactMechs);
1995: }
1996:
1997: // make the shipment prefs
1998: List shipmentPrefs = new ArrayList();
1999: List orderSp = null;
2000: try {
2001: orderSp = orderHeader
2002: .getRelated("OrderShipmentPreference");
2003: } catch (GenericEntityException e) {
2004: Debug.logError(e, module);
2005: }
2006: if (orderSp != null) {
2007: Iterator orderSpi = orderSp.iterator();
2008: while (orderSpi.hasNext()) {
2009: GenericValue v = (GenericValue) orderSpi.next();
2010: shipmentPrefs.add(GenericValue.create(v));
2011: }
2012: orderMap.put("orderShipmentPreferences",
2013: shipmentPrefs);
2014: }
2015:
2016: // make the order items
2017: double itemTotal = 0.00;
2018: List orderItems = new ArrayList();
2019: List orderItemShipGroupInfo = new ArrayList();
2020: List orderItemShipGroupIds = new ArrayList(); // this is used to store the ship group ids of the groups already added to the orderItemShipGroupInfo list
2021: List orderItemAssocs = new ArrayList();
2022: if (items != null) {
2023: Iterator ri = items.iterator();
2024: int itemCount = 1;
2025: while (ri.hasNext()) {
2026: GenericValue returnItem = (GenericValue) ri
2027: .next();
2028: GenericValue orderItem = null;
2029: try {
2030: orderItem = returnItem
2031: .getRelatedOne("OrderItem");
2032: } catch (GenericEntityException e) {
2033: Debug.logError(e, module);
2034: continue;
2035: }
2036: if (orderItem != null) {
2037: Double quantity = returnItem
2038: .getDouble("returnQuantity");
2039: Double unitPrice = returnItem
2040: .getDouble("returnPrice");
2041: if (quantity != null && unitPrice != null) {
2042: itemTotal = (quantity.doubleValue() * unitPrice
2043: .doubleValue());
2044: GenericValue newItem = delegator
2045: .makeValue(
2046: "OrderItem",
2047: UtilMisc
2048: .toMap(
2049: "orderItemSeqId",
2050: UtilFormatOut
2051: .formatPaddedNumber(
2052: itemCount,
2053: 5)));
2054:
2055: newItem
2056: .set(
2057: "orderItemTypeId",
2058: orderItem
2059: .get("orderItemTypeId"));
2060: newItem.set("productId", orderItem
2061: .get("productId"));
2062: newItem
2063: .set(
2064: "productFeatureId",
2065: orderItem
2066: .get("productFeatureId"));
2067: newItem.set("prodCatalogId", orderItem
2068: .get("prodCatalogId"));
2069: newItem
2070: .set(
2071: "productCategoryId",
2072: orderItem
2073: .get("productCategoryId"));
2074: newItem.set("quantity", quantity);
2075: newItem.set("unitPrice", unitPrice);
2076: newItem.set("unitListPrice", orderItem
2077: .get("unitListPrice"));
2078: newItem
2079: .set(
2080: "itemDescription",
2081: orderItem
2082: .get("itemDescription"));
2083: newItem.set("comments", orderItem
2084: .get("comments"));
2085: newItem
2086: .set(
2087: "correspondingPoId",
2088: orderItem
2089: .get("correspondingPoId"));
2090: newItem.set("statusId", "ITEM_CREATED");
2091: orderItems.add(newItem);
2092:
2093: // Set the order item ship group information
2094: // TODO: only the first ship group associated to the item
2095: // of the original order is considered and cloned,
2096: // anIs there a better way to handle this?d the returned units are assigned to it.
2097: //
2098:
2099: try {
2100: GenericValue orderItemShipGroupAssoc = EntityUtil
2101: .getFirst(orderItem
2102: .getRelated("OrderItemShipGroupAssoc"));
2103: if (orderItemShipGroupAssoc != null) {
2104: if (!orderItemShipGroupIds
2105: .contains(orderItemShipGroupAssoc
2106: .getString("shipGroupSeqId"))) {
2107: GenericValue orderItemShipGroup = orderItemShipGroupAssoc
2108: .getRelatedOne("OrderItemShipGroup");
2109: GenericValue newOrderItemShipGroup = (GenericValue) orderItemShipGroup
2110: .clone();
2111: newOrderItemShipGroup.set(
2112: "orderId", null);
2113: orderItemShipGroupInfo
2114: .add(newOrderItemShipGroup);
2115: orderItemShipGroupIds
2116: .add(orderItemShipGroupAssoc
2117: .getString("shipGroupSeqId"));
2118: }
2119: GenericValue newOrderItemShipGroupAssoc = delegator
2120: .makeValue(
2121: "OrderItemShipGroupAssoc",
2122: UtilMisc
2123: .toMap(
2124: "orderItemSeqId",
2125: newItem
2126: .getString("orderItemSeqId"),
2127: "shipGroupSeqId",
2128: orderItemShipGroupAssoc
2129: .getString("shipGroupSeqId"),
2130: "quantity",
2131: quantity));
2132: orderItemShipGroupInfo
2133: .add(newOrderItemShipGroupAssoc);
2134: }
2135: } catch (GenericEntityException gee) {
2136: Debug.logError(gee, module);
2137: }
2138: // Create an association between the replacement order item and the order item of the original order
2139: GenericValue newOrderItemAssoc = delegator
2140: .makeValue(
2141: "OrderItemAssoc",
2142: UtilMisc
2143: .toMap(
2144: "orderId",
2145: orderHeader
2146: .getString("orderId"),
2147: "orderItemSeqId",
2148: orderItem
2149: .getString("orderItemSeqId"),
2150: "shipGroupSeqId",
2151: "_NA_",
2152: "toOrderItemSeqId",
2153: newItem
2154: .getString("orderItemSeqId"),
2155: "toShipGroupSeqId",
2156: "_NA_",
2157: "orderItemAssocTypeId",
2158: "REPLACEMENT"));
2159: orderItemAssocs.add(newOrderItemAssoc);
2160: }
2161: }
2162: }
2163: orderMap.put("orderItems", orderItems);
2164: if (orderItemShipGroupInfo.size() > 0) {
2165: orderMap.put("orderItemShipGroupInfo",
2166: orderItemShipGroupInfo);
2167: }
2168: if (orderItemAssocs.size() > 0) {
2169: orderMap.put("orderItemAssociations",
2170: orderItemAssocs);
2171: }
2172: } else {
2173: Debug.logError("No return items found??", module);
2174: continue;
2175: }
2176:
2177: // create the replacement adjustment
2178: GenericValue adj = delegator.makeValue(
2179: "OrderAdjustment", new HashMap());
2180: adj.set("orderAdjustmentTypeId", "REPLACE_ADJUSTMENT");
2181: adj.set("amount", new Double(itemTotal * -1));
2182: adj.set("comments", "Replacement Item Return #"
2183: + returnId);
2184: adj.set("createdDate", UtilDateTime.nowTimestamp());
2185: adj.set("createdByUserLogin", userLogin
2186: .getString("userLoginId"));
2187: orderMap.put("orderAdjustments", UtilMisc.toList(adj));
2188:
2189: // we'll assume new order is under same terms as original. note orderTerms is a required parameter of storeOrder
2190: try {
2191: orderMap.put("orderTerms", orderHeader
2192: .getRelated("OrderTerm"));
2193: } catch (GenericEntityException e) {
2194: Debug
2195: .logError(
2196: e,
2197: "Cannot create replacement order because order terms for original order are not available",
2198: module);
2199: }
2200: // we'll assume the new order has the same order roles of the original one
2201: try {
2202: List orderRoles = orderHeader
2203: .getRelated("OrderRole");
2204: Map orderRolesMap = FastMap.newInstance();
2205: if (orderRoles != null) {
2206: Iterator orderRolesIt = orderRoles.iterator();
2207: while (orderRolesIt.hasNext()) {
2208: GenericValue orderRole = (GenericValue) orderRolesIt
2209: .next();
2210: List parties = (List) orderRolesMap
2211: .get(orderRole
2212: .getString("roleTypeId"));
2213: if (parties == null) {
2214: parties = FastList.newInstance();
2215: orderRolesMap.put(orderRole
2216: .getString("roleTypeId"),
2217: parties);
2218: }
2219: parties.add(orderRole.getString("partyId"));
2220: }
2221: }
2222: if (orderRolesMap.size() > 0) {
2223: orderMap.put("orderAdditionalPartyRoleMap",
2224: orderRolesMap);
2225: }
2226: } catch (GenericEntityException e) {
2227: Debug
2228: .logError(
2229: e,
2230: "Cannot create replacement order because order roles for original order are not available",
2231: module);
2232: }
2233:
2234: // create the order
2235: String createdOrderId = null;
2236: Map orderResult = null;
2237: try {
2238: orderResult = dispatcher.runSync("storeOrder",
2239: orderMap);
2240: } catch (GenericServiceException e) {
2241: Debug.logInfo(e, "Problem creating the order!",
2242: module);
2243: }
2244: if (orderResult != null) {
2245: createdOrderId = (String) orderResult
2246: .get("orderId");
2247: createdOrderIds.add(createdOrderId);
2248: }
2249:
2250: // since there is no payments required; order is ready for processing/shipment
2251: if (createdOrderId != null) {
2252: OrderChangeHelper.approveOrder(dispatcher,
2253: userLogin, createdOrderId);
2254: }
2255: }
2256: }
2257:
2258: StringBuffer successMessage = new StringBuffer();
2259: if (createdOrderIds.size() > 0) {
2260: successMessage
2261: .append("The following new orders have been created : ");
2262: Iterator i = createdOrderIds.iterator();
2263: while (i.hasNext()) {
2264: successMessage.append(i.next());
2265: if (i.hasNext()) {
2266: successMessage.append(", ");
2267: }
2268: }
2269: } else {
2270: successMessage.append("No orders were created.");
2271: }
2272:
2273: return ServiceUtil.returnSuccess(successMessage.toString());
2274: }
2275:
2276: /**
2277: * Takes a List of returnItems and returns a Map of orderId -> items and a Map of orderId -> orderTotal
2278: * @param returnItems
2279: * @param itemsByOrder
2280: * @param totalByOrder
2281: * @param delegator
2282: * @param returnId
2283: */
2284: public static void groupReturnItemsByOrder(List returnItems,
2285: Map itemsByOrder, Map totalByOrder,
2286: GenericDelegator delegator, String returnId) {
2287: Iterator itemIt = returnItems.iterator();
2288: while (itemIt.hasNext()) {
2289: GenericValue item = (GenericValue) itemIt.next();
2290: String orderId = item.getString("orderId");
2291: if (orderId != null) {
2292: if (itemsByOrder != null) {
2293: List orderList = (List) itemsByOrder.get(orderId);
2294: Double totalForOrder = null;
2295: if (totalByOrder != null) {
2296: totalForOrder = (Double) totalByOrder
2297: .get(orderId);
2298: }
2299: if (orderList == null) {
2300: orderList = new ArrayList();
2301: }
2302: if (totalForOrder == null) {
2303: totalForOrder = new Double(0.00);
2304: }
2305:
2306: // add to the items list
2307: orderList.add(item);
2308: itemsByOrder.put(orderId, orderList);
2309:
2310: if (totalByOrder != null) {
2311: // add on the total for this line
2312: Double quantity = item
2313: .getDouble("returnQuantity");
2314: Double amount = item.getDouble("returnPrice");
2315: if (quantity == null) {
2316: quantity = new Double(0);
2317: }
2318: if (amount == null) {
2319: amount = new Double(0.00);
2320: }
2321: double this Total = amount.doubleValue()
2322: * quantity.doubleValue();
2323: double existingTotal = totalForOrder
2324: .doubleValue();
2325: Map condition = UtilMisc.toMap("returnId", item
2326: .get("returnId"), "returnItemSeqId",
2327: item.get("returnItemSeqId"));
2328: Double newTotal = new Double(existingTotal
2329: + this Total
2330: + getReturnAdjustmentTotal(delegator,
2331: condition));
2332: totalByOrder.put(orderId, newTotal);
2333: }
2334: }
2335: }
2336: }
2337:
2338: // We may also have some order-level adjustments, so we need to go through each order again and add those as well
2339: if ((totalByOrder != null) && (totalByOrder.keySet() != null)) {
2340: Iterator orderIterator = totalByOrder.keySet().iterator();
2341: while (orderIterator.hasNext()) {
2342: String orderId = (String) orderIterator.next();
2343: // find returnAdjustment for returnHeader
2344: Map condition = UtilMisc.toMap("returnId", returnId,
2345: "returnItemSeqId",
2346: org.ofbiz.common.DataModelConstants.SEQ_ID_NA);
2347: double existingTotal = ((Double) totalByOrder
2348: .get(orderId)).doubleValue()
2349: + getReturnAdjustmentTotal(delegator, condition);
2350: totalByOrder.put(orderId, new Double(existingTotal));
2351: }
2352: }
2353: }
2354:
2355: public static Map getReturnAmountByOrder(DispatchContext dctx,
2356: Map context) {
2357: GenericDelegator delegator = dctx.getDelegator();
2358: String returnId = (String) context.get("returnId");
2359: Locale locale = (Locale) context.get("locale");
2360: List returnItems = null;
2361: Map returnAmountByOrder = new HashMap();
2362: try {
2363: returnItems = delegator.findByAnd("ReturnItem", UtilMisc
2364: .toMap("returnId", returnId));
2365:
2366: } catch (GenericEntityException e) {
2367: Debug.logError(e, "Problems looking up return information",
2368: module);
2369: return ServiceUtil.returnError(UtilProperties.getMessage(
2370: resource_error,
2371: "OrderErrorGettingReturnHeaderItemInformation",
2372: locale));
2373: }
2374: if ((returnItems != null) && (returnItems.size() > 0)) {
2375: Iterator returnItemIterator = returnItems.iterator();
2376: GenericValue returnItem = null;
2377: GenericValue returnItemResponse = null;
2378: GenericValue payment = null;
2379: String orderId;
2380: List paymentList = new ArrayList();
2381: while (returnItemIterator.hasNext()) {
2382: returnItem = (GenericValue) returnItemIterator.next();
2383: orderId = returnItem.getString("orderId");
2384: try {
2385: returnItemResponse = returnItem
2386: .getRelatedOne("ReturnItemResponse");
2387: if ((returnItemResponse != null)
2388: && (orderId != null)) {
2389: // TODO should we filter on payment's status (PMNT_SENT,PMNT_RECEIVED)
2390: payment = returnItemResponse
2391: .getRelatedOne("Payment");
2392: if ((payment != null)
2393: && (payment.getBigDecimal("amount") != null)
2394: && !paymentList.contains(payment
2395: .get("paymentId"))) {
2396: UtilMisc.addToBigDecimalInMap(
2397: returnAmountByOrder, orderId,
2398: payment.getBigDecimal("amount"));
2399: paymentList.add(payment.get("paymentId")); // make sure we don't add duplicated payment amount
2400: }
2401: }
2402: } catch (GenericEntityException e) {
2403: Debug
2404: .logError(
2405: e,
2406: "Problems looking up return item related information",
2407: module);
2408: return ServiceUtil
2409: .returnError(UtilProperties
2410: .getMessage(
2411: resource_error,
2412: "OrderErrorGettingReturnHeaderItemInformation",
2413: locale));
2414: }
2415: }
2416: }
2417: return UtilMisc.toMap("orderReturnAmountMap",
2418: returnAmountByOrder);
2419: }
2420:
2421: public static Map checkPaymentAmountForRefund(DispatchContext dctx,
2422: Map context) {
2423: LocalDispatcher dispatcher = dctx.getDispatcher();
2424: GenericDelegator delegator = dctx.getDelegator();
2425: String returnId = (String) context.get("returnId");
2426: Locale locale = (Locale) context.get("locale");
2427: Map returnAmountByOrder = null;
2428: Map serviceResult = null;
2429: //GenericValue orderHeader = null;
2430: try {
2431: serviceResult = dispatcher.runSync(
2432: "getReturnAmountByOrder",
2433: org.ofbiz.base.util.UtilMisc.toMap("returnId",
2434: returnId));
2435: } catch (GenericServiceException e) {
2436: Debug
2437: .logError(
2438: e,
2439: "Problem running the getReturnAmountByOrder service",
2440: module);
2441: return ServiceUtil.returnError(UtilProperties.getMessage(
2442: resource_error,
2443: "OrderProblemsWithGetReturnAmountByOrder", locale));
2444: }
2445: if (ServiceUtil.isError(serviceResult)) {
2446: return ServiceUtil.returnError((String) serviceResult
2447: .get(ModelService.ERROR_MESSAGE));
2448: } else {
2449: returnAmountByOrder = (Map) serviceResult
2450: .get("orderReturnAmountMap");
2451: }
2452:
2453: if ((returnAmountByOrder != null)
2454: && (returnAmountByOrder.keySet() != null)) {
2455: Iterator orderIterator = returnAmountByOrder.keySet()
2456: .iterator();
2457: while (orderIterator.hasNext()) {
2458: String orderId = (String) orderIterator.next();
2459: BigDecimal returnAmount = (BigDecimal) returnAmountByOrder
2460: .get(orderId);
2461: if (returnAmount.abs().compareTo(
2462: new BigDecimal("0.000001")) < 0) {
2463: Debug.logError("Order [" + orderId
2464: + "] refund amount[ " + returnAmount
2465: + "] less than zero", module);
2466: return ServiceUtil
2467: .returnError(UtilProperties
2468: .getMessage(
2469: resource_error,
2470: "OrderReturnTotalCannotLessThanZero",
2471: locale));
2472: }
2473: OrderReadHelper helper = new OrderReadHelper(
2474: OrderReadHelper.getOrderHeader(delegator,
2475: orderId));
2476: BigDecimal grandTotal = helper.getOrderGrandTotalBd();
2477: if (returnAmount == null) {
2478: Debug.logInfo("No returnAmount found for order:"
2479: + orderId, module);
2480: } else {
2481: if (returnAmount.subtract(grandTotal).compareTo(
2482: new BigDecimal("0.01")) > 0) {
2483: Debug.logError("Order [" + orderId
2484: + "] refund amount[ " + returnAmount
2485: + "] exceeds order total ["
2486: + grandTotal + "]", module);
2487: return ServiceUtil
2488: .returnError(UtilProperties
2489: .getMessage(
2490: resource_error,
2491: "OrderRefundAmountExceedsOrderTotal",
2492: locale));
2493: }
2494: }
2495: }
2496: }
2497: return ServiceUtil.returnSuccess();
2498: }
2499:
2500: public static Map createReturnAdjustment(DispatchContext dctx,
2501: Map context) {
2502: GenericDelegator delegator = dctx.getDelegator();
2503: String orderAdjustmentId = (String) context
2504: .get("orderAdjustmentId");
2505: String returnAdjustmentTypeId = (String) context
2506: .get("returnAdjustmentTypeId");
2507: String returnId = (String) context.get("returnId");
2508: String returnItemSeqId = (String) context
2509: .get("returnItemSeqId");
2510: String description = (String) context.get("description");
2511:
2512: GenericValue returnItemTypeMap = null;
2513: GenericValue orderAdjustment = null;
2514: GenericValue returnAdjustmentType = null;
2515: GenericValue orderItem = null;
2516: GenericValue returnItem = null;
2517: GenericValue returnHeader = null;
2518:
2519: Double amount;
2520:
2521: // if orderAdjustment is not empty, then copy most return adjustment information from orderAdjustment's
2522: if (orderAdjustmentId != null) {
2523: try {
2524: orderAdjustment = delegator
2525: .findByPrimaryKey("OrderAdjustment", UtilMisc
2526: .toMap("orderAdjustmentId",
2527: orderAdjustmentId));
2528:
2529: // get returnHeaderTypeId from ReturnHeader and then use it to figure out return item type mapping
2530: returnHeader = delegator.findByPrimaryKey(
2531: "ReturnHeader", UtilMisc.toMap("returnId",
2532: returnId));
2533: String returnHeaderTypeId = ((returnHeader != null) && (returnHeader
2534: .getString("returnHeaderTypeId") != null)) ? returnHeader
2535: .getString("returnHeaderTypeId")
2536: : "CUSTOMER_RETURN";
2537: returnItemTypeMap = delegator.findByPrimaryKey(
2538: "ReturnItemTypeMap", UtilMisc.toMap(
2539: "returnHeaderTypeId",
2540: returnHeaderTypeId, "returnItemMapKey",
2541: orderAdjustment
2542: .get("orderAdjustmentTypeId")));
2543: returnAdjustmentType = returnItemTypeMap
2544: .getRelatedOne("ReturnAdjustmentType");
2545: if (returnAdjustmentType != null
2546: && UtilValidate.isEmpty(description)) {
2547: description = returnAdjustmentType
2548: .getString("description");
2549: }
2550: if ((returnItemSeqId != null)
2551: && !("_NA_".equals(returnItemSeqId))) {
2552: returnItem = delegator.findByPrimaryKey(
2553: "ReturnItem", UtilMisc.toMap("returnId",
2554: returnId, "returnItemSeqId",
2555: returnItemSeqId));
2556: Debug.log("returnId:" + returnId
2557: + ",returnItemSeqId:" + returnItemSeqId);
2558: orderItem = returnItem.getRelatedOne("OrderItem");
2559: }
2560: } catch (GenericEntityException e) {
2561: Debug.logError(e, module);
2562: throw new GeneralRuntimeException(e.getMessage());
2563: }
2564: context.putAll(orderAdjustment.getAllFields());
2565: }
2566:
2567: // if orderAdjustmentTypeId is empty, ie not found from orderAdjustmentId, then try to get returnAdjustmentTypeId from returnItemTypeMap,
2568: // if still empty, use default RET_MAN_ADJ
2569: if (returnAdjustmentTypeId == null) {
2570: String mappingTypeId = returnItemTypeMap != null ? returnItemTypeMap
2571: .get("returnItemTypeId").toString()
2572: : null;
2573: returnAdjustmentTypeId = mappingTypeId != null ? mappingTypeId
2574: : "RET_MAN_ADJ";
2575: }
2576: // calculate the returnAdjustment amount
2577: if (returnItem != null) { // returnAdjustment for returnItem
2578: if (needRecalculate(returnAdjustmentTypeId)) {
2579: Debug.logInfo(
2580: "returnPrice:"
2581: + returnItem.getDouble("returnPrice")
2582: + ",returnQuantity:"
2583: + returnItem
2584: .getDouble("returnQuantity")
2585: + ",sourcePercentage:"
2586: + orderAdjustment
2587: .getDouble("sourcePercentage"),
2588: module);
2589: if (orderAdjustment == null) {
2590: Debug
2591: .logError(
2592: "orderAdjustment ["
2593: + orderAdjustmentId
2594: + "] not found", module);
2595: return ServiceUtil.returnError("orderAdjustment ["
2596: + orderAdjustmentId + "] not found");
2597: }
2598: BigDecimal returnTotal = returnItem.getBigDecimal(
2599: "returnPrice").multiply(
2600: returnItem.getBigDecimal("returnQuantity"));
2601: BigDecimal orderTotal = orderItem.getBigDecimal(
2602: "quantity").multiply(
2603: orderItem.getBigDecimal("unitPrice"));
2604: amount = getAdjustmentAmount("RET_SALES_TAX_ADJ"
2605: .equals(returnAdjustmentType), returnTotal,
2606: orderTotal, orderAdjustment
2607: .getBigDecimal("amount"));
2608: } else {
2609: amount = (Double) context.get("amount");
2610: }
2611: } else { // returnAdjustment for returnHeader
2612: amount = (Double) context.get("amount");
2613: }
2614:
2615: // store the return adjustment
2616: String seqId = delegator.getNextSeqId("ReturnAdjustment");
2617: GenericValue newReturnAdjustment = delegator.makeValue(
2618: "ReturnAdjustment", UtilMisc.toMap(
2619: "returnAdjustmentId", seqId));
2620:
2621: try {
2622: newReturnAdjustment.setNonPKFields(context);
2623: if (orderAdjustment != null
2624: && orderAdjustment.get("taxAuthorityRateSeqId") != null) {
2625: newReturnAdjustment.set("taxAuthorityRateSeqId",
2626: orderAdjustment
2627: .getString("taxAuthorityRateSeqId"));
2628: }
2629: newReturnAdjustment.set("amount", amount);
2630: newReturnAdjustment.set("returnAdjustmentTypeId",
2631: returnAdjustmentTypeId);
2632: newReturnAdjustment.set("description", description);
2633: newReturnAdjustment.set("returnItemSeqId", UtilValidate
2634: .isEmpty(returnItemSeqId) ? "_NA_"
2635: : returnItemSeqId);
2636:
2637: delegator.create(newReturnAdjustment);
2638: Map result = ServiceUtil
2639: .returnSuccess("Create ReturnAdjustment with Id:"
2640: + seqId + " successfully.");
2641: result.put("returnAdjustmentId", seqId);
2642: return result;
2643: } catch (GenericEntityException e) {
2644: Debug.logError(e, "Failed to store returnAdjustment",
2645: module);
2646: return ServiceUtil
2647: .returnError("Failed to store returnAdjustment");
2648: }
2649: }
2650:
2651: public static Map updateReturnAdjustment(DispatchContext dctx,
2652: Map context) {
2653: GenericDelegator delegator = dctx.getDelegator();
2654:
2655: GenericValue returnItem = null;
2656: GenericValue returnAdjustment = null;
2657: String returnAdjustmentTypeId = null;
2658: Double amount;
2659:
2660: try {
2661: returnAdjustment = delegator.findByPrimaryKey(
2662: "ReturnAdjustment", UtilMisc.toMap(
2663: "returnAdjustmentId", context
2664: .get("returnAdjustmentId")));
2665:
2666: if (returnAdjustment != null) {
2667: returnItem = delegator
2668: .findByPrimaryKey("ReturnItem", UtilMisc.toMap(
2669: "returnId", returnAdjustment
2670: .get("returnId"),
2671: "returnItemSeqId", returnAdjustment
2672: .get("returnItemSeqId")));
2673: returnAdjustmentTypeId = returnAdjustment
2674: .getString("returnAdjustmentTypeId");
2675: }
2676:
2677: // calculate the returnAdjustment amount
2678: if (returnItem != null) { // returnAdjustment for returnItem
2679: double originalReturnPrice = (context
2680: .get("originalReturnPrice") != null) ? ((Double) context
2681: .get("originalReturnPrice")).doubleValue()
2682: : returnItem.getDouble("returnPrice")
2683: .doubleValue();
2684: double originalReturnQuantity = (context
2685: .get("originalReturnQuantity") != null) ? ((Double) context
2686: .get("originalReturnQuantity")).doubleValue()
2687: : returnItem.getDouble("returnQuantity")
2688: .doubleValue();
2689:
2690: if (needRecalculate(returnAdjustmentTypeId)) {
2691: BigDecimal returnTotal = returnItem.getBigDecimal(
2692: "returnPrice").multiply(
2693: returnItem.getBigDecimal("returnQuantity"));
2694: BigDecimal originalReturnTotal = new BigDecimal(
2695: originalReturnPrice)
2696: .multiply(new BigDecimal(
2697: originalReturnQuantity));
2698: amount = getAdjustmentAmount("RET_SALES_TAX_ADJ"
2699: .equals(returnAdjustmentTypeId),
2700: returnTotal, originalReturnTotal,
2701: returnAdjustment.getBigDecimal("amount"));
2702: } else {
2703: amount = (Double) context.get("amount");
2704: }
2705: } else { // returnAdjustment for returnHeader
2706: amount = (Double) context.get("amount");
2707: }
2708:
2709: returnAdjustment.setNonPKFields(context);
2710: returnAdjustment.set("amount", amount);
2711: delegator.store(returnAdjustment);
2712: Debug.logInfo("Update ReturnAdjustment with Id:"
2713: + context.get("returnAdjustmentId") + " to amount "
2714: + amount + " successfully.", module);
2715: Map result = ServiceUtil
2716: .returnSuccess("Update ReturnAdjustment with Id:"
2717: + context.get("returnAdjustmentId")
2718: + " to amount " + amount + " successfully.");
2719: return result;
2720: } catch (GenericEntityException e) {
2721: Debug.logError(e, "Failed to store returnAdjustment",
2722: module);
2723: return ServiceUtil
2724: .returnError("Failed to store returnAdjustment");
2725: }
2726: }
2727:
2728: // used as a dispatch service, invoke different service based on the parameters passed in
2729: public static Map createReturnItemOrAdjustment(
2730: DispatchContext dctx, Map context) {
2731: Debug.logInfo("createReturnItemOrAdjustment's context:"
2732: + context, module);
2733: String orderItemSeqId = (String) context.get("orderItemSeqId");
2734: Debug.logInfo("orderItemSeqId:" + orderItemSeqId + "#", module);
2735: LocalDispatcher dispatcher = dctx.getDispatcher();
2736: //if the request is to create returnItem, orderItemSeqId should not be empty
2737: String serviceName = UtilValidate.isNotEmpty(orderItemSeqId) ? "createReturnItem"
2738: : "createReturnAdjustment";
2739: Debug.logInfo("serviceName:" + serviceName, module);
2740: try {
2741: return dispatcher.runSync(serviceName,
2742: filterServiceContext(dctx, serviceName, context));
2743: } catch (org.ofbiz.service.GenericServiceException e) {
2744: Debug.logError(e, module);
2745: return ServiceUtil.returnError(e.getMessage());
2746: }
2747: }
2748:
2749: // used as a dispatch service, invoke different service based on the parameters passed in
2750: public static Map updateReturnItemOrAdjustment(
2751: DispatchContext dctx, Map context) {
2752: Debug.logInfo("updateReturnItemOrAdjustment's context:"
2753: + context, module);
2754: String returnAdjustmentId = (String) context
2755: .get("returnAdjustmentId");
2756: Debug.logInfo("returnAdjustmentId:" + returnAdjustmentId + "#",
2757: module);
2758: LocalDispatcher dispatcher = dctx.getDispatcher();
2759: //if the request is to create returnItem, orderItemSeqId should not be empty
2760: String serviceName = UtilValidate.isEmpty(returnAdjustmentId) ? "updateReturnItem"
2761: : "updateReturnAdjustment";
2762: Debug.logInfo("serviceName:" + serviceName, module);
2763: try {
2764: return dispatcher.runSync(serviceName,
2765: filterServiceContext(dctx, serviceName, context));
2766: } catch (org.ofbiz.service.GenericServiceException e) {
2767: Debug.logError(e, module);
2768: return ServiceUtil.returnError(e.getMessage());
2769: }
2770: }
2771:
2772: /**
2773: * These return adjustment types need to be recalculated when the return item is updated
2774: * @param returnAdjustmentTypeId
2775: * @return
2776: */
2777: public static boolean needRecalculate(String returnAdjustmentTypeId) {
2778: return "RET_PROMOTION_ADJ".equals(returnAdjustmentTypeId)
2779: || "RET_DISCOUNT_ADJ".equals(returnAdjustmentTypeId)
2780: || "RET_SALES_TAX_ADJ".equals(returnAdjustmentTypeId);
2781:
2782: }
2783:
2784: /**
2785: * Get the total return adjustments for a set of key -> value condition pairs. Done for code efficiency.
2786: * @param delegator
2787: * @param condition
2788: * @return
2789: */
2790: public static double getReturnAdjustmentTotal(
2791: GenericDelegator delegator, Map condition) {
2792: double total = 0.0;
2793: List adjustments;
2794: try {
2795: // TODO: find on a view-entity with a sum is probably more efficient
2796: adjustments = delegator.findByAnd("ReturnAdjustment",
2797: condition);
2798: if (adjustments != null) {
2799: Iterator adjustmentIterator = adjustments.iterator();
2800: while (adjustmentIterator.hasNext()) {
2801: GenericValue returnAdjustment = (GenericValue) adjustmentIterator
2802: .next();
2803: if ((returnAdjustment != null)
2804: && (returnAdjustment.get("amount") != null)) {
2805: total += returnAdjustment.getDouble("amount")
2806: .doubleValue();
2807: }
2808: }
2809: }
2810: } catch (org.ofbiz.entity.GenericEntityException e) {
2811: Debug.logError(e, module);
2812: }
2813: return total;
2814: }
2815:
2816: /**
2817: * Get rid of unnecessary parameters based on the given service name
2818: * @param dctx Service DispatchContext
2819: * @param serviceName
2820: * @param context context before clean up
2821: * @return filtered context
2822: * @throws GenericServiceException
2823: */
2824: public static Map filterServiceContext(DispatchContext dctx,
2825: String serviceName, Map context)
2826: throws GenericServiceException {
2827: ModelService modelService = dctx.getModelService(serviceName);
2828:
2829: if (modelService == null) {
2830: throw new GenericServiceException(
2831: "Problems getting the service model");
2832: }
2833: Map serviceContext = FastMap.newInstance();
2834: List modelParmInList = modelService.getInModelParamList();
2835: Iterator modelParmInIter = modelParmInList.iterator();
2836: while (modelParmInIter.hasNext()) {
2837: ModelParam modelParam = (ModelParam) modelParmInIter.next();
2838: String paramName = modelParam.name;
2839:
2840: Object value = context.get(paramName);
2841: if (value != null) {
2842: serviceContext.put(paramName, value);
2843: }
2844: }
2845: return serviceContext;
2846: }
2847:
2848: /**
2849: * Calculate new returnAdjustment amount and set scale and rounding mode based on returnAdjustmentType: RET_SALES_TAX_ADJ use sales.tax._ and others use order._
2850: * @param isSalesTax if returnAdjustmentType is SaleTax
2851: * @param returnTotal
2852: * @param originalTotal
2853: * @param amount
2854: * @return new returnAdjustment amount
2855: */
2856: public static Double getAdjustmentAmount(boolean isSalesTax,
2857: BigDecimal returnTotal, BigDecimal originalTotal,
2858: BigDecimal amount) {
2859: String settingPrefix = isSalesTax ? "salestax" : "order";
2860: String decimalsPrefix = isSalesTax ? ".calc" : "";
2861: int decimals = UtilNumber.getBigDecimalScale(settingPrefix
2862: + decimalsPrefix + ".decimals");
2863: int rounding = UtilNumber
2864: .getBigDecimalRoundingMode(settingPrefix + ".rounding");
2865: int finalDecimals = isSalesTax ? UtilNumber
2866: .getBigDecimalScale(settingPrefix + ".final.decimals")
2867: : decimals;
2868: returnTotal = returnTotal.setScale(decimals, rounding);
2869: originalTotal = originalTotal.setScale(decimals, rounding);
2870: BigDecimal newAmount = returnTotal.divide(originalTotal,
2871: decimals, rounding).multiply(amount).setScale(
2872: finalDecimals, rounding);
2873: return new Double(newAmount.doubleValue());
2874: }
2875: }
|