svn commit: r529422 - in /ofbiz/trunk/applications/product: config/ entitydef/ servicedef/ src/org/ofbiz/shipment/shipment/ src/org/ofbiz/shipment/thirdparty/ups/ webapp/facility/WEB-INF/actions/shipment/ webapp/facility/shipment/

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

svn commit: r529422 - in /ofbiz/trunk/applications/product: config/ entitydef/ servicedef/ src/org/ofbiz/shipment/shipment/ src/org/ofbiz/shipment/thirdparty/ups/ webapp/facility/WEB-INF/actions/shipment/ webapp/facility/shipment/

sichen
Author: sichen
Date: Mon Apr 16 15:39:18 2007
New Revision: 529422

URL: http://svn.apache.org/viewvc?view=rev&rev=529422
Log:
UPS Support for: Insured package value; Third-party shipper payment; COD

- Insured package value controlled by a new ShipmentPackage.insuredValue field and UI in EditShipmentPackages screen
- Third-party shipper payment controlled by new ShipmentRouteSegment.thirdPartyAccountNumber and ShipmentRouteSegment.thirdPartyContactMechId fields, and UI in EditShipmentRouteSegments screen
- COD controlled by properties in shipment.properties and this assumption: If all items in a shipment package come from orders which were paid ONLY with EXT_COD, then the package is a COD package
- COD surcharge amount defined in shipment.properties and split (or not) between packages, and converted to the shipment currency if necessary
- Package value for COD is calculated using the getShipmentPackageValueFromOrders service, which calculates the value of each shipment package item by prorating the value of the order item (as determined by the getOrderItemValue service) via packed quantity vs. ordered quantity, for each item of the shipment - converting to the shipment currency if necessary
- Providing a 'Residential Delivery' checkbox in the EditShipmentRouteSegments screen for UPS shipments so that UPS Voice Notification service will be requested automatically if checked (and a destination telecom contactMechId is provided)

Modified:
    ofbiz/trunk/applications/product/config/ProductUiLabels.properties
    ofbiz/trunk/applications/product/config/shipment.properties
    ofbiz/trunk/applications/product/entitydef/entitygroup.xml
    ofbiz/trunk/applications/product/entitydef/entitymodel_shipment.xml
    ofbiz/trunk/applications/product/servicedef/services_shipment.xml
    ofbiz/trunk/applications/product/src/org/ofbiz/shipment/shipment/ShipmentServices.java
    ofbiz/trunk/applications/product/src/org/ofbiz/shipment/thirdparty/ups/UpsServices.java
    ofbiz/trunk/applications/product/webapp/facility/WEB-INF/actions/shipment/EditShipmentRouteSegments.bsh
    ofbiz/trunk/applications/product/webapp/facility/shipment/EditShipmentPackages.ftl
    ofbiz/trunk/applications/product/webapp/facility/shipment/EditShipmentRouteSegments.ftl

Modified: ofbiz/trunk/applications/product/config/ProductUiLabels.properties
URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/product/config/ProductUiLabels.properties?view=diff&rev=529422&r1=529421&r2=529422
==============================================================================
--- ofbiz/trunk/applications/product/config/ProductUiLabels.properties (original)
+++ ofbiz/trunk/applications/product/config/ProductUiLabels.properties Mon Apr 16 15:39:18 2007
@@ -1203,6 +1203,7 @@
 ProductShipmentFedexHomeEvening=Evening
 ProductShipmentFedexHomeAppointment=Appointment
 ProductShipmentId=Shipment Id
+ProductShipmentInsuredValuePackage=Insured Value
 ProductShipmentItemSeqId=Shipment Item Seq Id
 ProductShipmentManifest=Manifest for Shipment
 ProductShipmentMethod=Shipment Method
@@ -1210,15 +1211,19 @@
 ProductShipmentMethodTypes=Shipment Method Types
 ProductShipmentNone=None
 ProductShipmentNotFoundId=The Shipment was not found with ID
+ProductShipmentPackageNotFound=shipmentPackageSeqId [${shipmentPackageSeqId}] was not found in shipment with ID [${shipmentId}]
 ProductShipmentPlan=Shipment Plan
 ProductShipmentPlanToOrderItems=Shipment Plan --> Order Items
 ProductShipmentQuickComplete=Quick Complete Drop Shipment
+ProductShipmentThirdPartyAccountNumber=Third Party Account Number
+ProductShipmentThirdPartyAddress=Third Party Account Address ID
 ProductShipmentTotalWeight=Total Weight
 ProductShipmentTotalVolume=Total Volume
 ProductShipmentType=Shipment Type
 ProductShipmentTypeId=Shipment type Id
 ProductShipmentUomAbbreviation_WT_lb=lbs
 ProductShipmentUomAbbreviation_WT_kg=kg
+ProductShipmentUpsResidential=Residential Delivery
 ProductShipments=Shipments
 ProductShipmentsFound=Shipments Found
 ProductShipping=Shipping

Modified: ofbiz/trunk/applications/product/config/shipment.properties
URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/product/config/shipment.properties?view=diff&rev=529422&r1=529421&r2=529422
==============================================================================
--- ofbiz/trunk/applications/product/config/shipment.properties (original)
+++ ofbiz/trunk/applications/product/config/shipment.properties Mon Apr 16 15:39:18 2007
@@ -72,6 +72,28 @@
 # minimum be equal to the value below.
 shipment.ups.min.estimate.weight=.1
 
