Package org.ofbiz.oagis

Source Code of org.ofbiz.oagis.OagisShipmentServices

/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements.  See the NOTICE file
distributed with this work for additional information
regarding copyright ownership.  The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License.  You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied.  See the License for the
specific language governing permissions and limitations
under the License.
*/

package org.ofbiz.oagis;

import java.io.IOException;
import java.io.OutputStream;
import java.io.StringWriter;
import java.io.Writer;
import java.sql.Timestamp;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;

import javolution.util.FastList;
import javolution.util.FastMap;
import javolution.util.FastSet;

import org.ofbiz.base.util.Debug;
import org.ofbiz.base.util.GeneralException;
import org.ofbiz.base.util.StringUtil;
import org.ofbiz.base.util.UtilDateTime;
import org.ofbiz.base.util.UtilFormatOut;
import org.ofbiz.base.util.UtilGenerics;
import org.ofbiz.base.util.UtilMisc;
import org.ofbiz.base.util.UtilProperties;
import org.ofbiz.base.util.UtilValidate;
import org.ofbiz.base.util.UtilXml;
import org.ofbiz.base.util.collections.MapStack;
import org.ofbiz.entity.Delegator;
import org.ofbiz.entity.GenericEntityException;
import org.ofbiz.entity.GenericValue;
import org.ofbiz.entity.condition.EntityCondition;
import org.ofbiz.entity.condition.EntityOperator;
import org.ofbiz.entity.transaction.GenericTransactionException;
import org.ofbiz.entity.transaction.TransactionUtil;
import org.ofbiz.entity.util.EntityUtil;
import org.ofbiz.order.order.OrderReadHelper;
import org.ofbiz.party.party.PartyWorker;
import org.ofbiz.product.product.ProductWorker;
import org.ofbiz.service.DispatchContext;
import org.ofbiz.service.GenericServiceException;
import org.ofbiz.service.LocalDispatcher;
import org.ofbiz.service.ServiceUtil;
import org.ofbiz.widget.fo.FoFormRenderer;
import org.ofbiz.widget.html.HtmlScreenRenderer;
import org.ofbiz.widget.screen.ScreenRenderer;
import org.w3c.dom.Document;
import org.w3c.dom.Element;


public class OagisShipmentServices {

    public static final String module = OagisShipmentServices.class.getName();

    protected static final HtmlScreenRenderer htmlScreenRenderer = new HtmlScreenRenderer();
    protected static final FoFormRenderer foFormRenderer = new FoFormRenderer();

    public static final Set<String> invalidShipmentStatusSet = UtilMisc.toSet("SHIPMENT_CANCELLED", "SHIPMENT_PICKED", "SHIPMENT_PACKED",
            "SHIPMENT_SHIPPED", "SHIPMENT_DELIVERED");

    public static final String resource = "OagisUiLabels";

    public static final String certAlias = UtilProperties.getPropertyValue("oagis.properties", "auth.client.certificate.alias");
    public static final String basicAuthUsername = UtilProperties.getPropertyValue("oagis.properties", "auth.basic.username");
    public static final String basicAuthPassword = UtilProperties.getPropertyValue("oagis.properties", "auth.basic.password");

    public static final String oagisMainNamespacePrefix = "n";
    public static final String oagisSegmentsNamespacePrefix = "os";
    public static final String oagisFieldsNamespacePrefix = "of";