+#ÊCOD Surcharge
+#ÊWill be applied if shipment.ups.cod.applyCODSurcharge is true and all shipment package items
+#Êare from orders which have been fully paid via EXT_COD
+shipment.ups.cod.applyCODSurcharge=true
+shipment.ups.cod.surcharge.amount=9
+shipment.ups.cod.surcharge.currencyUomId=USD
+
+#Êshipment.ups.cod.surcharge.applyToPackages:
+#Êall - surcharge amount will be applied to each shipment package
+#Êfirst - surcharge amount will be applied to the first package in the shipment
+#Êsplit - surcharge amount will be split between shipment packages (fractional cents are rounded
+#Ê        via symmetric arithmetic rounding)
+shipment.ups.cod.surcharge.applyToPackages=first
+
+#ÊCODFundsCode
+#ÊThe code that indicates the type of funds used for the COD payment.
+#ÊFor package-level COD:
+#Ê0 = Unsecured funds allowed (check, cashier's check or money order - no cash allowed)
+#Ê8 = Secured Funds only (cashier's check or money order - no cash allowed)
+#ÊShipment-level COD: Not supported
+shipment.ups.cod.codFundsCode=0
+
 ############################################
 # USPS Webtools API Configuration
 ############################################

Modified: ofbiz/trunk/applications/product/entitydef/entitygroup.xml
URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/product/entitydef/entitygroup.xml?view=diff&rev=529422&r1=529421&r2=529422
==============================================================================
--- ofbiz/trunk/applications/product/entitydef/entitygroup.xml (original)
+++ ofbiz/trunk/applications/product/entitydef/entitygroup.xml Mon Apr 16 15:39:18 2007
@@ -324,5 +324,6 @@
     <entity-group group="org.ofbiz" entity="ShipmentType" />
     <entity-group group="org.ofbiz" entity="ShipmentTypeAttr" />
     <entity-group group="org.ofbiz" entity="ShippingDocument" />
+    <entity-group group="org.ofbiz" entity="PackedQtyVsOrderItemQuantity" />
 </entitygroup>
 

Modified: ofbiz/trunk/applications/product/entitydef/entitymodel_shipment.xml
URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/product/entitydef/entitymodel_shipment.xml?view=diff&rev=529422&r1=529421&r2=529422
==============================================================================
--- ofbiz/trunk/applications/product/entitydef/entitymodel_shipment.xml (original)
+++ ofbiz/trunk/applications/product/entitydef/entitymodel_shipment.xml Mon Apr 16 15:39:18 2007
@@ -997,6 +997,7 @@
       <field name="dateCreated" type="date-time"></field>
       <field name="weight" type="floating-point"></field>
       <field name="weightUomId" type="id"></field>
+      <field name="insuredValue" type="currency-amount"></field>
       <prim-key field="shipmentId"/>
       <prim-key field="shipmentPackageSeqId"/>
       <relation type="one" fk-name="SHPKG_SHPMNT" rel-entity-name="Shipment">
@@ -1110,6 +1111,8 @@
       <field name="lastUpdatedDate" type="date-time"></field>
       <field name="homeDeliveryType" type="id"></field>
       <field name="homeDeliveryDate" type="date-time"></field>
+      <field name="thirdPartyAccountNumber" type="id"></field>
+      <field name="thirdPartyContactMechId" type="id"></field>
       <prim-key field="shipmentId"/>
       <prim-key field="shipmentRouteSegmentId"/>
       <relation type="one" fk-name="SHPMT_RTSEG_SHPMT" rel-entity-name="Shipment">
@@ -1163,6 +1166,9 @@
       <relation type="one" fk-name="SHPKRTSG_BWUOM" title="BillingWeight" rel-entity-name="Uom">
         <key-map field-name="billingWeightUomId" rel-field-name="uomId"/>
       </relation>
+      <relation type="one" fk-name="SHPMT_RTSEG_TPPAD" title="ThirdParty" rel-entity-name="PostalAddress">
+        <key-map field-name="thirdPartyContactMechId" rel-field-name="contactMechId"/>
+      </relation>
     </entity>
     <view-entity entity-name="ShipmentPackageRouteDetail"
             package-name="org.ofbiz.shipment.shipment"
@@ -1274,5 +1280,32 @@
         <key-map field-name="shipmentPackageSeqId"/>
       </relation>
     </entity>
+
+    <view-entity entity-name="PackedQtyVsOrderItemQuantity"
+            package-name="org.ofbiz.shipment.shipment"
+            title="Shipment Route Segment Detail View Entity">
+      <description>View to report ShipmentPackageContent quantity vs. OrderItem quantity via
+        ItemIssuance</description>
+      <member-entity entity-alias="SPC" entity-name="ShipmentPackageContent"/>
+      <member-entity entity-alias="II" entity-name="ItemIssuance"/>
+      <member-entity entity-alias="OI" entity-name="OrderItem"/>
+      <alias entity-alias="SPC" name="shipmentId"/>
+      <alias entity-alias="SPC" name="shipmentPackageSeqId"/>
+      <alias entity-alias="SPC" name="packedQuantity" field="quantity"/>
+      <alias entity-alias="OI" name="orderId"/>
+      <alias entity-alias="OI" name="orderItemSeqId"/>
+      <alias entity-alias="OI" name="orderedQuantity" field="quantity"/>
+      <view-link entity-alias="SPC" rel-entity-alias="II">
+        <key-map field-name="shipmentId"/>
+        <key-map field-name="shipmentItemSeqId"/>
+      </view-link>
+      <view-link entity-alias="II" rel-entity-alias="OI">
+        <key-map field-name="orderId"/>
+        <key-map field-name="orderItemSeqId"/>
+      </view-link>
+      <relation type="one" rel-entity-name="OrderHeader">
+          <key-map field-name="orderId"/>
+      </relation>
+    </view-entity>
 </entitymodel>
 

Modified: ofbiz/trunk/applications/product/servicedef/services_shipment.xml
URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/product/servicedef/services_shipment.xml?view=diff&rev=529422&r1=529421&r2=529422
==============================================================================
--- ofbiz/trunk/applications/product/servicedef/services_shipment.xml (original)
+++ ofbiz/trunk/applications/product/servicedef/services_shipment.xml Mon Apr 16 15:39:18 2007
@@ -645,4 +645,16 @@
         <description>Delete a QuantityBreak</description>
         <auto-attributes entity-name="QuantityBreak" include="pk" mode="IN" optional="false"/>
     </service>
+
+    <service name="getShipmentPackageValueFromOrders" engine="java"
+            location="org.ofbiz.shipment.shipment.ShipmentServices" invoke="getShipmentPackageValueFromOrders" auth="true">
+        <description>Calculates the total value of a shipment package by totalling the results of the getOrderItemValue service
+            for the orderItem related to each ShipmentPackageContent, prorated by the quantity of the orderItem packed in the
+            ShipmentPackageContent. Value is converted according to the incoming currencyUomId.</description>
+        <attribute name="shipmentId" type="String" mode="IN" optional="false"/>
+        <attribute name="shipmentPackageSeqId" type="String" mode="IN" optional="false"/>
+        <attribute name="currencyUomId" type="String" mode="IN" optional="false"/>
+        <attribute name="packageValue" type="BigDecimal" mode="OUT" optional="true"/>
+    </service>
+
 </services>

Modified: ofbiz/trunk/applications/product/src/org/ofbiz/shipment/shipment/ShipmentServices.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/product/src/org/ofbiz/shipment/shipment/ShipmentServices.java?view=diff&rev=529422&r1=529421&r2=529422
==============================================================================
--- ofbiz/trunk/applications/product/src/org/ofbiz/shipment/shipment/ShipmentServices.java (original)
+++ ofbiz/trunk/applications/product/src/org/ofbiz/shipment/shipment/ShipmentServices.java Mon Apr 16 15:39:18 2007
@@ -19,13 +19,11 @@
 package org.ofbiz.shipment.shipment;
 
 import java.util.*;
+import java.math.BigDecimal;
 
 import javolution.util.FastMap;
 
-import org.ofbiz.base.util.Debug;
-import org.ofbiz.base.util.StringUtil;
-import org.ofbiz.base.util.UtilMisc;
-import org.ofbiz.base.util.UtilValidate;
+import org.ofbiz.base.util.*;
 import org.ofbiz.common.geo.GeoWorker;
 import org.ofbiz.entity.GenericDelegator;
 import org.ofbiz.entity.GenericEntityException;
@@ -45,6 +43,11 @@
 
     public static final String module = ShipmentServices.class.getName();
 
+    public static final String resource = "ProductUiLabels";
+    public static final int decimals = UtilNumber.getBigDecimalScale("order.decimals");
+    public static final int rounding = UtilNumber.getBigDecimalRoundingMode("order.rounding");
+    public static final BigDecimal ZERO = (new BigDecimal("0")).setScale(decimals, rounding);    
+
     public static Map createShipmentEstimate(DispatchContext dctx, Map context) {
         Map result = new HashMap();
         GenericDelegator delegator = dctx.getDelegator();
@@ -943,5 +946,87 @@
 
         // don't return an error
         return ServiceUtil.returnSuccess();
+    }
+
+    /**
+     * Calculates the total value of a shipment package by totalling the results of the getOrderItemValue service
+     *  for the orderItem related to each ShipmentPackageContent, prorated by the quantity of the orderItem packed in the
+     *  ShipmentPackageContent. Value is converted according to the incoming currencyUomId.
+     * @param dctx DispatchContext
+     * @param context Map
+     * @return Map
+     */
+    public static Map getShipmentPackageValueFromOrders(DispatchContext dctx, Map context) {
+        LocalDispatcher dispatcher = dctx.getDispatcher();
+        GenericDelegator delegator = dctx.getDelegator();
+        GenericValue userLogin = (GenericValue) context.get("userLogin");
+        Locale locale = (Locale) context.get("locale");
+        
+        String shipmentId = (String) context.get("shipmentId");
+        String shipmentPackageSeqId = (String) context.get("shipmentPackageSeqId");
+        String currencyUomId = (String) context.get("currencyUomId");
+
+        BigDecimal packageTotalValue = ZERO;
+
+        GenericValue shipment = null;
+        GenericValue shipmentPackage = null;
+        try {
+
+            shipment = delegator.findByPrimaryKey("Shipment", UtilMisc.toMap("shipmentId", shipmentId));
+            if (UtilValidate.isEmpty(shipment)) {
+                String errorMessage = UtilProperties.getMessage(resource, "ProductShipmentNotFoundId", locale);
+                Debug.logError(errorMessage, module);
+                return ServiceUtil.returnError(errorMessage);
+            }
+            
+            shipmentPackage = delegator.findByPrimaryKey("ShipmentPackage", UtilMisc.toMap("shipmentId", shipmentId, "shipmentPackageSeqId", shipmentPackageSeqId));
+            if (UtilValidate.isEmpty(shipmentPackage)) {
+                String errorMessage = UtilProperties.getMessage(resource, "ProductShipmentPackageNotFound", context, locale);
+                Debug.logError(errorMessage, module);
+                return ServiceUtil.returnError(errorMessage);
+            }
+            
+            List packageContents = delegator.findByAnd("PackedQtyVsOrderItemQuantity", UtilMisc.toMap("shipmentId", shipmentId, "shipmentPackageSeqId", shipmentPackageSeqId));
+            Iterator packageContentsIt = packageContents.iterator();
+            while (packageContentsIt.hasNext()) {
+                GenericValue packageContent = (GenericValue) packageContentsIt.next();
+                String orderId = packageContent.getString("orderId");
+                String orderItemSeqId = packageContent.getString("orderItemSeqId");
+            
+                // Get the value of the orderItem by calling the getOrderItemValue service
+                Map getOrderItemValueResult = dispatcher.runSync("getOrderItemValue", UtilMisc.toMap("orderId", orderId, "orderItemSeqId", orderItemSeqId, "userLogin", userLogin, "locale", locale));
+                if (ServiceUtil.isError(getOrderItemValueResult)) return getOrderItemValueResult;
+                BigDecimal orderItemTotalValue = (BigDecimal) getOrderItemValueResult.get("orderItemValue");
+            
+                // How much of the orderItem does the packed item represent?
+                BigDecimal packedQuantity = packageContent.getBigDecimal("packedQuantity");
+                BigDecimal orderedQuantity = packageContent.getBigDecimal("orderedQuantity");
+                BigDecimal proportionOfOrderedQuantity = packedQuantity.divide(orderedQuantity, 10, rounding);
+            
+                // Prorate the orderItem's value by that proportion
+                BigDecimal packageContentValue = proportionOfOrderedQuantity.multiply(orderItemTotalValue).setScale(decimals, rounding);
+            
+                // Convert the value to the shipment currency, if necessary
+                GenericValue orderHeader = packageContent.getRelatedOne("OrderHeader");
+                Map convertUomResult = dispatcher.runSync("convertUom", UtilMisc.toMap("uomId", orderHeader.getString("currencyUom"), "uomIdTo", currencyUomId, "originalValue", new Double(packageContentValue.doubleValue())));
+                if (ServiceUtil.isError(convertUomResult)) return convertUomResult;
+                if (convertUomResult.containsKey("convertedValue")) {
+                    packageContentValue = new BigDecimal(((Double) convertUomResult.get("convertedValue")).doubleValue()).setScale(decimals, rounding);
+                }
+                
+                // Add the value of the packed item to the package's total value
+                packageTotalValue = packageTotalValue.add(packageContentValue);
+            }
+
+        } catch (GenericEntityException e) {
+            Debug.logError(e, module);
+            return ServiceUtil.returnError(e.getMessage());
+        } catch (GenericServiceException e) {
+            Debug.logError(e, module);
+            return ServiceUtil.returnError(e.getMessage());
+        }
+        Map result = ServiceUtil.returnSuccess();
+        result.put("packageValue", packageTotalValue);
+        return result;
     }
 }