    public static Map<String, Object> oagisReceiveShowShipment(DispatchContext ctx, Map<String, Object> context) {
        Document doc = (Document) context.get("document");
        boolean isErrorRetry = Boolean.TRUE.equals(context.get("isErrorRetry"));

        LocalDispatcher dispatcher = ctx.getDispatcher();
        Delegator delegator = ctx.getDelegator();
        Timestamp nowTimestamp = UtilDateTime.nowTimestamp();
        List<Map<String, String>> errorMapList = FastList.newInstance();

        GenericValue userLogin = null;
        try {
            userLogin = delegator.findByPrimaryKey("UserLogin", UtilMisc.toMap("userLoginId", "system"));
        } catch (GenericEntityException e) {
            String errMsg = "Error Getting UserLogin with userLoginId system: "+e.toString();
            Debug.logError(e, errMsg, module);
        }

        Element showShipmentElement = doc.getDocumentElement();
        showShipmentElement.normalize();

        Element controlAreaElement = UtilXml.firstChildElement(showShipmentElement, "os:CNTROLAREA"); // os
        Element bsrElement = UtilXml.firstChildElement(controlAreaElement, "os:BSR"); // os
        String bsrVerb = UtilXml.childElementValue(bsrElement, "of:VERB"); // of
        String bsrNoun = UtilXml.childElementValue(bsrElement, "of:NOUN"); // of
        String bsrRevision = UtilXml.childElementValue(bsrElement, "of:REVISION"); // of
        Map<String, Object> oagisMsgInfoCtx = UtilMisc.toMap("bsrVerb", (Object) bsrVerb, "bsrNoun", bsrNoun, "bsrRevision", bsrRevision);

        Element senderElement = UtilXml.firstChildElement(controlAreaElement, "os:SENDER"); // os
        String logicalId = UtilXml.childElementValue(senderElement, "of:LOGICALID"); // of
        String component = UtilXml.childElementValue(senderElement, "of:COMPONENT"); // of
        String task = UtilXml.childElementValue(senderElement, "of:TASK"); // of
        String referenceId = UtilXml.childElementValue(senderElement, "of:REFERENCEID"); // of
        String confirmation = UtilXml.childElementValue(senderElement, "of:CONFIRMATION"); // of
        String authId = UtilXml.childElementValue(senderElement, "of:AUTHID"); // of

        String sentDate = UtilXml.childElementValue(controlAreaElement, "os:DATETIMEISO");
        Timestamp sentTimestamp = OagisServices.parseIsoDateString(sentDate, errorMapList);

        Element dataAreaElement = UtilXml.firstChildElement(showShipmentElement, "ns:DATAAREA"); // n
        Element daShowShipmentElement = UtilXml.firstChildElement(dataAreaElement, "ns:SHOW_SHIPMENT"); // n
        Element shipmentElement = UtilXml.firstChildElement(daShowShipmentElement, "ns:SHIPMENT"); // n
        String shipmentId = UtilXml.childElementValue(shipmentElement, "of:DOCUMENTID"); // of

        Map<String, String> omiPkMap = UtilMisc.toMap("logicalId", logicalId, "component", component, "task", task, "referenceId", referenceId);

        // always log this to make messages easier to find
        Debug.logInfo("Processing oagisReceiveShowShipment for shipmentId [" + shipmentId + "] message ID [" + omiPkMap + "]", module);

        // before getting into this check to see if we've tried once and had an error, if so set isErrorRetry even if it wasn't passed in
        GenericValue previousOagisMessageInfo = null;
        try {
            previousOagisMessageInfo = delegator.findByPrimaryKey("OagisMessageInfo", omiPkMap);
        } catch (GenericEntityException e) {
            String errMsg = "Error getting OagisMessageInfo from database for shipment ID [" + shipmentId + "] message ID [" + omiPkMap + "]: " + e.toString();
            Debug.logInfo(e, errMsg, module);
            // anything else to do about this? we don't really want to send the error back or anything...
        }

        if (previousOagisMessageInfo != null && !isErrorRetry) {
            if ("OAGMP_SYS_ERROR".equals(previousOagisMessageInfo.getString("processingStatusId"))) {
                isErrorRetry = true;
            } else {
                // message already in the db, but is not in a system error state...
                String errMsg = "Message received for shipmentId [" + shipmentId + "] message ID [" + omiPkMap + "] was already partially processed but is not in a system error state, needs manual review; message ID: " + omiPkMap;
                Debug.logError(errMsg, module);
                return ServiceUtil.returnError(errMsg);
            }
        }

        oagisMsgInfoCtx.putAll(omiPkMap);
        oagisMsgInfoCtx.put("confirmation", confirmation);
        oagisMsgInfoCtx.put("authId", authId);
        oagisMsgInfoCtx.put("outgoingMessage", "N");
        oagisMsgInfoCtx.put("receivedDate", nowTimestamp);
        oagisMsgInfoCtx.put("sentDate", sentTimestamp);
        oagisMsgInfoCtx.put("shipmentId", shipmentId);
        oagisMsgInfoCtx.put("userLogin", userLogin);
        oagisMsgInfoCtx.put("processingStatusId", "OAGMP_RECEIVED");
        if (OagisServices.debugSaveXmlIn) {
            try {
                oagisMsgInfoCtx.put("fullMessageXml", UtilXml.writeXmlDocument(doc));
            } catch (IOException e) {
                // this is just for debug info, so just log and otherwise ignore error
                String errMsg = "Warning: error creating text from XML Document for saving to database: " + e.toString();
                Debug.logWarning(errMsg, module);
            }
        }

        try {
            if (isErrorRetry) {
                dispatcher.runSync("updateOagisMessageInfo", oagisMsgInfoCtx, 60, true);
            } else {
                dispatcher.runSync("createOagisMessageInfo", oagisMsgInfoCtx, 60, true);
            }
            /* running async for better error handling
            if (ServiceUtil.isError(oagisMsgInfoResult)) {
                String errMsg = ServiceUtil.getErrorMessage(oagisMsgInfoResult);
                // errorMapList.add(UtilMisc.toMap("description", errMsg, "reasonCode", "CreateOagisMessageInfoServiceError"));
                Debug.logError(errMsg, module);
            }
            */
        } catch (GenericServiceException e) {
            String errMsg = "Error creating OagisMessageInfo for the Incoming Message: " + e.toString();
            // don't pass this back, nothing they can do about it: errorMapList.add(UtilMisc.toMap("description", errMsg, "reasonCode", "GenericServiceException"));
            Debug.logError(e, errMsg, module);
        }

        GenericValue shipment = null;
        try {
            shipment = delegator.findByPrimaryKey("Shipment", UtilMisc.toMap("shipmentId", shipmentId));
        } catch (GenericEntityException e) {
            String errMsg = "Error getting Shipment from database for ID [" + shipmentId + "]: " + e.toString();
            Debug.logInfo(e, errMsg, module);
            errorMapList.add(UtilMisc.toMap("description", errMsg, "reasonCode", "GenericEntityException"));
        }

        if (shipment == null) {
            String errMsg = "Could not find Shipment ID [" + shipmentId + "]";
            errorMapList.add(UtilMisc.toMap("description", errMsg, "reasonCode", "ShipmentIdNotValid"));
        } else {
            if (invalidShipmentStatusSet.contains(shipment.get("statusId"))) {
                String errMsg = "Shipment with ID [" + shipmentId + "] is in a status [" + shipment.get("statusId") + "] that means it has been or is being shipped, so this Show Shipment message may be a duplicate.";
                errorMapList.add(UtilMisc.toMap("description", errMsg, "reasonCode", "ShipmentInBadStatus"));
            }
        }

        List<? extends Element> shipUnitElementList = UtilXml.childElementList(daShowShipmentElement, "ns:SHIPUNIT"); // n
        if (errorMapList.size() == 0 && UtilValidate.isNotEmpty(shipUnitElementList)) {
            try {
                String shipGroupSeqId = shipment.getString("primaryShipGroupSeqId");
                String originFacilityId = shipment.getString("originFacilityId");

                Element shipUnitFirstElement = (Element)shipUnitElementList.get(0);
                String trackingNum = UtilXml.childElementValue(shipUnitFirstElement, "of:TRACKINGID"); // of
                String carrierCode = UtilXml.childElementValue(shipUnitFirstElement, "of:CARRIER"); // of
                if (UtilValidate.isNotEmpty(carrierCode)) {
                    String carrierPartyId = null;
                    if (carrierCode.startsWith("F") || carrierCode.startsWith("f")) {
                        carrierPartyId = "FEDEX";
                    } else if (carrierCode.startsWith("U")|| carrierCode.startsWith("u")) {
                        carrierPartyId = "UPS";
                    }
                    Map<String, Object> resultMap = dispatcher.runSync("updateShipmentRouteSegment", UtilMisc.<String, Object>toMap("shipmentId", shipmentId,
                            "shipmentRouteSegmentId", "00001", "carrierPartyId", carrierPartyId, "trackingIdNumber", trackingNum, "userLogin", userLogin));
                    if (ServiceUtil.isError(resultMap)) {
                        String errMsg = ServiceUtil.getErrorMessage(resultMap);
                        errorMapList.add(UtilMisc.toMap("description", errMsg, "reasonCode", "updateShipmentRouteSegmentError"));
                        Debug.logError(errMsg, module);
                    }
                }

                // TODO: looks like we may have multiple shipunits for a single productId, AND with things split into
                //multiple package we may need to sort by the packages by productId to avoid deadlocks, just like we
                //do with INVITEMs... note sure exactly how that will work

                for (Element shipUnitElement : shipUnitElementList) {
                    String shipmentPackageSeqId = UtilXml.childElementValue(shipUnitElement, "of:SHPUNITSEQ"); // of
                    List<? extends Element> invItemElementList = UtilXml.childElementList(shipUnitElement, "ns:INVITEM"); //n
                    if (UtilValidate.isNotEmpty(invItemElementList)) {

                        // sort the INVITEM elements by ITEM so that all shipments are processed in the same order, avoids deadlocking problems we've seen with concurrently processed orders
                        List<Map<String, Object>> invitemMapList = FastList.newInstance();
                        boolean foundBadProductId = false;
                        for (Element invItemElement : invItemElementList) {
                            String productId = UtilXml.childElementValue(invItemElement, "of:ITEM"); // of

                            // make sure productId is valid
                            GenericValue product = delegator.findByPrimaryKeyCache("Product", UtilMisc.toMap("productId", productId));
                            if (product == null) {
                                String errMsg = "Product with ID [" + productId + "] not found (invalid Product ID).";
                                errorMapList.add(UtilMisc.toMap("reasonCode", "ProductIdNotValid", "description", errMsg));
                                Debug.logError(errMsg, module);
                                foundBadProductId = true;
                                continue;
                            }

                            Map<String, Object> invitemMap = FastMap.newInstance();
                            invitemMap.put("productId", productId);
                            // support multiple INVITEM elements for a given productId
                            UtilMisc.addToListInMap(invItemElement, invitemMap, "invItemElementList");
                            invitemMapList.add(invitemMap);
                        }

                        if (foundBadProductId) {
                            continue;
                        }

                        invitemMapList = UtilGenerics.cast(
                                UtilMisc.sortMaps(UtilGenerics.<List<Map<Object, Object>>>cast(invitemMapList), UtilMisc.toList("productId")));

                        for (Map<String, Object> invitemMap : invitemMapList) {
                            List<Element> localInvItemElementList = UtilGenerics.checkList(invitemMap.get("invItemElementList"), Element.class);

                            for (Element invItemElement : localInvItemElementList) {
                                String productId = UtilXml.childElementValue(invItemElement, "of:ITEM"); // of

                                // this is based on the SHPUNIT which is basically a box/package, but we'll try to find the item with it if applicable
                                String possibleShipmentItemSeqId = null;
                                if (UtilValidate.isNotEmpty(shipmentPackageSeqId)) {
                                    possibleShipmentItemSeqId = UtilFormatOut.formatPaddedNumber(Long.parseLong(shipmentPackageSeqId), 5);
                                }

                                Element quantityElement = UtilXml.firstChildElement(invItemElement, "os:QUANTITY"); // os
                                String quantityValueStr = UtilXml.childElementValue(quantityElement, "of:VALUE"); // os
                                // TODO: <of:NUMOFDEC>0</of:NUMOFDEC> should always be 0, but might want to add code to check
                                Integer messageQuantity = Integer.valueOf(quantityValueStr);

                                // do a few things to try to find the ShipmentItem corresponding to the INVITEM
                                List<GenericValue> shipmentItemList = null;

                                // try getting it by the unit number, which is bogus but can be what some try IFF there is only one INVITEM in the SHPUNIT
                                if (invitemMapList.size() == 1 && localInvItemElementList.size() == 1 && UtilValidate.isNotEmpty(possibleShipmentItemSeqId)) {
                                    GenericValue shipmentItem = delegator.findByPrimaryKey("ShipmentItem", UtilMisc.toMap("shipmentId", shipmentId, "shipmentItemSeqId", possibleShipmentItemSeqId));
                                    if (shipmentItem != null && !productId.equals(shipmentItem.getString("productId"))) {
                                        // found an item, but it was for the wrong Product!
                                        shipmentItem = null;
                                    }
                                    if (shipmentItem != null) {
                                        Debug.logInfo("For Shipment [" + shipmentId + "] found ShipmentItem based on Package/Unit ID, possibleShipmentItemSeqId is [" + possibleShipmentItemSeqId + "]", module);
                                        shipmentItemList = UtilMisc.toList(shipmentItem);
                                    }
                                }

                                if (UtilValidate.isEmpty(shipmentItemList)) {
                                    shipmentItemList = delegator.findByAnd("ShipmentItem", UtilMisc.toMap("shipmentId", shipmentId, "productId",productId));
                                    if (UtilValidate.isEmpty(shipmentItemList)) {
                                        String errMsg = "Could not find Shipment Item for Shipment with ID [" + shipmentId + "] and Product with ID [" + productId + "].";
                                        errorMapList.add(UtilMisc.toMap("reasonCode", "ShipmentItemForProductNotFound", "description", errMsg));
                                        Debug.logError(errMsg, module);
                                        continue;
                                    }

                                    // try to isolate it to one item, ie find the first in the list that matches the quantity
                                    //AND that has not already been used/issued
                                    for (GenericValue shipmentItem : shipmentItemList) {
                                        if (messageQuantity.intValue() == shipmentItem.getDouble("quantity").intValue()) {
                                            // see if there is an ItemIssuance for this ShipmentItem, ie has already had inventory issued to it
                                            //if so then move on, this isn't the ShipmentItem you want
                                            List<GenericValue> itemIssuanceList = delegator.findByAnd("ItemIssuance",
                                                    UtilMisc.toMap("shipmentId", shipmentId, "shipmentItemSeqId", shipmentItem.get("shipmentItemSeqId")));
                                            if (itemIssuanceList.size() == 0) {
                                                // found a match, set the list to be a new list with just this item and then break
                                                shipmentItemList = UtilMisc.toList(shipmentItem);
                                                break;
                                            }
                                        }
                                    }
                                }

                                // TODO: if there is more than one shipmentItem, what to do? split quantity somehow?
                                // for now just get the first item, the other scenario is not yet supported
                                if (shipmentItemList.size() > 1) {
                                    String errMsg = "Could not find single Shipment Item for Shipment with ID [" + shipmentId + "] and Product with ID [" + productId + "], found [" + shipmentItemList.size() + "] and could not narrow down to one.";
                                    errorMapList.add(UtilMisc.toMap("reasonCode", "SingleShipmentItemForProductNotFound", "description", errMsg));
                                    Debug.logError(errMsg, module);
                                    continue;
                                }
                                GenericValue shipmentItem = (GenericValue) shipmentItemList.get(0);

                                String shipmentItemSeqId = shipmentItem.getString("shipmentItemSeqId");
                                GenericValue orderShipment = EntityUtil.getFirst(delegator.findByAnd("OrderShipment", UtilMisc.toMap("shipmentId", shipmentId, "shipmentItemSeqId", shipmentItemSeqId)));
                                if (orderShipment == null) {
                                    String errMsg = "Could not find Order-Shipment record for ShipmentItem with ID [" + shipmentId + "] and Item Seq-ID [" + shipmentItemSeqId + "].";
                                    errorMapList.add(UtilMisc.toMap("reasonCode", "OrderShipmentNotFound", "description", errMsg));
                                    Debug.logError(errMsg, module);
                                    continue;
                                }

                                String orderId = orderShipment.getString("orderId");
                                String orderItemSeqId = orderShipment.getString("orderItemSeqId");
                                GenericValue product = delegator.findByPrimaryKey("Product",UtilMisc.toMap("productId", productId));
                                String requireInventory = product.getString("requireInventory");
                                if (requireInventory == null) {
                                    requireInventory = "N";
                                }

                                // NOTE: there could be more than one reservation record for a given shipment item? for example if there wasn't enough quantity in one inventory item and reservations on two were needed
                                List<GenericValue> orderItemShipGrpInvReservationList = delegator.findByAnd("OrderItemShipGrpInvRes",
                                        UtilMisc.toMap("orderId", orderId, "orderItemSeqId", orderItemSeqId,"shipGroupSeqId",shipGroupSeqId));

                                // find the total quantity for all reservations
                                int totalReserved = 0;
                                for (GenericValue orderItemShipGrpInvReservation : orderItemShipGrpInvReservationList) {
                                    if (orderItemShipGrpInvReservation.getDouble("quantity") != null) {
                                        totalReserved += orderItemShipGrpInvReservation.getDouble("quantity").doubleValue();
                                    }
                                }

                                List<String> serialNumberList = FastList.newInstance();
                                List<? extends Element> invDetailElementList = UtilXml.childElementList(invItemElement, "ns:INVDETAIL"); //n
                                for (Element invDetailElement : invDetailElementList) {
                                    String serialNumber = UtilXml.childElementValue(invDetailElement, "of:SERIALNUM"); // os
                                    if (UtilValidate.isNotEmpty(serialNumber)) {
                                        serialNumberList.add(serialNumber);
                                    }
                                }

                                // do some validations
                                if (UtilValidate.isNotEmpty(serialNumberList)) {
                                    if (messageQuantity.intValue() != serialNumberList.size()) {
                                        String errMsg = "Error: the quantity in the message [" + messageQuantity.intValue() + "] did not match the number of serial numbers passed [" + serialNumberList.size() + "] for ShipmentItem with ID [" + shipmentId + "] and Item Seq-ID [" + shipmentItemSeqId + "].";
                                        errorMapList.add(UtilMisc.toMap("reasonCode", "QuantitySerialMismatch", "description", errMsg));
                                        Debug.logInfo(errMsg, module);
                                        continue;
                                    }
                                }

                                // because there may be more than one ShipmentItem for an OrderItem allow there to be more inventory reservations for the
                                //OrderItem than there is quantity on the current ShipmentItem
                                if ((int) totalReserved < messageQuantity.intValue()) {
                                    String errMsg = "Inventory reservation quantity [" + totalReserved + "] was less than the message quantity [" + messageQuantity.intValue() + "] so cannot receive against reservations for ShipmentItem with ID [" + shipmentId + ":" + shipmentItemSeqId + "], and OrderItem [" + orderShipment.getString("orderId") + ":" + orderShipment.getString("orderItemSeqId") + "]";
                                    errorMapList.add(UtilMisc.toMap("description", errMsg, "reasonCode", "SerialNumbersMissing"));
                                    Debug.logInfo(errMsg, module);
                                    continue;
                                }

                                // just receive quantity for this ShipmentItem
                                int quantityLeft;
                                int shipmentItemQuantity = shipmentItem.getDouble("quantity").intValue();
                                if (shipmentItemQuantity <= messageQuantity.intValue()) {
                                    quantityLeft = shipmentItemQuantity;
                                } else {
                                    quantityLeft = messageQuantity.intValue();
                                }


                                for (GenericValue orderItemShipGrpInvReservation : orderItemShipGrpInvReservationList) {
                                    if (quantityLeft <= 0) {
                                        break;
                                    }
                                    int currentInvResQuantity = orderItemShipGrpInvReservation.getDouble("quantity").intValue();

                                    int quantityToUse;
                                    if (quantityLeft > currentInvResQuantity) {
                                        quantityToUse = currentInvResQuantity;
                                        quantityLeft -= currentInvResQuantity;
                                    } else {
                                        quantityToUse = quantityLeft;
                                        quantityLeft = 0;
                                    }

                                    Map<String, Object> isitspastCtx = UtilMisc.toMap("orderId", (Object) orderId, "shipGroupSeqId", shipGroupSeqId,
                                            "orderItemSeqId", orderItemSeqId);
                                    isitspastCtx.put("productId", productId);
                                    isitspastCtx.put("reservedDatetime", orderItemShipGrpInvReservation.get("reservedDatetime"));
                                    isitspastCtx.put("requireInventory", requireInventory);
                                    isitspastCtx.put("reserveOrderEnumId", orderItemShipGrpInvReservation.get("reserveOrderEnumId"));
                                    isitspastCtx.put("sequenceId", orderItemShipGrpInvReservation.get("sequenceId"));
                                    isitspastCtx.put("originFacilityId", originFacilityId);
                                    isitspastCtx.put("userLogin", userLogin);
                                    isitspastCtx.put("trackingNum", trackingNum);
                                    isitspastCtx.put("inventoryItemId", orderItemShipGrpInvReservation.get("inventoryItemId"));
                                    isitspastCtx.put("shipmentId", shipmentId);
                                    isitspastCtx.put("shipmentPackageSeqId", shipmentPackageSeqId);
                                    isitspastCtx.put("promisedDatetime", orderItemShipGrpInvReservation.get("promisedDatetime"));

                                    if (UtilValidate.isNotEmpty(serialNumberList)) {
                                        for (int i = 0; i < quantityToUse; i++) {
                                            String serialNumber = serialNumberList.get(i);

                                            if (OagisServices.requireSerialNumberExist != null) {
                                                // according to requireSerialNumberExist make sure serialNumber does or does not exist in database, add an error message as needed
                                                Set<String> productIdSet = ProductWorker.getRefurbishedProductIdSet(productId, delegator);
                                                productIdSet.add(productId);

                                                EntityCondition bySerialNumberCondition = EntityCondition.makeCondition(EntityCondition.makeCondition("serialNumber", EntityOperator.EQUALS, serialNumber),
                                                        EntityOperator.AND, EntityCondition.makeCondition("productId", EntityOperator.IN, productIdSet));
                                                List<GenericValue> inventoryItemsBySerialNumber = delegator.findList("InventoryItem", bySerialNumberCondition, null, null, null, false);
                                                if (OagisServices.requireSerialNumberExist.booleanValue()) {
                                                    if (inventoryItemsBySerialNumber.size() == 0) {
                                                        String errMsg = "Referenced serial numbers must already exist, but serial number [" + serialNumber + "] was not found. Product ID(s) considered are: " + productIdSet;
                                                        errorMapList.add(UtilMisc.toMap("description", errMsg, "reasonCode", "SerialNumberRequiredButNotFound"));
                                                        continue;
                                                    }
                                                } else {
                                                    if (inventoryItemsBySerialNumber.size() > 0) {
                                                        String errMsg = "Referenced serial numbers must NOT already exist, but serial number [" + serialNumber + "] already exists. Product ID(s) considered are: " + productIdSet;
                                                        errorMapList.add(UtilMisc.toMap("description", errMsg, "reasonCode", "SerialNumberRequiredNotExistButFound"));
                                                        continue;
                                                    }
                                                }
                                            }

                                            isitspastCtx.put("serialNumber", serialNumber);
                                            isitspastCtx.put("quantity", new Double (1));
                                            isitspastCtx.put("inventoryItemId", orderItemShipGrpInvReservation.get("inventoryItemId"));
                                            isitspastCtx.remove("itemIssuanceId");
                                            Map<String, Object> resultMap = dispatcher.runSync("issueSerializedInvToShipmentPackageAndSetTracking", isitspastCtx);
                                            if (ServiceUtil.isError(resultMap)) {
                                                String errMsg = ServiceUtil.getErrorMessage(resultMap);
                                                errorMapList.add(UtilMisc.toMap("description", errMsg, "reasonCode", "IssueSerializedInvServiceError"));
                                                Debug.logError(errMsg, module);
                                            }
                                        }
                                    } else {
                                        isitspastCtx.put("quantity", new Double(quantityToUse));
                                        // NOTE: this same service is called for non-serialized inventory in spite of the name it is made to handle it
                                        Map<String, Object> resultMap = dispatcher.runSync("issueSerializedInvToShipmentPackageAndSetTracking", isitspastCtx);
                                        if (ServiceUtil.isError(resultMap)) {
                                            String errMsg = ServiceUtil.getErrorMessage(resultMap);
                                            errorMapList.add(UtilMisc.toMap("description", errMsg, "reasonCode", "IssueSerializedInvServiceError"));
                                            Debug.logError(errMsg, module);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }

                if (errorMapList.size() == 0) {
                    // NOTTODOLATER: to support mulitple and partial Show Shipment messages per shipment:
                    //check here if the entire shipment has been issues, ie there should be sufficient
                    //ItemIssuance quantities for the ShipmentItem quantities
                    // NOTE ON THIS DEJ20070906: this is actually really bad because it implies the shipment
                    //has been split and that isn't really allowed; maybe better to return an error!

                    List<GenericValue> shipmentItemList = delegator.findByAnd("ShipmentItem", UtilMisc.toMap("shipmentId", shipmentId));
                    for (GenericValue shipmentItem : shipmentItemList) {
                        int shipmentItemQuantity = shipmentItem.getDouble("quantity").intValue();

                        int totalItemIssuanceQuantity = 0;
                        List<GenericValue> itemIssuanceList = delegator.findByAnd("ItemIssuance", UtilMisc.toMap("shipmentId", shipmentId, "shipmentItemSeqId",
                                shipmentItem.get("shipmentItemSeqId")));
                        for (GenericValue itemIssuance : itemIssuanceList) {
                            totalItemIssuanceQuantity += itemIssuance.getDouble("quantity").intValue();
                        }

                        if (shipmentItemQuantity > totalItemIssuanceQuantity) {
                            String errMsg = "ShipmentItem [" + shipmentId + ":" + shipmentItem.get("shipmentItemSeqId") + "] was not completely fulfilled; shipment item quantity was [" + shipmentItemQuantity + "], but total fulfilled is only [" + totalItemIssuanceQuantity + "]";
                            errorMapList.add(UtilMisc.toMap("description", errMsg, "reasonCode", "ShipmentItemNotCompletelyFulfilled"));
                            Debug.logError(errMsg, module);
                        }
                    }
                }

                if (errorMapList.size() == 0) {
                    Map<String, Object> resultMap = dispatcher.runSync("setShipmentStatusPackedAndShipped",
                            UtilMisc.toMap("shipmentId", shipmentId, "userLogin", userLogin));
                    if (ServiceUtil.isError(resultMap)) {
                        String errMsg = ServiceUtil.getErrorMessage(resultMap);
                        errorMapList.add(UtilMisc.toMap("description", errMsg, "reasonCode", "SetShipmentStatusPackedAndShippedError"));
                        Debug.logError(errMsg, module);
                    }
                }
            } catch (Throwable t) {
                String errMsg = "System Error processing Show Shipment message for shipmentId [" + shipmentId + "] message [" + omiPkMap + "]: " + t.toString();
                errorMapList.add(UtilMisc.toMap("description", errMsg, "reasonCode", "SystemError"));

                try {
                    oagisMsgInfoCtx.put("processingStatusId", "OAGMP_SYS_ERROR");
                    dispatcher.runSync("updateOagisMessageInfo", oagisMsgInfoCtx, 60, true);

                    Map<String, Object> saveErrorMapListCtx = FastMap.newInstance();
                    saveErrorMapListCtx.putAll(omiPkMap);
                    saveErrorMapListCtx.put("errorMapList", errorMapList);
                    saveErrorMapListCtx.put("userLogin", userLogin);
                    dispatcher.runSync("createOagisMsgErrInfosFromErrMapList", saveErrorMapListCtx, 60, true);
                } catch (GenericServiceException e) {
                    String errMsg2 = "Error updating OagisMessageInfo for the Incoming Message: " + e.toString();
                    Debug.logError(e, errMsg2, module);
                }

                Debug.logInfo(t, errMsg, module);
                // in this case we don't want to return a Confirm BOD, so return an error now
                return ServiceUtil.returnError(errMsg);
            }
        }

        Map<String, Object> result = FastMap.newInstance();
        result.putAll(omiPkMap);
        result.put("userLogin", userLogin);

        if (errorMapList.size() > 0) {
            try {
                oagisMsgInfoCtx.put("processingStatusId", "OAGMP_PROC_ERROR");
                dispatcher.runSync("updateOagisMessageInfo", oagisMsgInfoCtx, 60, true);
            } catch (GenericServiceException e) {
                String errMsg = "Error updating OagisMessageInfo for the Incoming Message: " + e.toString();
                Debug.logError(e, errMsg, module);
            }

            try {
                // call services createOagisMsgErrInfosFromErrMapList and for incoming messages oagisSendConfirmBod
                Map<String, Object> saveErrorMapListCtx = FastMap.newInstance();
                saveErrorMapListCtx.putAll(omiPkMap);
                saveErrorMapListCtx.put("errorMapList", errorMapList);
                saveErrorMapListCtx.put("userLogin", userLogin);
                dispatcher.runSync("createOagisMsgErrInfosFromErrMapList", saveErrorMapListCtx, 60, true);
            } catch (GenericServiceException e) {
                String errMsg = "Error updating OagisMessageInfo for the Incoming Message: " + e.toString();
                Debug.logError(e, errMsg, module);
            }

            try {
                Map<String, Object> sendConfirmBodCtx = FastMap.newInstance();
                sendConfirmBodCtx.putAll(omiPkMap);
                sendConfirmBodCtx.put("errorMapList", errorMapList);
                sendConfirmBodCtx.put("userLogin", userLogin);
                // NOTE: this is different for each service, should be shipmentId or returnId or PO orderId or etc
                sendConfirmBodCtx.put("origRefId", shipmentId);

                // run async because this will send a message back to the other server and may take some time, and/or fail
                dispatcher.runAsync("oagisSendConfirmBod", sendConfirmBodCtx, null, true, 60, true);
            } catch (GenericServiceException e) {
                String errMsg = "Error sending Confirm BOD: " + e.toString();
                Debug.logError(e, errMsg, module);
            }

            String errMsg = "Found business level errors in message processing, not saving those changes but saving error messages; first error is: " + errorMapList.get(0);

            // return success here so that the message won't be retried and the Confirm BOD, etc won't be sent multiple times
            result.putAll(ServiceUtil.returnSuccess(errMsg));

            // however, we still don't want to save the partial results, so set rollbackOnly
            try {
                TransactionUtil.setRollbackOnly(errMsg, null);
            } catch (GenericTransactionException e) {
                Debug.logError(e, "Error setting rollback only ", module);
            }

            return result;
        } else {
            try {
                oagisMsgInfoCtx.put("processingStatusId", "OAGMP_PROC_SUCCESS");
                dispatcher.runSync("updateOagisMessageInfo", oagisMsgInfoCtx, 60, true);
            } catch (GenericServiceException e) {
                String errMsg = "Error updating OagisMessageInfo for the Incoming Message: " + e.toString();
                Debug.logError(e, errMsg, module);
            }
        }

        result.putAll(ServiceUtil.returnSuccess("Service Completed Successfully"));
        return result;
    }

    public static Map<String, Object> oagisSendProcessShipmentsFromBackOrderSet(DispatchContext ctx, Map<String, Object> context) {
        LocalDispatcher dispatcher = ctx.getDispatcher();

        Set<String> noLongerOnBackOrderIdSet = UtilGenerics.checkSet(context.get("noLongerOnBackOrderIdSet"), String.class);
        Debug.logInfo("Running oagisSendProcessShipmentsFromBackOrderSet with noLongerOnBackOrderIdSet=" + noLongerOnBackOrderIdSet, module);
        if (UtilValidate.isEmpty(noLongerOnBackOrderIdSet)) {
            return ServiceUtil.returnSuccess();
        }

        try {
            for (String orderId : noLongerOnBackOrderIdSet) {
                dispatcher.runAsync("oagisSendProcessShipment", UtilMisc.toMap("orderId", orderId), true);
            }
        } catch (GenericServiceException e) {
            String errMsg = "Error calling oagisSendProcessShipment service for orders with items no longer on backorder: " + e.toString();
            return ServiceUtil.returnError(errMsg);
        }

        return ServiceUtil.returnSuccess();
    }

    public static Map<String, Object> oagisSendProcessShipment(DispatchContext ctx, Map<String, Object> context) {
        LocalDispatcher dispatcher = ctx.getDispatcher();
        Delegator delegator = ctx.getDelegator();
        String orderId = (String) context.get("orderId");

        // Check if order is not on back order before processing shipment
        try {
            Map<String, Object> checkOrderResp = dispatcher.runSync("checkOrderIsOnBackOrder", UtilMisc.toMap("orderId", orderId));
            if (((Boolean) checkOrderResp.get("isBackOrder")).booleanValue()) {
                Debug.logWarning("Order [" + orderId + "] is on back order, cannot Process Shipment", module);
                return ServiceUtil.returnSuccess();
            }
        } catch (GenericServiceException e) {
            Debug.logError(e, module);
            return ServiceUtil.returnError(e.getMessage());
        }

        String sendToUrl = (String) context.get("sendToUrl");
        if (UtilValidate.isEmpty(sendToUrl)) {
            sendToUrl = UtilProperties.getPropertyValue("oagis.properties", "url.send.processShipment");
        }

        String saveToFilename = (String) context.get("saveToFilename");
        if (UtilValidate.isEmpty(saveToFilename)) {
            String saveToFilenameBase = UtilProperties.getPropertyValue("oagis.properties", "test.save.outgoing.filename.base", "");
            if (UtilValidate.isNotEmpty(saveToFilenameBase)) {
                saveToFilename = saveToFilenameBase + "ProcessShipment" + orderId + ".xml";
            }
        }
        String saveToDirectory = (String) context.get("saveToDirectory");
        if (UtilValidate.isEmpty(saveToDirectory)) {
            saveToDirectory = UtilProperties.getPropertyValue("oagis.properties", "test.save.outgoing.directory");
        }

        OutputStream out = (OutputStream) context.get("outputStream");

        if (Debug.infoOn()) Debug.logInfo("Call to oagisSendProcessShipment for orderId [" + orderId + "], sendToUrl=[" + sendToUrl + "], saveToDirectory=[" + saveToDirectory + "], saveToFilename=[" + saveToFilename + "]", module);

        Map<String, Object> result = ServiceUtil.returnSuccess();
        MapStack<String> bodyParameters =  MapStack.create();
        bodyParameters.put("orderId", orderId);

        // the userLogin passed in will usually be the customer, so don't use it; use the system user instead
        GenericValue userLogin = null;
        try {
            userLogin = delegator.findByPrimaryKey("UserLogin", UtilMisc.toMap("userLoginId", "system"));
        } catch (GenericEntityException e) {
            Debug.logError(e, "Error getting userLogin", module);
        }

        GenericValue orderHeader = null;
        GenericValue orderItemShipGroup = null;

        String logicalId = UtilProperties.getPropertyValue("oagis.properties", "CNTROLAREA.SENDER.LOGICALID");
        String referenceId = null;
        String task = "SHIPREQUEST"; // Actual value of task is "SHIPREQUEST" which is more than 10 char, need this in the db so it will match Confirm BODs, etc
        String component = "INVENTORY";
        Map<String, String> omiPkMap = null;

        String shipmentId = null;

        try {
            // see if there are any OagisMessageInfo for this order that are in the OAGMP_OGEN_SUCCESS or OAGMP_SENT statuses, if so don't send again; these need to be manually reviewed before resending to avoid accidental duplicate messages
            List<GenericValue> previousOagisMessageInfoList = delegator.findByAnd("OagisMessageInfo",
                    UtilMisc.toMap("orderId", orderId, "task", task, "component", component));
            if (EntityUtil.filterByAnd(previousOagisMessageInfoList, UtilMisc.toMap("processingStatusId", "OAGMP_OGEN_SUCCESS")).size() > 0) {
                // this isn't really an error, just a failed constraint so return success
                String successMsg = "Found existing message info(s) in OAGMP_OGEN_SUCCESS, so not sending Process Shipment message for order [" + orderId + "] existing message(s) are: " + EntityUtil.filterByAnd(previousOagisMessageInfoList, UtilMisc.toMap("processingStatusId", "OAGMP_OGEN_SUCCESS"));
                return ServiceUtil.returnSuccess(successMsg);
            }
            if (EntityUtil.filterByAnd(previousOagisMessageInfoList, UtilMisc.toMap("processingStatusId", "OAGMP_SENT")).size() > 0) {
                // this isn't really an error, just a failed constraint so return success
                String successMsg = "Found existing message info(s) in OAGMP_SENT status, so not sending Process Shipment message for order [" + orderId + "] existing message(s) are: " + EntityUtil.filterByAnd(previousOagisMessageInfoList, UtilMisc.toMap("processingStatusId", "OAGMP_SENT"));
                return ServiceUtil.returnSuccess(successMsg);
            }

            orderHeader = delegator.findByPrimaryKey("OrderHeader", UtilMisc.toMap("orderId", orderId));
            if (orderHeader == null) {
                return ServiceUtil.returnError("Could not find OrderHeader with ID [" + orderId + "]");
            }

            List<String> validStores = StringUtil.split(UtilProperties.getPropertyValue("oagis.properties", "Oagis.Order.ValidProductStores"), ",");
            if (UtilValidate.isNotEmpty(validStores)) {
                if (!validStores.contains(orderHeader.getString("productStoreId"))) {
                    return ServiceUtil.returnSuccess("Order [" + orderId + "] placed is not for valid Store(s)");
                }
            }
            String orderStatusId = orderHeader.getString("statusId");
            if (!"ORDER_APPROVED".equals(orderStatusId)) {
                return ServiceUtil.returnSuccess("OrderHeader not in the approved status (ORDER_APPROVED) with ID [" + orderId + "], is in the [" + orderStatusId + "] status");
            }
            if (!"SALES_ORDER".equals(orderHeader.getString("orderTypeId"))) {
                return ServiceUtil.returnError("OrderHeader not a sales order (SALES_ORDER) with ID [" + orderId + "]");
            }

            // first check some things...
            OrderReadHelper orderReadHelper = new OrderReadHelper(orderHeader);
            // before doing or saving anything see if any OrderItems are Products with isPhysical=Y
            if (!orderReadHelper.hasPhysicalProductItems()) {
                // no need to process shipment, return success
                return ServiceUtil.returnSuccess();
            }
            if (!orderReadHelper.hasShippingAddress()) {
                return ServiceUtil.returnError("Cannot send Process Shipment for order [" + orderId + "], it has no shipping address.");
            }

            // check payment authorization
            Map<String, Object> authServiceContext = FastMap.newInstance();
            authServiceContext.put("orderId", orderId);
            authServiceContext.put("userLogin", userLogin);
            authServiceContext.put("reAuth", true);
            Map<String, Object> authResult = dispatcher.runSync("authOrderPayments", authServiceContext);
            if (!authResult.get("processResult").equals("APPROVED")) {
                return ServiceUtil.returnError("No authorized payment available, not sending Process Shipment");
            }

            referenceId = delegator.getNextSeqId("OagisMessageInfo");
            omiPkMap = UtilMisc.toMap("logicalId", logicalId, "component", component, "task", task, "referenceId", referenceId);

            String authId = UtilProperties.getPropertyValue("oagis.properties", "CNTROLAREA.SENDER.AUTHID");
            Timestamp timestamp = UtilDateTime.nowTimestamp();
            String sentDate = OagisServices.isoDateFormat.format(timestamp);

            bodyParameters.putAll(omiPkMap);
            bodyParameters.put("authId", authId);
            bodyParameters.put("sentDate", sentDate);

            // prepare map to Create Oagis Message Info
            try {
                Map<String, Object> comiCtx = FastMap.newInstance();
                comiCtx.putAll(omiPkMap);
                comiCtx.put("processingStatusId", "OAGMP_TRIGGERED");
                comiCtx.put("outgoingMessage", "Y");
                comiCtx.put("confirmation", "1");
                comiCtx.put("bsrVerb", "PROCESS");
                comiCtx.put("bsrNoun", "SHIPMENT");
                comiCtx.put("bsrRevision", "001");
                comiCtx.put("orderId", orderId);
                comiCtx.put("sentDate", timestamp);
                comiCtx.put("authId", authId);
                comiCtx.put("userLogin", userLogin);
                dispatcher.runSync("createOagisMessageInfo", comiCtx, 60, true);
            } catch (GenericServiceException e) {
                String errMsg = UtilProperties.getMessage(ServiceUtil.resource, "OagisErrorInCreatingDataForOagisMessageInfoEntity", (Locale) context.get("locale"));
                Debug.logError(e, errMsg, module);
            }
            if (Debug.infoOn()) Debug.logInfo("Saved OagisMessageInfo for oagisSendProcessShipment message for orderId [" + orderId + "]", module);

            // check to see if there is already a Shipment for this order
            EntityCondition findShipmentCondition = EntityCondition.makeCondition(UtilMisc.toList(
                    EntityCondition.makeCondition("primaryOrderId", EntityOperator.EQUALS, orderId),
                    EntityCondition.makeCondition("statusId", EntityOperator.NOT_EQUAL, "SHIPMENT_CANCELLED")
                   ), EntityOperator.AND);
            List<GenericValue> shipmentList = delegator.findList("Shipment", findShipmentCondition, null, null, null, false);
            GenericValue shipment = EntityUtil.getFirst(shipmentList);

            if (shipment != null) {
                // if picked, packed, shipped, delivered then complain, no reason to process the shipment!
                String statusId = shipment.getString("statusId");
                if ("SHIPMENT_PICKED".equals(statusId) || "SHIPMENT_PACKED".equals(statusId) || "SHIPMENT_SHIPPED".equals(statusId) || "SHIPMENT_DELIVERED".equals(statusId)) {
                    return ServiceUtil.returnError("Not sending Process Shipment message because found Shipment that is already being processed, is in status [" + statusId + "]");
                }
                shipmentId = shipment.getString("shipmentId");
            } else {
                Map<String, Object> cospResult= dispatcher.runSync("createOrderShipmentPlan", UtilMisc.<String, Object>toMap("orderId", orderId, "userLogin", userLogin));
                shipmentId = (String) cospResult.get("shipmentId");
                shipment = delegator.findByPrimaryKey("Shipment", UtilMisc.toMap("shipmentId", shipmentId));
            }

            bodyParameters.put("shipment", shipment);
            List<GenericValue> shipmentItems = delegator.findByAnd("ShipmentItem", UtilMisc.toMap("shipmentId", shipmentId));
            bodyParameters.put("shipmentItems", shipmentItems);

            GenericValue  address = EntityUtil.getFirst(orderReadHelper.getShippingLocations());
            bodyParameters.put("address", address);
            String emailString = orderReadHelper.getOrderEmailString();
            bodyParameters.put("emailString", emailString);
            String contactMechId = shipment.getString("destinationTelecomNumberId");

            GenericValue telecomNumber = delegator.findByPrimaryKey("TelecomNumber", UtilMisc.toMap("contactMechId", contactMechId));
            if (telecomNumber == null) {
                return ServiceUtil.returnError("In Send ProcessShipment Telecom number not found for orderId [" + orderId + "]");
            }
            bodyParameters.put("telecomNumber", telecomNumber);

            orderItemShipGroup = EntityUtil.getFirst(delegator.findByAnd("OrderItemShipGroup", UtilMisc.toMap("orderId", orderId)));
            bodyParameters.put("orderItemShipGroup", orderItemShipGroup);
            Set<String> correspondingPoIdSet = FastSet.newInstance();

            List<GenericValue> orderItems = orderReadHelper.getOrderItems();
            for (GenericValue orderItem : orderItems) {
                String correspondingPoId = orderItem.getString("correspondingPoId");
                if (correspondingPoId != null) {
                    correspondingPoIdSet.add(correspondingPoId);
                }
            }
            bodyParameters.put("correspondingPoIdSet", correspondingPoIdSet);
            if (orderHeader.get("externalId") != null) {
                Set<String> externalIdSet = FastSet.newInstance();
                externalIdSet.add(orderHeader.getString("externalId"));
                bodyParameters.put("externalIdSet", externalIdSet);
            }
            // Check if order was a return replacement order (associated with return)
            GenericValue returnItemResponse = EntityUtil.getFirst(delegator.findByAnd("ReturnItemResponse", UtilMisc.toMap("replacementOrderId", orderId)));
            if (returnItemResponse != null) {
                boolean includeReturnLabel = false;

                // Get the associated return Id (replaceReturnId)
                String returnItemResponseId = returnItemResponse.getString("returnItemResponseId");
                List<GenericValue> returnItemList = delegator.findByAnd("ReturnItem", UtilMisc.toMap("returnItemResponseId", returnItemResponseId));
                GenericValue firstReturnItem = EntityUtil.getFirst(returnItemList);
                if (firstReturnItem != null) {
                    bodyParameters.put("replacementReturnId", firstReturnItem.getString("returnId"));
                } else {
                    Debug.logWarning("Could not find a ReturnItem for returnItemResponseId [" + returnItemResponseId + "]; this really shouldn't happen but isn't a real error either. It means a ReturnItemResponse was created but not attached to any item!", module);
                }

                // return label should only be sent when we want a return label to be included; this would be for a cross-ship replacement type ReturnItem

                // go through the returnItemList and if any are cross-ship replacement, then include a label (not for wait replacement in other words)
                for (GenericValue returnItem : returnItemList) {
                    if ("RTN_CSREPLACE".equals(returnItem.getString("returnTypeId"))) {
                        includeReturnLabel = true;
                    }
                }

                if (includeReturnLabel) {
                    bodyParameters.put("shipnotes", "RETURNLABEL");
                }

            }
            // tracking shipper account, other Party info
            String partyId = shipment.getString("partyIdTo");
            bodyParameters.put("partyNameView", delegator.findByPrimaryKey("PartyNameView", UtilMisc.toMap("partyId", partyId)));
            List<GenericValue> partyCarrierAccounts = delegator.findByAnd("PartyCarrierAccount", UtilMisc.toMap("partyId", partyId));
            partyCarrierAccounts = EntityUtil.filterByDate(partyCarrierAccounts);
            if (partyCarrierAccounts != null) {
                for (GenericValue partyCarrierAccount : partyCarrierAccounts) {
                    String carrierPartyId = partyCarrierAccount.getString("carrierPartyId");
                    if (carrierPartyId.equals(orderItemShipGroup.getString("carrierPartyId"))) {
                        String accountNumber = partyCarrierAccount.getString("accountNumber");
                        bodyParameters.put("shipperId", accountNumber);
                    }
                }
            }

            bodyParameters.put("shipmentId", shipmentId);
            bodyParameters.put("orderId", orderId);
            bodyParameters.put("userLogin", userLogin);

            String bodyScreenUri = UtilProperties.getPropertyValue("oagis.properties", "Oagis.Template.ProcessShipment");
            String outText = null;
            Writer writer = new StringWriter();
            ScreenRenderer screens = new ScreenRenderer(writer, bodyParameters, htmlScreenRenderer);
            screens.render(bodyScreenUri);
            writer.close();
            outText = writer.toString();
            if (Debug.infoOn()) Debug.logInfo("Finished rendering oagisSendProcessShipment message for orderId [" + orderId + "]", module);

            try {
                Map<String, Object> uomiCtx = FastMap.newInstance();
                uomiCtx.putAll(omiPkMap);
                uomiCtx.put("processingStatusId", "OAGMP_OGEN_SUCCESS");
                uomiCtx.put("shipmentId", shipmentId);
                uomiCtx.put("userLogin", userLogin);
                if (OagisServices.debugSaveXmlOut) {
                    uomiCtx.put("fullMessageXml", outText);
                }
                dispatcher.runSync("updateOagisMessageInfo", uomiCtx, 60, true);
            } catch (GenericServiceException e) {
                String errMsg = UtilProperties.getMessage(ServiceUtil.resource, "OagisErrorInCreatingDataForOagisMessageInfoEntity", (Locale) context.get("locale"));
                Debug.logError(e, errMsg, module);
            }

            Map<String, Object> sendMessageReturn = OagisServices.sendMessageText(outText, out, sendToUrl, saveToDirectory, saveToFilename);
            if (sendMessageReturn != null && ServiceUtil.isError(sendMessageReturn)) {
                try {
                    Map<String, Object> uomiCtx = FastMap.newInstance();
                    uomiCtx.putAll(omiPkMap);
                    uomiCtx.put("processingStatusId", "OAGMP_SEND_ERROR");
                    uomiCtx.put("userLogin", userLogin);
                    dispatcher.runSync("updateOagisMessageInfo", uomiCtx, 60, true);
                } catch (GenericServiceException e) {
                    String errMsg = UtilProperties.getMessage(ServiceUtil.resource, "OagisErrorInCreatingDataForOagisMessageInfoEntity", (Locale) context.get("locale"));
                    Debug.logError(e, errMsg, module);
                }
                return sendMessageReturn;
            }

            if (Debug.infoOn()) Debug.logInfo("Message send done for oagisSendProcessShipment for orderId [" + orderId + "], sendToUrl=[" + sendToUrl + "], saveToDirectory=[" + saveToDirectory + "], saveToFilename=[" + saveToFilename + "]", module);
            try {
                Map<String, Object> uomiCtx = FastMap.newInstance();
                uomiCtx.putAll(omiPkMap);
                uomiCtx.put("processingStatusId", "OAGMP_SENT");
                uomiCtx.put("userLogin", userLogin);
                dispatcher.runSync("updateOagisMessageInfo", uomiCtx, 60, true);
            } catch (GenericServiceException e) {
                String errMsg = UtilProperties.getMessage(ServiceUtil.resource, "OagisErrorInCreatingDataForOagisMessageInfoEntity", (Locale) context.get("locale"));
                Debug.logError(e, errMsg, module);
            }
        } catch (Throwable t) {
            String errMsg = "System Error doing Process Shipment message for orderId [" + orderId + "] shipmentId [" + shipmentId + "] message [" + omiPkMap + "]: " + t.toString();
            Debug.logError(t, errMsg, module);

            // if we have a referenceId and the omiPkMap not null, save the error status
            if (omiPkMap != null) {
                try {
                    // only do this if there is a record already in place
                    if (delegator.findByPrimaryKey("OagisMessageInfo", omiPkMap) == null) {
                        return ServiceUtil.returnError(errMsg);
                    }

                    Map<String, Object> uomiCtx = FastMap.newInstance();
                    uomiCtx.putAll(omiPkMap);
                    uomiCtx.put("processingStatusId", "OAGMP_SYS_ERROR");
                    uomiCtx.put("bsrVerb", "PROCESS");
                    uomiCtx.put("bsrNoun", "SHIPMENT");
                    uomiCtx.put("orderId", orderId);
                    uomiCtx.put("shipmentId", shipmentId);
                    uomiCtx.put("userLogin", userLogin);
                    dispatcher.runSync("updateOagisMessageInfo", uomiCtx, 60, true);

                    List<Map<String, String>> errorMapList = UtilMisc.toList(UtilMisc.toMap("description", errMsg, "reasonCode", "SystemError"));
                    Map<String, Object> saveErrorMapListCtx = FastMap.newInstance();
                    saveErrorMapListCtx.putAll(omiPkMap);
                    saveErrorMapListCtx.put("errorMapList", errorMapList);
                    saveErrorMapListCtx.put("userLogin", userLogin);
                    dispatcher.runSync("createOagisMsgErrInfosFromErrMapList", saveErrorMapListCtx, 60, true);
                } catch (GeneralException e) {
                    String errMsg2 = "Error saving message error info: " + e.toString();
                    Debug.logError(e, errMsg2, module);
                }
            }

            return ServiceUtil.returnError(errMsg);
        }
        return result;
    }

    public static Map<String, Object> oagisSendReceiveDelivery(DispatchContext dctx, Map<String, Object> context) {
        LocalDispatcher dispatcher = dctx.getDispatcher();
        Delegator delegator = dctx.getDelegator();
        String returnId = (String) context.get("returnId");

        String sendToUrl = (String) context.get("sendToUrl");
        if (UtilValidate.isEmpty(sendToUrl)) {
            sendToUrl = UtilProperties.getPropertyValue("oagis.properties", "url.send.receiveDelivery");
        }

        String saveToFilename = (String) context.get("saveToFilename");
        if (UtilValidate.isEmpty(saveToFilename)) {
            String saveToFilenameBase = UtilProperties.getPropertyValue("oagis.properties", "test.save.outgoing.filename.base", "");
            if (UtilValidate.isNotEmpty(saveToFilenameBase)) {
                saveToFilename = saveToFilenameBase + "ReceiveDelivery" + returnId + ".xml";
            }
        }
        String saveToDirectory = (String) context.get("saveToDirectory");
        if (UtilValidate.isEmpty(saveToDirectory)) {
            saveToDirectory = UtilProperties.getPropertyValue("oagis.properties", "test.save.outgoing.directory");
        }

        GenericValue userLogin = null;
        try {
            userLogin = delegator.findByPrimaryKey("UserLogin", UtilMisc.toMap("userLoginId", "system"));
        } catch (GenericEntityException e) {
            Debug.logError(e, "Error getting system userLogin", module);
        }

        OutputStream out = (OutputStream) context.get("outputStream");

        Map<String, Object> result = ServiceUtil.returnSuccess();
        MapStack<String> bodyParameters =  MapStack.create();

        String orderId = null;

        String referenceId = null;
        String task = "RMA"; // Actual value of task is "SHIPREQUEST" which is more than 10 char, need this in the db so it will match Confirm BODs, etc
        String component = "INVENTORY";
        Map<String, String> omiPkMap = null;

        try {
            // see if there are any OagisMessageInfo for this order that are in the OAGMP_OGEN_SUCCESS or OAGMP_SENT statuses, if so don't send again; these need to be manually reviewed before resending to avoid accidental duplicate messages
            List<GenericValue> previousOagisMessageInfoList = delegator.findByAnd("OagisMessageInfo",
                    UtilMisc.toMap("returnId", returnId, "task", task, "component", component));
            if (EntityUtil.filterByAnd(previousOagisMessageInfoList, UtilMisc.toMap("processingStatusId", "OAGMP_OGEN_SUCCESS")).size() > 0) {
                // this isn't really an error, just a failed constraint so return success
                String successMsg = "Found existing message info(s) in OAGMP_OGEN_SUCCESS, so not sending Receive Delivery message for return [" + returnId + "] existing message(s) are: " + EntityUtil.filterByAnd(previousOagisMessageInfoList, UtilMisc.toMap("processingStatusId", "OAGMP_OGEN_SUCCESS"));
                return ServiceUtil.returnSuccess(successMsg);
            }
            if (EntityUtil.filterByAnd(previousOagisMessageInfoList, UtilMisc.toMap("processingStatusId", "OAGMP_SENT")).size() > 0) {
                // this isn't really an error, just a failed constraint so return success
                String successMsg = "Found existing message info(s) in OAGMP_SENT status, so not sending Receive Delivery message for return [" + returnId + "] existing message(s) are: " + EntityUtil.filterByAnd(previousOagisMessageInfoList, UtilMisc.toMap("processingStatusId", "OAGMP_SENT"));
                return ServiceUtil.returnSuccess(successMsg);
            }

            GenericValue returnHeader = delegator.findByPrimaryKey("ReturnHeader", UtilMisc.toMap("returnId", returnId));
            if (returnHeader == null) {
                return ServiceUtil.returnError("Could not find Return with ID [" + returnId + "]");
            }
            String statusId = returnHeader.getString("statusId");
            if (!"RETURN_ACCEPTED".equals(statusId)) {
                return ServiceUtil.returnError("Return with ID [" + returnId + "] no in accepted status (RETURN_ACCEPTED)");
            }

            List<GenericValue> returnItems = delegator.findByAnd("ReturnItem", UtilMisc.toMap("returnId", returnId));
            bodyParameters.put("returnItems", returnItems);

            orderId = EntityUtil.getFirst(returnItems).getString("orderId");

            String logicalId = UtilProperties.getPropertyValue("oagis.properties", "CNTROLAREA.SENDER.LOGICALID");
            String authId = UtilProperties.getPropertyValue("oagis.properties", "CNTROLAREA.SENDER.AUTHID");

            referenceId = delegator.getNextSeqId("OagisMessageInfo");
            omiPkMap = UtilMisc.toMap("logicalId", logicalId, "component", component, "task", task, "referenceId", referenceId);

            Timestamp nowTimestamp = UtilDateTime.nowTimestamp();
            String sentDate = OagisServices.isoDateFormat.format(nowTimestamp);

            bodyParameters.putAll(omiPkMap);
            bodyParameters.put("authId", authId);
            bodyParameters.put("sentDate", sentDate);

            // prepare map to Create Oagis Message Info
            try {
                Map<String, Object> comiCtx = FastMap.newInstance();
                comiCtx.putAll(omiPkMap);
                comiCtx.put("outgoingMessage", "Y");
                comiCtx.put("confirmation", "1");
                comiCtx.put("bsrVerb", "RECEIVE");
                comiCtx.put("bsrNoun", "DELIVERY");
                comiCtx.put("bsrRevision", "001");
                comiCtx.put("returnId", returnId);
                comiCtx.put("orderId", orderId);
                comiCtx.put("authId", authId);
                comiCtx.put("sentDate", nowTimestamp);
                comiCtx.put("processingStatusId", "OAGMP_TRIGGERED");
                comiCtx.put("userLogin", userLogin);
                dispatcher.runSync("createOagisMessageInfo", comiCtx, 60, true);
            } catch (GenericServiceException e) {
                String errMsg = UtilProperties.getMessage(ServiceUtil.resource, "OagisErrorInCreatingDataForOagisMessageInfoEntity", (Locale) context.get("locale"));
                Debug.logError(e, errMsg, module);
            }

            GenericValue orderHeader = delegator.findByPrimaryKey("OrderHeader", UtilMisc.toMap("orderId", orderId));
            if (orderHeader == null) {
                return ServiceUtil.returnError("No valid Order with [" + orderId + "] found, cannot process Return");
            }

            String partyId = returnHeader.getString("fromPartyId");
            GenericValue postalAddress = delegator.findByPrimaryKey("PostalAddress", UtilMisc.toMap("contactMechId", returnHeader.getString("originContactMechId")));
            bodyParameters.put("postalAddress", postalAddress);
            bodyParameters.put("partyNameView", delegator.findByPrimaryKey("PartyNameView", UtilMisc.toMap("partyId", partyId)));

            // calculate total qty of return items in a shipping unit received, order associated with return
            double totalQty = 0.0;
            Map<String, List<String>> serialNumberListByReturnItemSeqIdMap = FastMap.newInstance();
            bodyParameters.put("serialNumberListByReturnItemSeqIdMap", serialNumberListByReturnItemSeqIdMap);
            for (GenericValue returnItem : returnItems) {
                double itemQty = returnItem.getDouble("returnQuantity").doubleValue();
                totalQty += itemQty;

                // for each ReturnItem also get serial numbers using ItemIssuanceAndInventoryItem
                // NOTE: technically if the ReturnItem.quantity != OrderItem.quantity then we don't know which serial number is being returned, so rather than guessing we will send it only in that case
                GenericValue orderItem = returnItem.getRelatedOne("OrderItem");
                if (orderItem != null) {
                    if (orderItem.getDouble("quantity").doubleValue() == itemQty) {
                        List<GenericValue> itemIssuanceAndInventoryItemList = delegator.findByAnd("ItemIssuanceAndInventoryItem",
                                UtilMisc.toMap("orderId", orderItem.get("orderId"), "orderItemSeqId", orderItem.get("orderItemSeqId"),
                                        "inventoryItemTypeId", "SERIALIZED_INV_ITEM"));
                        if (itemIssuanceAndInventoryItemList.size() == itemQty) {
                            List<String> serialNumberList = FastList.newInstance();
                            serialNumberListByReturnItemSeqIdMap.put(returnItem.getString("returnItemSeqId"), serialNumberList);
                            for (GenericValue itemIssuanceAndInventoryItem : itemIssuanceAndInventoryItemList) {
                                serialNumberList.add(itemIssuanceAndInventoryItem.getString("serialNumber"));
                            }
                        } else {
                            // TODO: again a quantity mismatch, whatever to do?
                            // just logging this as info because the product may not be serialized or have serialized inventory
                            Debug.logInfo("Number of serial numbers [" + itemIssuanceAndInventoryItemList.size() + "] did not match quantity [" + itemQty + "] for return item: " + returnItem.getPrimaryKey() + "; may not be a serialized inventory product", module);
                        }
                    } else {
                        // TODO: we don't know which serial numbers are returned, should we throw an error? probably not, just do what we can
                        Debug.logWarning("Could not get matching serial numbers because order item quantity [" + orderItem.getDouble("quantity") + "] did not match quantity [" + itemQty + "] for return item: " + returnItem.getPrimaryKey(), module);
                    }
                }
            }
            bodyParameters.put("totalQty", new Double(totalQty));

            String emailString = PartyWorker.findPartyLatestContactMech(partyId, "EMAIL_ADDRESS", delegator).getString("infoString");
            bodyParameters.put("emailString", emailString);

            GenericValue telecomNumber = PartyWorker.findPartyLatestTelecomNumber(partyId, delegator);
            bodyParameters.put("telecomNumber", telecomNumber);

            String entryDate = OagisServices.isoDateFormat.format(returnHeader.getTimestamp("entryDate"));
            bodyParameters.put("entryDate", entryDate);

            bodyParameters.put("returnId", returnId);

            String bodyScreenUri = UtilProperties.getPropertyValue("oagis.properties", "Oagis.Template.ReceiveDelivery");
            Writer writer = new StringWriter();
            ScreenRenderer screens = new ScreenRenderer(writer, bodyParameters, htmlScreenRenderer);
            screens.render(bodyScreenUri);
            writer.close();
            String outText = writer.toString();

            try {
                Map<String, Object> uomiCtx = FastMap.newInstance();
                uomiCtx.putAll(omiPkMap);
                uomiCtx.put("processingStatusId", "OAGMP_OGEN_SUCCESS");
                uomiCtx.put("userLogin", userLogin);
                if (OagisServices.debugSaveXmlOut) {
                    uomiCtx.put("fullMessageXml", outText);
                }
                dispatcher.runSync("updateOagisMessageInfo", uomiCtx, 60, true);
            } catch (GenericServiceException e) {
                String errMsg = UtilProperties.getMessage(ServiceUtil.resource, "OagisErrorInCreatingDataForOagisMessageInfoEntity", (Locale) context.get("locale"));
                Debug.logError(e, errMsg, module);
            }

            Map<String, Object> sendMessageReturn = OagisServices.sendMessageText(outText, out, sendToUrl, saveToDirectory, saveToFilename);
            if (sendMessageReturn != null && ServiceUtil.isError(sendMessageReturn)) {
                try {
                    Map<String, Object> uomiCtx = FastMap.newInstance();
                    uomiCtx.putAll(omiPkMap);
                    uomiCtx.put("processingStatusId", "OAGMP_SEND_ERROR");
                    uomiCtx.put("userLogin", userLogin);
                    dispatcher.runSync("updateOagisMessageInfo", uomiCtx, 60, true);
                } catch (GenericServiceException e) {
                    String errMsg = UtilProperties.getMessage(ServiceUtil.resource, "OagisErrorInCreatingDataForOagisMessageInfoEntity", (Locale) context.get("locale"));
                    Debug.logError(e, errMsg, module);
                }
                return sendMessageReturn;
            }

            try {
                Map<String, Object> uomiCtx = FastMap.newInstance();
                uomiCtx.putAll(omiPkMap);
                uomiCtx.put("processingStatusId", "OAGMP_SENT");
                uomiCtx.put("userLogin", userLogin);
                dispatcher.runSync("updateOagisMessageInfo", uomiCtx, 60, true);
            } catch (GenericServiceException e) {
                String errMsg = UtilProperties.getMessage(ServiceUtil.resource, "OagisErrorInCreatingDataForOagisMessageInfoEntity", (Locale) context.get("locale"));
                Debug.logError(e, errMsg, module);
            }
        } catch (Throwable t) {
            String errMsg = "System Error doing Receive Delivery message for returnId [" + returnId + "] orderId [" + orderId + "] message [" + omiPkMap + "]: " + t.toString();
            Debug.logError(t, errMsg, module);

            // if we have a referenceId and the omiPkMap not null, save the error status
            if (omiPkMap != null) {
                try {
                    // only do this if there is a record already in place
                    if (delegator.findByPrimaryKey("OagisMessageInfo", omiPkMap) == null) {
                        return ServiceUtil.returnError(errMsg);
                    }

                    Map<String, Object> uomiCtx = FastMap.newInstance();
                    uomiCtx.putAll(omiPkMap);
                    uomiCtx.put("processingStatusId", "OAGMP_SYS_ERROR");
                    uomiCtx.put("bsrVerb", "RECEIVE");
                    uomiCtx.put("bsrNoun", "DELIVERY");
                    uomiCtx.put("returnId", returnId);
                    uomiCtx.put("orderId", orderId);
                    uomiCtx.put("userLogin", userLogin);
                    dispatcher.runSync("updateOagisMessageInfo", uomiCtx, 60, true);

                    List<Map<String, String>> errorMapList = UtilMisc.toList(UtilMisc.toMap("description", errMsg, "reasonCode", "SystemError"));
                    Map<String, Object> saveErrorMapListCtx = FastMap.newInstance();
                    saveErrorMapListCtx.putAll(omiPkMap);
                    saveErrorMapListCtx.put("errorMapList", errorMapList);
                    saveErrorMapListCtx.put("userLogin", userLogin);
                    dispatcher.runSync("createOagisMsgErrInfosFromErrMapList", saveErrorMapListCtx, 60, true);
                } catch (GeneralException e) {
                    String errMsg2 = "Error saving message error info: " + e.toString();
                    Debug.logError(e, errMsg2, module);
                }
            }

            return ServiceUtil.returnError(errMsg);
        }
        return result;
    }
}
TOP

Related Classes of org.ofbiz.oagis.OagisShipmentServices

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.