Modified: ofbiz/trunk/applications/product/src/org/ofbiz/shipment/thirdparty/ups/UpsServices.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/product/src/org/ofbiz/shipment/thirdparty/ups/UpsServices.java?view=diff&rev=529422&r1=529421&r2=529422
==============================================================================
--- ofbiz/trunk/applications/product/src/org/ofbiz/shipment/thirdparty/ups/UpsServices.java (original)
+++ ofbiz/trunk/applications/product/src/org/ofbiz/shipment/thirdparty/ups/UpsServices.java Mon Apr 16 15:39:18 2007
@@ -21,35 +21,20 @@
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeSet;
+import java.util.*;
+import java.math.BigDecimal;
 
 import javax.xml.parsers.ParserConfigurationException;
 
 import org.apache.velocity.test.MiscTestCase;
-import org.ofbiz.base.util.Base64;
-import org.ofbiz.base.util.Debug;
-import org.ofbiz.base.util.GeneralException;
-import org.ofbiz.base.util.HttpClient;
-import org.ofbiz.base.util.HttpClientException;
-import org.ofbiz.base.util.StringUtil;
-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.*;
 import org.ofbiz.entity.GenericDelegator;
 import org.ofbiz.entity.GenericEntityException;
 import org.ofbiz.entity.GenericValue;
+import org.ofbiz.entity.condition.EntityExpr;
+import org.ofbiz.entity.condition.EntityOperator;
 import org.ofbiz.entity.util.EntityUtil;
-import org.ofbiz.service.DispatchContext;
-import org.ofbiz.service.GenericServiceException;
-import org.ofbiz.service.ServiceDispatcher;
-import org.ofbiz.service.ServiceUtil;
+import org.ofbiz.service.*;
 import org.ofbiz.product.store.ProductStoreWorker;
 
 import org.w3c.dom.Document;
@@ -75,11 +60,15 @@
             unitsOfbizToUps.put(entry.getValue(), entry.getKey());
         }
     }
-
+    public static final int decimals = UtilNumber.getBigDecimalScale("order.decimals");
+    public static final int rounding = UtilNumber.getBigDecimalRoundingMode("order.rounding");
 
     public static Map upsShipmentConfirm(DispatchContext dctx, Map context) {
         Map result = new HashMap();
         GenericDelegator delegator = dctx.getDelegator();
+        LocalDispatcher dispatcher = dctx.getDispatcher();
+        GenericValue userLogin = (GenericValue) context.get("userLogin");
+        Locale locale = (Locale) context.get("locale");
         String shipmentId = (String) context.get("shipmentId");
         String shipmentRouteSegmentId = (String) context.get("shipmentRouteSegmentId");
 
@@ -200,6 +189,67 @@
                 ordersDescription = "Order " + (String) orderIdSet.iterator().next();
             }
             
+            // COD Support
+            boolean applyCODSurcharge = "true".equalsIgnoreCase(UtilProperties.getPropertyValue("shipment", "shipment.ups.cod.applyCODSurcharge"));
+
+            // COD only applies if all orders involved with the shipment were paid only with EXT_COD - anything else becomes too complicated
+            if (applyCODSurcharge) {
+
+                // Get the paymentMethodTypeIds of all the orderPaymentPreferences involved with the shipment
+                List opps = delegator.findByCondition("OrderPaymentPreference", new EntityExpr("orderId", EntityOperator.IN, orderIdSet), null, null);
+                List paymentMethodTypeIds = EntityUtil.getFieldListFromEntityList(opps, "paymentMethodTypeId", true);
+                
+                if (paymentMethodTypeIds.size() > 1 || ! paymentMethodTypeIds.contains("EXT_COD")) {
+                    applyCODSurcharge = false;
+                }
+            }
+
+            String codSurchargeAmount = null;
+            String codSurchargeCurrencyUomId = null;
+            String codFundsCode = null;
+            
+            boolean codSurchargeApplyToFirstPackage = false;
+            boolean codSurchargeApplyToAllPackages = false;
+            boolean codSurchargeSplitBetweenPackages = false;
+
+            BigDecimal codSurchargePackageAmount = null;
+            
+            if (applyCODSurcharge) {
+
+                codSurchargeAmount = UtilProperties.getPropertyValue("shipment", "shipment.ups.cod.surcharge.amount");
+                if (UtilValidate.isEmpty(codSurchargeAmount)) {
+                    return ServiceUtil.returnError("shipment.ups.cod.surcharge.amount is not configured in shipment.properties");
+                }
+                codSurchargeCurrencyUomId = UtilProperties.getPropertyValue("shipment", "shipment.ups.cod.surcharge.currencyUomId");
+                if (UtilValidate.isEmpty(codSurchargeCurrencyUomId)) {
+                    return ServiceUtil.returnError("shipment.ups.cod.surcharge.currencyUomId is not configured in shipment.properties");
+                }
+                String codSurchargeApplyToPackages = UtilProperties.getPropertyValue("shipment", "shipment.ups.cod.surcharge.applyToPackages");
+                if (UtilValidate.isEmpty(codSurchargeApplyToPackages)) {
+                    return ServiceUtil.returnError("shipment.ups.cod.surcharge.applyToPackages is not configured in shipment.properties");
+                }
+                codFundsCode = UtilProperties.getPropertyValue("shipment", "shipment.ups.cod.codFundsCode");
+                if (UtilValidate.isEmpty(codFundsCode)) {
+                    return ServiceUtil.returnError("shipment.ups.cod.codFundsCode is not configured in shipment.properties");
+                }
+
+                codSurchargeApplyToFirstPackage = "first".equalsIgnoreCase(codSurchargeApplyToPackages);
+                codSurchargeApplyToAllPackages = "all".equalsIgnoreCase(codSurchargeApplyToPackages);
+                codSurchargeSplitBetweenPackages = "split".equalsIgnoreCase(codSurchargeApplyToPackages);
+                
+                codSurchargePackageAmount = new BigDecimal(codSurchargeAmount).setScale(decimals, rounding);
+                if (codSurchargeSplitBetweenPackages) {
+                    codSurchargePackageAmount = codSurchargePackageAmount.divide(new BigDecimal(shipmentPackageRouteSegs.size()), decimals, rounding);
+                }
+
+                if (UtilValidate.isEmpty(destTelecomNumber)) {
+                    Debug.logInfo("Voice notification service will not be requested for COD shipmentId " + shipmentId + ", shipmentRouteSegmentId " + shipmentRouteSegmentId + " - missing destination phone number", module);
+                }
+                if (UtilValidate.isEmpty(shipmentRouteSegment.get("homeDeliveryType"))) {
+                    Debug.logInfo("Voice notification service will not be requested for COD shipmentId " + shipmentId + ", shipmentRouteSegmentId " + shipmentRouteSegmentId + " - destination address is not residential", module);
+                }
+            }
+
             // Okay, start putting the XML together...
             Document shipmentConfirmRequestDoc = UtilXml.makeEmptyXmlDocument("ShipmentConfirmRequest");
             Element shipmentConfirmRequestElement = shipmentConfirmRequestDoc.getDocumentElement();
@@ -267,6 +317,9 @@
             UtilXml.addChildElementValue(shipToAddressElement, "StateProvinceCode", destPostalAddress.getString("stateProvinceGeoId"), shipmentConfirmRequestDoc);
             UtilXml.addChildElementValue(shipToAddressElement, "PostalCode", destPostalAddress.getString("postalCode"), shipmentConfirmRequestDoc);
             UtilXml.addChildElementValue(shipToAddressElement, "CountryCode", destCountryGeo.getString("geoCode"), shipmentConfirmRequestDoc);
+            if (UtilValidate.isNotEmpty(shipmentRouteSegment.getString("homeDeliveryType"))) {
+                UtilXml.addChildElement(shipToAddressElement, "ResidentialAddress", shipmentConfirmRequestDoc);
+            }
 
             // Child of Shipment: ShipFrom
             Element shipFromElement = UtilXml.addChildElement(shipmentElement, "ShipFrom", shipmentConfirmRequestDoc);
@@ -286,17 +339,46 @@
 
             // Child of Shipment: PaymentInformation
             Element paymentInformationElement = UtilXml.addChildElement(shipmentElement, "PaymentInformation", shipmentConfirmRequestDoc);
-            Element prepaidElement = UtilXml.addChildElement(paymentInformationElement, "Prepaid", shipmentConfirmRequestDoc);
-            Element billShipperElement = UtilXml.addChildElement(prepaidElement, "BillShipper", shipmentConfirmRequestDoc);
-            // fill in BillShipper AccountNumber element from properties file
-            UtilXml.addChildElementValue(billShipperElement, "AccountNumber", UtilProperties.getPropertyValue("shipment", "shipment.ups.bill.shipper.account.number"), shipmentConfirmRequestDoc);
+            
+            String thirdPartyAccountNumber = shipmentRouteSegment.getString("thirdPartyAccountNumber");
+
+            if (UtilValidate.isEmpty(thirdPartyAccountNumber)) {
+                
+                // Paid by shipper
+                Element prepaidElement = UtilXml.addChildElement(paymentInformationElement, "Prepaid", shipmentConfirmRequestDoc);
+                Element billShipperElement = UtilXml.addChildElement(prepaidElement, "BillShipper", shipmentConfirmRequestDoc);
+
+                // fill in BillShipper AccountNumber element from properties file
+                UtilXml.addChildElementValue(billShipperElement, "AccountNumber", UtilProperties.getPropertyValue("shipment", "shipment.ups.bill.shipper.account.number"), shipmentConfirmRequestDoc);
+            } else {
+
+                // Paid by another shipper (may be receiver or not)
+                GenericValue thirdPartyPostalAddress = shipmentRouteSegment.getRelatedOne("ThirdPartyPostalAddress");
+    
+                // UPS requires the postal code and country code of the third party
+                if (UtilValidate.isEmpty(thirdPartyPostalAddress)) {
+                    return ServiceUtil.returnError("ThirdPartyPostalAddress not found for ShipmentRouteSegment with shipmentId " + shipmentId + " and shipmentRouteSegmentId " + shipmentRouteSegmentId);
+                }
+                GenericValue thirdPartyCountryGeo = thirdPartyPostalAddress.getRelatedOne("CountryGeo");
+                if (UtilValidate.isEmpty(thirdPartyCountryGeo)) {
+                    return ServiceUtil.returnError("ThirdPartyCountryGeo not found for ShipmentRouteSegment with shipmentId " + shipmentId + " and shipmentRouteSegmentId " + shipmentRouteSegmentId);
+                }
+
+                Element billThirdPartyElement = UtilXml.addChildElement(paymentInformationElement, "BillThirdParty", shipmentConfirmRequestDoc);
+                Element billThirdPartyShipperElement = UtilXml.addChildElement(billThirdPartyElement, "BillThirdPartyShipper", shipmentConfirmRequestDoc);
+                UtilXml.addChildElementValue(billThirdPartyShipperElement, "AccountNumber", thirdPartyAccountNumber, shipmentConfirmRequestDoc);
+                Element thirdPartyElement = UtilXml.addChildElement(billThirdPartyShipperElement, "ThirdParty", shipmentConfirmRequestDoc);
+                Element addressElement = UtilXml.addChildElement(thirdPartyElement, "Address", shipmentConfirmRequestDoc);
+                UtilXml.addChildElementValue(addressElement, "PostalCode", thirdPartyPostalAddress.getString("postalCode"), shipmentConfirmRequestDoc);
+                UtilXml.addChildElementValue(addressElement, "CountryCode", thirdPartyCountryGeo.getString("geoCode"), shipmentConfirmRequestDoc);
+            }
 
             // Child of Shipment: Service
             Element serviceElement = UtilXml.addChildElement(shipmentElement, "Service", shipmentConfirmRequestDoc);
             UtilXml.addChildElementValue(serviceElement, "Code", carrierShipmentMethod.getString("carrierServiceCode"), shipmentConfirmRequestDoc);
 
             // Child of Shipment: Package
-            Iterator shipmentPackageRouteSegIter = shipmentPackageRouteSegs.iterator();
+            ListIterator shipmentPackageRouteSegIter = shipmentPackageRouteSegs.listIterator();
             while (shipmentPackageRouteSegIter.hasNext()) {
                 GenericValue shipmentPackageRouteSeg = (GenericValue) shipmentPackageRouteSegIter.next();
                 GenericValue shipmentPackage = shipmentPackageRouteSeg.getRelatedOne("ShipmentPackage");
@@ -352,6 +434,54 @@
                 if (carrierShipmentBoxType != null && carrierShipmentBoxType.get("oversizeCode") != null) {
                     UtilXml.addChildElementValue(packageElement, "OversizePackage", carrierShipmentBoxType.getString("oversizeCode"), shipmentConfirmRequestDoc);
                 }
+                
+                Element packageServiceOptionsElement = UtilXml.addChildElement(packageElement, "PackageServiceOptions", shipmentConfirmRequestDoc);
+
+                // Determine the currency by trying the shipmentRouteSegment, then the Shipment, then the framework's default currency, and finally default to USD
+                String currencyCode = null;
+                if (UtilValidate.isNotEmpty(shipmentRouteSegment.getString("currencyUomId"))) {
+                    currencyCode = shipmentRouteSegment.getString("currencyUomId");
+                } else if (UtilValidate.isNotEmpty(shipmentRouteSegment.getString("currencyUomId"))) {
+                    currencyCode = shipment.getString("currencyUomId");
+                } else {
+                    currencyCode = UtilProperties.getPropertyValue("general.properties", "currency.uom.id.default", "USD");
+                }
+
+                // Package insured value
+                BigDecimal insuredValue = shipmentPackage.getBigDecimal("insuredValue");
+                if (! UtilValidate.isEmpty(insuredValue)) {
+
+                    Element insuredValueElement = UtilXml.addChildElement(packageServiceOptionsElement, "InsuredValue", shipmentConfirmRequestDoc);
+                    UtilXml.addChildElementValue(insuredValueElement, "MonetaryValue", insuredValue.setScale(2, BigDecimal.ROUND_HALF_UP).toString(), shipmentConfirmRequestDoc);
+                    UtilXml.addChildElementValue(insuredValueElement, "CurrencyCode", currencyCode, shipmentConfirmRequestDoc);
+                }
+
+                if (applyCODSurcharge) {
+                    Element codElement = UtilXml.addChildElement(packageServiceOptionsElement, "COD", shipmentConfirmRequestDoc);
+                    UtilXml.addChildElementValue(codElement, "CODCode", "3", shipmentConfirmRequestDoc); // "3" is the only valid value for package-level COD
+                    UtilXml.addChildElementValue(codElement, "CODFundsCode", codFundsCode, shipmentConfirmRequestDoc);
+                    Element codAmountElement = UtilXml.addChildElement(codElement, "CODAmount", shipmentConfirmRequestDoc);
+                    UtilXml.addChildElementValue(codAmountElement, "CurrencyCode", currencyCode, shipmentConfirmRequestDoc);
+
+                    // Get the value of the package by going back to the orderItems
+                    Map getPackageValueResult = dispatcher.runSync("getShipmentPackageValueFromOrders", UtilMisc.toMap("shipmentId", shipmentId, "shipmentPackageSeqId", shipmentPackage.get("shipmentPackageSeqId"), "currencyUomId", currencyCode, "userLogin", userLogin, "locale", locale));
+                    if (ServiceUtil.isError(getPackageValueResult)) return getPackageValueResult;
+                    BigDecimal packageValue = (BigDecimal) getPackageValueResult.get("packageValue");
+                    
+                    // Convert the value of the COD surcharge to the shipment currency, if necessary
+                    Map convertUomResult = dispatcher.runSync("convertUom", UtilMisc.toMap("uomId", codSurchargeCurrencyUomId, "uomIdTo", currencyCode, "originalValue", new Double(codSurchargePackageAmount.doubleValue())));
+                    if (ServiceUtil.isError(convertUomResult)) return convertUomResult;
+                    if (convertUomResult.containsKey("convertedValue")) {
+                        codSurchargePackageAmount = new BigDecimal(((Double) convertUomResult.get("convertedValue")).doubleValue()).setScale(decimals, rounding);
+                    }
+                    
+                    // Add the amount of the surcharge for the package, if the surcharge should be on all packages or the first and this is the first package
+                    if (codSurchargeApplyToAllPackages || codSurchargeSplitBetweenPackages || (codSurchargeApplyToFirstPackage && shipmentPackageRouteSegIter.previousIndex() <= 0)) {
+                        packageValue = packageValue.add(codSurchargePackageAmount);
+                    }
+
+                    UtilXml.addChildElementValue(codAmountElement, "MonetaryValue", packageValue.setScale(decimals, rounding).toString(), shipmentConfirmRequestDoc);
+                }
             }
 
             String shipmentConfirmRequestString = null;
@@ -432,6 +562,9 @@
             }
 
             return handleUpsShipmentConfirmResponse(shipmentConfirmResponseDocument, shipmentRouteSegment);
+        } catch (GenericServiceException e) {
+            Debug.logError(e, module);
+            return ServiceUtil.returnError("Error reading or writing Shipment data for UPS Shipment Confirm: " + e.toString());
         } catch (GenericEntityException e) {
             Debug.logError(e, module);
             if (shipmentConfirmResponseString != null) {

Modified: ofbiz/trunk/applications/product/webapp/facility/WEB-INF/actions/shipment/EditShipmentRouteSegments.bsh
URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/product/webapp/facility/WEB-INF/actions/shipment/EditShipmentRouteSegments.bsh?view=diff&rev=529422&r1=529421&r2=529422
==============================================================================
--- ofbiz/trunk/applications/product/webapp/facility/WEB-INF/actions/shipment/EditShipmentRouteSegments.bsh (original)
+++ ofbiz/trunk/applications/product/webapp/facility/WEB-INF/actions/shipment/EditShipmentRouteSegments.bsh Mon Apr 16 15:39:18 2007
@@ -60,6 +60,7 @@
             } else {
                 shipmentRouteSegmentData.put("carrierServiceStatusValidChangeToDetails", delegator.findByAnd("StatusValidChangeToDetail", UtilMisc.toMap("statusId", "SHRSCS_NOT_STARTED"), UtilMisc.toList("sequenceId")));
             }
+            shipmentRouteSegmentData.put("thirdPartyPostalAddress", shipmentRouteSegment.getRelatedOne("ThirdPartyPostalAddress"));
             shipmentRouteSegmentDatas.add(shipmentRouteSegmentData);
         }
     }

Modified: ofbiz/trunk/applications/product/webapp/facility/shipment/EditShipmentPackages.ftl
URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/product/webapp/facility/shipment/EditShipmentPackages.ftl?view=diff&rev=529422&r1=529421&r2=529422
==============================================================================
--- ofbiz/trunk/applications/product/webapp/facility/shipment/EditShipmentPackages.ftl (original)
+++ ofbiz/trunk/applications/product/webapp/facility/shipment/EditShipmentPackages.ftl Mon Apr 16 15:39:18 2007
@@ -58,6 +58,9 @@
                     <option value="${boxType.shipmentBoxTypeId}" <#if shipmentPackage.shipmentBoxTypeId?exists && shipmentPackage.shipmentBoxTypeId == boxType.shipmentBoxTypeId>selected</#if>>${boxType.get("description",locale)}</option>
                 </#list>
             </select>
+            <br />
+            <span class="tabletext">${uiLabelMap.ProductShipmentInsuredValuePackage}:</span>
+            <input type="text" size="5" name="insuredValue" value="${shipmentPackage.insuredValue?if_exists}" class="inputBox"/>
         </td>
         <td><a href="javascript:document.updateShipmentPackageForm${shipmentPackageData_index}.submit();" class="buttontext">${uiLabelMap.CommonUpdate}</a></td>
         <td><div class="tabletext"><a href="<@ofbizUrl>deleteShipmentPackage?shipmentId=${shipmentId}&shipmentPackageSeqId=${shipmentPackage.shipmentPackageSeqId}</@ofbizUrl>" class="buttontext">${uiLabelMap.CommonDelete}</a></div></td>

Modified: ofbiz/trunk/applications/product/webapp/facility/shipment/EditShipmentRouteSegments.ftl
URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/product/webapp/facility/shipment/EditShipmentRouteSegments.ftl?view=diff&rev=529422&r1=529421&r2=529422
==============================================================================
--- ofbiz/trunk/applications/product/webapp/facility/shipment/EditShipmentRouteSegments.ftl (original)
+++ ofbiz/trunk/applications/product/webapp/facility/shipment/EditShipmentRouteSegments.ftl Mon Apr 16 15:39:18 2007
@@ -25,6 +25,8 @@
             <div class="tableheadtext">${uiLabelMap.ProductOriginDestinationFacility}</div>
             <div class="tableheadtext">${uiLabelMap.ProductOriginDestinationAddressId}</div>
             <div class="tableheadtext">${uiLabelMap.ProductOriginDestinationPhoneId}</div>
+            <div class="tableheadtext">${uiLabelMap.ProductShipmentThirdPartyAccountNumber}</div>
+            <div class="tableheadtext">${uiLabelMap.ProductShipmentThirdPartyAddress}</div>
         </td>
         <td>
             <div class="tableheadtext">${uiLabelMap.ProductShipmentFedexHomeDeliveryTypeDate}</div>
@@ -60,6 +62,7 @@
     <#assign currencyUom = shipmentRouteSegmentData.currencyUom?if_exists>
     <#assign billingWeightUom = shipmentRouteSegmentData.billingWeightUom?if_exists>
     <#assign carrierServiceStatusValidChangeToDetails = shipmentRouteSegmentData.carrierServiceStatusValidChangeToDetails?if_exists>
+    <#assign thirdPartyPostalAddress = shipmentRouteSegmentData.thirdPartyPostalAddress?if_exists>
     <form action="<@ofbizUrl>updateShipmentRouteSegment</@ofbizUrl>" name="updateShipmentRouteSegmentForm${shipmentRouteSegmentData_index}">
     <input type="hidden" name="shipmentId" value="${shipmentId}"/>
     <input type="hidden" name="shipmentRouteSegmentId" value="${shipmentRouteSegment.shipmentRouteSegmentId}"/>
@@ -128,11 +131,21 @@
                 <input type="text" size="15" name="destTelecomNumberId" value="${shipmentRouteSegment.destTelecomNumberId?if_exists}" class="inputBox"/>
                 <#if destTelecomNumber?has_content>[${destTelecomNumber.countryCode?if_exists}  ${destTelecomNumber.areaCode?if_exists} ${destTelecomNumber.contactNumber?if_exists}]</#if>
             </div>
+            <div class="tabletext">
+                <input type="text" size="15" name="thirdPartyAccountNumber" value="${shipmentRouteSegment.thirdPartyAccountNumber?if_exists}" class="inputBox"/>
+            </div>
+            <div class="tabletext">
+                <input type="text" size="15" name="thirdPartyContactMechId" value="${shipmentRouteSegment.thirdPartyContactMechId?if_exists}" class="inputBox"/>
+                <#if thirdPartyPostalAddress?has_content>[${uiLabelMap.CommonTo}: ${thirdPartyPostalAddress.toName?if_exists}, ${uiLabelMap.CommonAttn}: ${thirdPartyPostalAddress.attnName?if_exists}, ${thirdPartyPostalAddress.address1?if_exists}, ${thirdPartyPostalAddress.address2?if_exists}, ${thirdPartyPostalAddress.city?if_exists}, ${thirdPartyPostalAddress.stateProvinceGeoId?if_exists}, ${thirdPartyPostalAddress.postalCode?if_exists}, ${thirdPartyPostalAddress.countryGeoId?if_exists}]</#if>
+            </div>
         </td>
         <td>
             <#if "UPS" == shipmentRouteSegment.carrierPartyId?if_exists>
                 <#if !shipmentRouteSegment.carrierServiceStatusId?has_content || "SHRSCS_NOT_STARTED" == shipmentRouteSegment.carrierServiceStatusId?if_exists>
                     <a href="<@ofbizUrl>upsShipmentConfirm?shipmentId=${shipmentRouteSegment.shipmentId}&shipmentRouteSegmentId=${shipmentRouteSegment.shipmentRouteSegmentId}</@ofbizUrl>" class="buttontext">${uiLabelMap.ProductConfirmShipmentUps}</a>
+                    <br/>
+                    ${uiLabelMap.ProductShipmentUpsResidential}:
+                    <input type="checkbox" name="homeDeliveryType" class="checkBox" value="Y" ${(shipmentRouteSegment.homeDeliveryType?has_content)?string("checked=\"checked\"","")}>
                 <#elseif "SHRSCS_CONFIRMED" == shipmentRouteSegment.carrierServiceStatusId?if_exists>
                     <a href="<@ofbizUrl>upsShipmentAccept?shipmentId=${shipmentRouteSegment.shipmentId}&shipmentRouteSegmentId=${shipmentRouteSegment.shipmentRouteSegmentId}</@ofbizUrl>" class="buttontext">${uiLabelMap.ProductAcceptUpsShipmentConfirmation}</a>
                     <br/>