svn commit: r504996 [1/2] - in /ofbiz/trunk/applications: ecommerce/data/ product/ product/config/ product/entitydef/ product/servicedef/ product/src/org/ofbiz/shipment/thirdparty/fedex/ product/templates/ product/templates/shipment/ product/webapp/fac...

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

svn commit: r504996 [1/2] - in /ofbiz/trunk/applications: ecommerce/data/ product/ product/config/ product/entitydef/ product/servicedef/ product/src/org/ofbiz/shipment/thirdparty/fedex/ product/templates/ product/templates/shipment/ product/webapp/fac...

sichen
Author: sichen
Date: Thu Feb  8 10:41:20 2007
New Revision: 504996

URL: http://svn.apache.org/viewvc?view=rev&rev=504996
Log:
Support for FedEx subscription and ship requests via FedEx Ship Manager Direct API

Added:
    ofbiz/trunk/applications/product/servicedef/services_shipment_fedex.xml   (with props)
    ofbiz/trunk/applications/product/src/org/ofbiz/shipment/thirdparty/fedex/
    ofbiz/trunk/applications/product/src/org/ofbiz/shipment/thirdparty/fedex/FedexServices.java   (with props)
    ofbiz/trunk/applications/product/templates/
    ofbiz/trunk/applications/product/templates/shipment/
    ofbiz/trunk/applications/product/templates/shipment/FedexShipRequestTemplate.xml   (with props)
    ofbiz/trunk/applications/product/templates/shipment/FedexSubscriptionRequestTemplate.xml   (with props)
Modified:
    ofbiz/trunk/applications/ecommerce/data/DemoShipping.xml
    ofbiz/trunk/applications/product/config/ProductUiLabels.properties
    ofbiz/trunk/applications/product/config/shipment.properties
    ofbiz/trunk/applications/product/entitydef/entitymodel_shipment.xml
    ofbiz/trunk/applications/product/ofbiz-component.xml
    ofbiz/trunk/applications/product/webapp/facility/WEB-INF/controller.xml
    ofbiz/trunk/applications/product/webapp/facility/shipment/EditShipmentPackages.ftl
    ofbiz/trunk/applications/product/webapp/facility/shipment/EditShipmentRouteSegments.ftl

Modified: ofbiz/trunk/applications/ecommerce/data/DemoShipping.xml
URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/ecommerce/data/DemoShipping.xml?view=diff&rev=504996&r1=504995&r2=504996
==============================================================================
--- ofbiz/trunk/applications/ecommerce/data/DemoShipping.xml (original)
+++ ofbiz/trunk/applications/ecommerce/data/DemoShipping.xml Thu Feb  8 10:41:20 2007
@@ -45,6 +45,8 @@
     <ShipmentMethodType description="No Shipping" shipmentMethodTypeId="NO_SHIPPING"/>
     <ShipmentMethodType description="Second Day" shipmentMethodTypeId="SECOND_DAY"/>
     <ShipmentMethodType description="Next Afternoon" shipmentMethodTypeId="NEXT_PM"/>
+    <ShipmentMethodType description="Next Morning" shipmentMethodTypeId="NEXT_AM"/>
+    <ShipmentMethodType description="Ground Home Delivery" shipmentMethodTypeId="GROUND_HOME"/>
 
     <CarrierShipmentMethod partyId="Company" roleTypeId="CARRIER" shipmentMethodTypeId="LOCAL_DELIVERY" sequenceNumber="4"/>
     <CarrierShipmentMethod partyId="USPS" roleTypeId="CARRIER" shipmentMethodTypeId="STANDARD" sequenceNumber="7" carrierServiceCode="Priority"/>
@@ -58,9 +60,13 @@
     <CarrierShipmentMethod partyId="DHL" roleTypeId="CARRIER" shipmentMethodTypeId="NEXT_PM" sequenceNumber="10" carrierServiceCode="N"/>
     <CarrierShipmentMethod partyId="DHL" roleTypeId="CARRIER" shipmentMethodTypeId="SECOND_DAY" sequenceNumber="11" carrierServiceCode="S"/>
     <CarrierShipmentMethod partyId="DHL" roleTypeId="CARRIER" shipmentMethodTypeId="GROUND" sequenceNumber="12" carrierServiceCode="G"/>
-    <CarrierShipmentMethod partyId="FEDEX" roleTypeId="CARRIER" shipmentMethodTypeId="EXPRESS"/>
-    <CarrierShipmentMethod partyId="FEDEX" roleTypeId="CARRIER" shipmentMethodTypeId="SECOND_DAY"/>
-    <CarrierShipmentMethod partyId="FEDEX" roleTypeId="CARRIER" shipmentMethodTypeId="GROUND"/>
+    <CarrierShipmentMethod partyId="FEDEX" roleTypeId="CARRIER" shipmentMethodTypeId="NEXT_AM" sequenceNumber="13" carrierServiceCode="FIRSTOVERNIGHT"/>
+    <CarrierShipmentMethod partyId="FEDEX" roleTypeId="CARRIER" shipmentMethodTypeId="NEXT_DAY" sequenceNumber="14" carrierServiceCode="PRIORITYOVERNIGHT"/>
+    <CarrierShipmentMethod partyId="FEDEX" roleTypeId="CARRIER" shipmentMethodTypeId="NEXT_PM" sequenceNumber="15" carrierServiceCode="STANDARDOVERNIGHT"/>
+    <CarrierShipmentMethod partyId="FEDEX" roleTypeId="CARRIER" shipmentMethodTypeId="SECOND_DAY" sequenceNumber="16" carrierServiceCode="FEDEX2DAY"/>
+    <CarrierShipmentMethod partyId="FEDEX" roleTypeId="CARRIER" shipmentMethodTypeId="EXPRESS" sequenceNumber="17" carrierServiceCode="FEDEXEXPRESSSAVER"/>
+    <CarrierShipmentMethod partyId="FEDEX" roleTypeId="CARRIER" shipmentMethodTypeId="GROUND" sequenceNumber="18" carrierServiceCode="FEDEXGROUND"/>
+    <CarrierShipmentMethod partyId="FEDEX" roleTypeId="CARRIER" shipmentMethodTypeId="GROUND_HOME" sequenceNumber="19" carrierServiceCode="GROUNDHOMEDELIVERY"/>
     
     <ProductStoreShipmentMeth productStoreId="9000" partyId="_NA_" includeNoChargeItems="Y" allowUspsAddr="N" requireUspsAddr="N" roleTypeId="CARRIER" shipmentMethodTypeId="NO_SHIPPING" sequenceNumber="6"/>
     <ProductStoreShipmentMeth productStoreId="9000" partyId="UPS" includeNoChargeItems="N" allowUspsAddr="N" requireUspsAddr="N" roleTypeId="CARRIER" shipmentMethodTypeId="GROUND" sequenceNumber="3"/>
@@ -72,10 +78,14 @@
     <ProductStoreShipmentMeth productStoreId="9000" partyId="DHL" includeNoChargeItems="N" allowUspsAddr="N" requireUspsAddr="N" roleTypeId="CARRIER" shipmentMethodTypeId="NEXT_PM" sequenceNumber="8"/>
     <ProductStoreShipmentMeth productStoreId="9000" partyId="DHL" includeNoChargeItems="N" allowUspsAddr="N" requireUspsAddr="N" roleTypeId="CARRIER" shipmentMethodTypeId="SECOND_DAY" sequenceNumber="9"/>
     <ProductStoreShipmentMeth productStoreId="9000" partyId="DHL" includeNoChargeItems="N" allowUspsAddr="N" requireUspsAddr="N" roleTypeId="CARRIER" shipmentMethodTypeId="GROUND" sequenceNumber="10"/>
-    <ProductStoreShipmentMeth productStoreId="9000" partyId="FEDEX" includeNoChargeItems="N" allowUspsAddr="N" requireUspsAddr="N" roleTypeId="CARRIER" shipmentMethodTypeId="EXPRESS" sequenceNumber="10"/>
-    <ProductStoreShipmentMeth productStoreId="9000" partyId="FEDEX" includeNoChargeItems="N" allowUspsAddr="N" requireUspsAddr="N" roleTypeId="CARRIER" shipmentMethodTypeId="SECOND_DAY" sequenceNumber="11"/>
-    <ProductStoreShipmentMeth productStoreId="9000" partyId="FEDEX" includeNoChargeItems="N" allowUspsAddr="N" requireUspsAddr="N" roleTypeId="CARRIER" shipmentMethodTypeId="GROUND" sequenceNumber="12"/>
-
+    <ProductStoreShipmentMeth productStoreId="9000" partyId="FEDEX" includeNoChargeItems="N" allowUspsAddr="N" requireUspsAddr="N" roleTypeId="CARRIER" shipmentMethodTypeId="NEXT_AM" sequenceNumber="11"/>
+    <ProductStoreShipmentMeth productStoreId="9000" partyId="FEDEX" includeNoChargeItems="N" allowUspsAddr="N" requireUspsAddr="N" roleTypeId="CARRIER" shipmentMethodTypeId="NEXT_DAY" sequenceNumber="12"/>
+    <ProductStoreShipmentMeth productStoreId="9000" partyId="FEDEX" includeNoChargeItems="N" allowUspsAddr="N" requireUspsAddr="N" roleTypeId="CARRIER" shipmentMethodTypeId="NEXT_PM" sequenceNumber="13"/>
+    <ProductStoreShipmentMeth productStoreId="9000" partyId="FEDEX" includeNoChargeItems="N" allowUspsAddr="N" requireUspsAddr="N" roleTypeId="CARRIER" shipmentMethodTypeId="SECOND_DAY" sequenceNumber="14"/>
+    <ProductStoreShipmentMeth productStoreId="9000" partyId="FEDEX" includeNoChargeItems="N" allowUspsAddr="N" requireUspsAddr="N" roleTypeId="CARRIER" shipmentMethodTypeId="EXPRESS" sequenceNumber="15"/>
+    <ProductStoreShipmentMeth productStoreId="9000" partyId="FEDEX" includeNoChargeItems="N" allowUspsAddr="N" requireUspsAddr="N" roleTypeId="CARRIER" shipmentMethodTypeId="GROUND" sequenceNumber="16"/>
+    <ProductStoreShipmentMeth productStoreId="9000" partyId="FEDEX" includeNoChargeItems="N" allowUspsAddr="N" requireUspsAddr="N" roleTypeId="CARRIER" shipmentMethodTypeId="GROUND_HOME" sequenceNumber="17"/>
+    
     <QuantityBreak fromQuantity="0.0" quantityBreakId="9000" quantityBreakTypeId="SHIP_WEIGHT" thruQuantity="0.0"/>
     <QuantityBreak fromQuantity="0.0" quantityBreakId="9001" quantityBreakTypeId="SHIP_WEIGHT" thruQuantity="0.0"/>
     <QuantityBreak fromQuantity="0.0" quantityBreakId="9002" quantityBreakTypeId="SHIP_WEIGHT" thruQuantity="0.0"/>
@@ -97,7 +107,36 @@
     <ShipmentCostEstimate productStoreId="9000" orderFlatPrice="9.0" orderItemFlatPrice="0.0" orderPricePercent="0.0" shipmentCostEstimateId="9101" shipmentMethodTypeId="NEXT_PM" carrierPartyId="DHL" carrierRoleTypeId="CARRIER" weightBreakId="9004" weightUnitPrice="0.2"/>
     <ShipmentCostEstimate productStoreId="9000" orderFlatPrice="5.0" orderItemFlatPrice="0.0" orderPricePercent="0.0" shipmentCostEstimateId="9102" shipmentMethodTypeId="SECOND_DAY" carrierPartyId="DHL" carrierRoleTypeId="CARRIER" weightBreakId="9005" weightUnitPrice="0.2"/>
     <ShipmentCostEstimate productStoreId="9000" orderFlatPrice="3.0" orderItemFlatPrice="0.0" orderPricePercent="0.0" shipmentCostEstimateId="9103" shipmentMethodTypeId="GROUND" carrierPartyId="DHL" carrierRoleTypeId="CARRIER" weightBreakId="9006" weightUnitPrice="0.1"/>
-    <ShipmentCostEstimate productStoreId="9000" orderFlatPrice="12.0" orderItemFlatPrice="0.0" orderPricePercent="0.0" shipmentCostEstimateId="9203" shipmentMethodTypeId="EXPRESS" carrierPartyId="FEDEX" carrierRoleTypeId="CARRIER" weightBreakId="9007" weightUnitPrice="0.1"/>
-    <ShipmentCostEstimate productStoreId="9000" orderFlatPrice="9.0" orderItemFlatPrice="0.0" orderPricePercent="0.0" shipmentCostEstimateId="9203" shipmentMethodTypeId="SECOND_DAY" carrierPartyId="FEDEX" carrierRoleTypeId="CARRIER" weightBreakId="9008" weightUnitPrice="0.1"/>
-    <ShipmentCostEstimate productStoreId="9000" orderFlatPrice="3.0" orderItemFlatPrice="0.0" orderPricePercent="0.0" shipmentCostEstimateId="9203" shipmentMethodTypeId="GROUND" carrierPartyId="FEDEX" carrierRoleTypeId="CARRIER" weightBreakId="9009" weightUnitPrice="0.1"/>
+    <ShipmentCostEstimate productStoreId="9000" orderFlatPrice="0.0" orderItemFlatPrice="0.0" orderPricePercent="0.0" shipmentCostEstimateId="9203" shipmentMethodTypeId="NEXT_AM" carrierPartyId="FEDEX" carrierRoleTypeId="CARRIER"/>
+    <ShipmentCostEstimate productStoreId="9000" orderFlatPrice="0.0" orderItemFlatPrice="0.0" orderPricePercent="0.0" shipmentCostEstimateId="9204" shipmentMethodTypeId="NEXT_DAY" carrierPartyId="FEDEX" carrierRoleTypeId="CARRIER"/>
+    <ShipmentCostEstimate productStoreId="9000" orderFlatPrice="0.0" orderItemFlatPrice="0.0" orderPricePercent="0.0" shipmentCostEstimateId="9205" shipmentMethodTypeId="NEXT_PM" carrierPartyId="FEDEX" carrierRoleTypeId="CARRIER"/>
+    <ShipmentCostEstimate productStoreId="9000" orderFlatPrice="0.0" orderItemFlatPrice="0.0" orderPricePercent="0.0" shipmentCostEstimateId="9206" shipmentMethodTypeId="SECOND_DAY" carrierPartyId="FEDEX" carrierRoleTypeId="CARRIER"/>
+    <ShipmentCostEstimate productStoreId="9000" orderFlatPrice="0.0" orderItemFlatPrice="0.0" orderPricePercent="0.0" shipmentCostEstimateId="9207" shipmentMethodTypeId="EXPRESS" carrierPartyId="FEDEX" carrierRoleTypeId="CARRIER"/>
+    <ShipmentCostEstimate productStoreId="9000" orderFlatPrice="0.0" orderItemFlatPrice="0.0" orderPricePercent="0.0" shipmentCostEstimateId="9208" shipmentMethodTypeId="GROUND" carrierPartyId="FEDEX" carrierRoleTypeId="CARRIER"/>
+    <ShipmentCostEstimate productStoreId="9000" orderFlatPrice="0.0" orderItemFlatPrice="0.0" orderPricePercent="0.0" shipmentCostEstimateId="9209" shipmentMethodTypeId="GROUND_HOME" carrierPartyId="FEDEX" carrierRoleTypeId="CARRIER"/>
+
+    <ShipmentBoxType shipmentBoxTypeId="FXENV" description="FedEx Envelope" dimensionUomId="LEN_in" boxLength="9.5" boxWidth="12.5" boxHeight="0"/>
+    <ShipmentBoxType shipmentBoxTypeId="FXENV_LGL" description="FedEx Envelope (Legal)" dimensionUomId="LEN_in" boxLength="10" boxWidth="15.75" boxHeight="0"/>
+    <ShipmentBoxType shipmentBoxTypeId="FXPAK_SM" description="FedEx Pak (Small)" dimensionUomId="LEN_in" boxLength="10.25" boxWidth="12.75" boxHeight="0"/>
+    <ShipmentBoxType shipmentBoxTypeId="FXPAK_LRG" description="FedEx Pak (Large)" dimensionUomId="LEN_in" boxLength="12" boxWidth="15.5" boxHeight="0"/>
+    <ShipmentBoxType shipmentBoxTypeId="FXBOX_SM" description="FedEx Box (Small)" dimensionUomId="LEN_in" boxLength="12.25" boxWidth="10.875" boxHeight="1.5"/>
+    <ShipmentBoxType shipmentBoxTypeId="FXBOX_MED" description="FedEx Box (Medium)" dimensionUomId="LEN_in" boxLength="13.25" boxWidth="11.5" boxHeight="2.375"/>
+    <ShipmentBoxType shipmentBoxTypeId="FXBOX_LRG" description="FedEx Box (Large)" dimensionUomId="LEN_in" boxLength="17.875" boxWidth="12.375" boxHeight="3"/>
+    <ShipmentBoxType shipmentBoxTypeId="FXTUBE" description="FedEx Tube" dimensionUomId="LEN_in" boxLength="38" boxWidth="6" boxHeight="5.2"/>
+    <ShipmentBoxType shipmentBoxTypeId="FX10KGBOX" description="FedEx 10KG Box" dimensionUomId="LEN_in" boxLength="15.8125" boxWidth="12.9375" boxHeight="10.1875"/>
+    <ShipmentBoxType shipmentBoxTypeId="FX25KGBOX" description="FedEx 25KG Box" dimensionUomId="LEN_in" boxLength="21.5625" boxWidth="16.5625" boxHeight="13.1875"/>
+    <ShipmentBoxType shipmentBoxTypeId="YOURPACKNG" description="Your Packaging" dimensionUomId="LEN_in" boxLength="" boxWidth="" boxHeight=""/>
+
+    <CarrierShipmentBoxType shipmentBoxTypeId="FXENV" partyId="FEDEX" packagingTypeCode="FEDEXENVELOPE"/>
+    <CarrierShipmentBoxType shipmentBoxTypeId="FXENV_LGL" partyId="FEDEX" packagingTypeCode="FEDEXENVELOPE"/>
+    <CarrierShipmentBoxType shipmentBoxTypeId="FXPAK_SM" partyId="FEDEX" packagingTypeCode="FEDEXPAK"/>
+    <CarrierShipmentBoxType shipmentBoxTypeId="FXPAK_LRG" partyId="FEDEX" packagingTypeCode="FEDEXPAK"/>
+    <CarrierShipmentBoxType shipmentBoxTypeId="FXBOX_SM" partyId="FEDEX" packagingTypeCode="FEDEXBOX"/>
+    <CarrierShipmentBoxType shipmentBoxTypeId="FXBOX_MED" partyId="FEDEX" packagingTypeCode="FEDEXBOX"/>
+    <CarrierShipmentBoxType shipmentBoxTypeId="FXBOX_LRG" partyId="FEDEX" packagingTypeCode="FEDEXBOX"/>
+    <CarrierShipmentBoxType shipmentBoxTypeId="FXTUBE" partyId="FEDEX" packagingTypeCode="FEDEXTUBE"/>
+    <CarrierShipmentBoxType shipmentBoxTypeId="FX10KGBOX" partyId="FEDEX" packagingTypeCode="FEDEX10KGBOX"/>
+    <CarrierShipmentBoxType shipmentBoxTypeId="FX25KGBOX" partyId="FEDEX" packagingTypeCode="FEDEX25KGBOX"/>
+    <CarrierShipmentBoxType shipmentBoxTypeId="YOURPACKNG" partyId="FEDEX" packagingTypeCode="YOURPACKAGING"/>
+
 </entity-engine-xml>

Modified: ofbiz/trunk/applications/product/config/ProductUiLabels.properties
URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/product/config/ProductUiLabels.properties?view=diff&rev=504996&r1=504995&r2=504996
==============================================================================
--- ofbiz/trunk/applications/product/config/ProductUiLabels.properties (original)
+++ ofbiz/trunk/applications/product/config/ProductUiLabels.properties Thu Feb  8 10:41:20 2007
@@ -401,6 +401,7 @@
 ProductConfirmSelectedMoves=Confirm selected moves
 ProductConfirmShipmentUps=Confirm Shipment with UPS
 ProductConfirmShipmentDHL=Confirm Shipment with DHL
+ProductConfirmShipmentFedex=Confirm Shipment with Fedex
 ProductContactTypeInformation=Contact Type Information
 ProductContainerLetter=C
 ProductContent=Content
@@ -1168,11 +1169,17 @@
 ProductShipamountPrice=shipamount=shipamount + price
 ProductShipamountTotalQuantityPrice=shipamount=shipamount + (totalQuantity * price)
 ProductShipamount=shipamount
+ProductShipmentBoxType=Box Type
+ProductShipmentFedexHomeDateCertain=Date Certain
+ProductShipmentFedexHomeDeliveryTypeDate=Home Delivery Type/Date (FedEx only)
+ProductShipmentFedexHomeEvening=Evening
+ProductShipmentFedexHomeAppointment=Appointment
 ProductShipmentId=Shipment Id
 ProductShipmentItemSeqId=Shipment Item Seq Id
 ProductShipmentManifest=Manifest for Shipment
 ProductShipmentMethod=Shipment Method
 ProductShipmentMethodType=Shipment Method Type
+ProductShipmentNone=None
 ProductShipmentNotFoundId=The Shipment was not found with ID
 ProductShipmentPlan=Shipment Plan
 ProductShipmentPlanToOrderItems=Shipment Plan --> Order Items

Modified: ofbiz/trunk/applications/product/config/shipment.properties
URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/product/config/shipment.properties?view=diff&rev=504996&r1=504995&r2=504996
==============================================================================
--- ofbiz/trunk/applications/product/config/shipment.properties (original)
+++ ofbiz/trunk/applications/product/config/shipment.properties Thu Feb  8 10:41:20 2007
@@ -21,6 +21,7 @@
 # General Configuration
 ############################################
 shipment.default.weight.uom=WT_lb
+shipment.default.dimension.uom=LEN_in
 shipment.default.weight.value=1
 
 ############################################
@@ -110,3 +111,48 @@
 
 # DHL Shipment Label configuration
 shipment.dhl.label.image.format=PNG
+
+
+############################################
+# FedEx Ship Manager Direct Configuration
+############################################
+
+# Template locations
+shipment.template.fedex.ship.location=component://product/templates/shipment/FedexShipRequestTemplate.xml
+shipment.template.fedex.subscription.location=component://product/templates/shipment/FedexSubscriptionRequestTemplate.xml
+
+# Credentials
+shipment.fedex.access.accountNbr=
+shipment.fedex.access.meterNumber=
+
+# Connection details
+# https://gatewaybeta.fedex.com/GatewayDC - Test URL
+# https://gateway.fedex.com/GatewayDC     - Production URL
+shipment.fedex.connect.url=https://gatewaybeta.fedex.com/GatewayDC
+shipment.fedex.connect.timeout=60
+
+# Label configuration
+# labelImageType must be PDF or PNG (PNG valid only for intra-US shipments)
+shipment.fedex.labelImageType=PNG
+
+# Default dropoff type
+#REGULARPICKUP      - Regular Pickup
+#REQUESTCOURIER     - Request Courier
+#DROPBOX            - Drop-Box
+#BUSINESSSERVICECTR - Business Service Center
+#STATION            - Station
+shipment.fedex.default.dropoffType=REGULARPICKUP
+
+# Default packaging type
+#FXENV      - FedEx Envelope
+#FXENV_LGL  - FedEx Envelope (Legal)
+#FXPAK_SM   - FedEx Pak (Small)
+#FXPAK_LRG  - FedEx Pak (Large)
+#FXBOX_SM   - FedEx Box (Small)
+#FXBOX_MED  - FedEx Box (Medium)
+#FXBOX_LRG  - FedEx Box (Large)
+#FXTUBE     - FedEx Tube
+#FX10KGBOX  - FedEx 10KG Box
+#FX25KGBOX  - FedEx 25KG Box
+#YOURPACKNG - Your Packaging
+shipment.fedex.default.packagingType=YOURPACKNG

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=504996&r1=504995&r2=504996
==============================================================================
--- ofbiz/trunk/applications/product/entitydef/entitymodel_shipment.xml (original)
+++ ofbiz/trunk/applications/product/entitydef/entitymodel_shipment.xml Thu Feb  8 10:41:20 2007
@@ -444,7 +444,7 @@
             title="Carrier Shipment Method Entity">
       <field name="shipmentBoxTypeId" type="id-ne"></field>
       <field name="partyId" type="id-ne"></field>
-      <field name="packagingTypeCode" type="very-short"></field>
+      <field name="packagingTypeCode" type="id"></field>
       <field name="oversizeCode" type="very-short"></field>
       <prim-key field="shipmentBoxTypeId"/>
       <prim-key field="partyId"/>
@@ -1108,6 +1108,8 @@
       <field name="trackingDigest" type="very-long"></field>
       <field name="updatedByUserLoginId" type="id-vlong"></field>
       <field name="lastUpdatedDate" type="date-time"></field>
+      <field name="homeDeliveryType" type="id"></field>
+      <field name="homeDeliveryDate" type="date-time"></field>
       <prim-key field="shipmentId"/>
       <prim-key field="shipmentRouteSegmentId"/>
       <relation type="one" fk-name="SHPMT_RTSEG_SHPMT" rel-entity-name="Shipment">

Modified: ofbiz/trunk/applications/product/ofbiz-component.xml
URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/product/ofbiz-component.xml?view=diff&rev=504996&r1=504995&r2=504996
==============================================================================
--- ofbiz/trunk/applications/product/ofbiz-component.xml (original)
+++ ofbiz/trunk/applications/product/ofbiz-component.xml Thu Feb  8 10:41:20 2007
@@ -44,6 +44,7 @@
     <service-resource type="model" loader="main" location="servicedef/services_picklist.xml"/>
     <service-resource type="model" loader="main" location="servicedef/services_pricepromo.xml"/>
     <service-resource type="model" loader="main" location="servicedef/services_shipment_dhl.xml"/>
+    <service-resource type="model" loader="main" location="servicedef/services_shipment_fedex.xml"/>
     <service-resource type="model" loader="main" location="servicedef/services_shipment_ups.xml"/>
     <service-resource type="model" loader="main" location="servicedef/services_shipment_usps.xml"/>
     <service-resource type="model" loader="main" location="servicedef/services_shipment.xml"/>

Added: ofbiz/trunk/applications/product/servicedef/services_shipment_fedex.xml
URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/product/servicedef/services_shipment_fedex.xml?view=auto&rev=504996
==============================================================================
--- ofbiz/trunk/applications/product/servicedef/services_shipment_fedex.xml (added)
+++ ofbiz/trunk/applications/product/servicedef/services_shipment_fedex.xml Thu Feb  8 10:41:20 2007
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+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.
+-->
+
+<services xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.ofbiz.org/dtds/services.xsd">
+  <description>Fedex Shipment Interface Services, based on Ship Manager Direct XML API</description>
+
+  <!-- FDXSubscriptionRequest/FDXSubscriptionReply -->
+  <service name="fedexSubscriptionRequest" engine="java" location="org.ofbiz.shipment.thirdparty.fedex.FedexServices" invoke="fedexSubscriptionRequest" auth="false">
+    <description>Sends a subscription request to FedEx to get the meter number, which is required for Fedex Ship Manager Direct as shipping credential.
+        You must supply a contact name and a partyId (usually your company's with a valid address and phone number per Fedex requirements.</description>
+    <attribute name="contactPartyName" type="String" mode="IN" optional="false"/>
+    <attribute name="companyPartyId" type="String" mode="IN" optional="false"/>
+    <attribute name="replaceMeterNumber" type="Boolean" mode="IN" optional="false"/>
+    <attribute name="meterNumber" type="String" mode="OUT" optional="false"/>
+  </service>
+
+  <!-- FDXShipRequest/FDXShipReply -->
+  <service name="fedexShipRequest" engine="java" location="org.ofbiz.shipment.thirdparty.fedex.FedexServices" invoke="fedexShipRequest" auth="false">
+    <description>Schedule a shipment route segment with FedEx.  You will get back a label image and tracking number which are stored for you in the same
+        ShipmentRouteSegment.</description>
+    <attribute name="shipmentId" type="String" mode="IN" optional="false"/>
+    <attribute name="shipmentRouteSegmentId" type="String" mode="IN" optional="false"/>
+  </service>
+
+</services>

Propchange: ofbiz/trunk/applications/product/servicedef/services_shipment_fedex.xml
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: ofbiz/trunk/applications/product/servicedef/services_shipment_fedex.xml
------------------------------------------------------------------------------
    svn:keywords = Date Rev Author URL Id

Propchange: ofbiz/trunk/applications/product/servicedef/services_shipment_fedex.xml
------------------------------------------------------------------------------
    svn:mime-type = text/xml

Added: ofbiz/trunk/applications/product/src/org/ofbiz/shipment/thirdparty/fedex/FedexServices.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/product/src/org/ofbiz/shipment/thirdparty/fedex/FedexServices.java?view=auto&rev=504996
==============================================================================
--- ofbiz/trunk/applications/product/src/org/ofbiz/shipment/thirdparty/fedex/FedexServices.java (added)
+++ ofbiz/trunk/applications/product/src/org/ofbiz/shipment/thirdparty/fedex/FedexServices.java Thu Feb  8 10:41:20 2007
@@ -0,0 +1,954 @@
+/*******************************************************************************
+ * 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.shipment.thirdparty.fedex;
+
+import org.ofbiz.base.util.*;
+import org.ofbiz.base.util.template.FreeMarkerWorker;
+import org.ofbiz.service.DispatchContext;
+import org.ofbiz.service.ServiceUtil;
+import org.ofbiz.service.LocalDispatcher;
+import org.ofbiz.service.GenericServiceException;
+import org.ofbiz.entity.GenericValue;
+import org.ofbiz.entity.GenericDelegator;
+import org.ofbiz.entity.GenericEntityException;
+import org.ofbiz.entity.condition.EntityExpr;
+import org.ofbiz.entity.condition.EntityOperator;
+import org.ofbiz.entity.condition.EntityConditionList;
+import org.ofbiz.entity.util.EntityUtil;
+import org.ofbiz.party.party.PartyHelper;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.xml.sax.SAXException;
+
+import javax.xml.parsers.ParserConfigurationException;
+import java.util.*;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.math.BigDecimal;
+import java.sql.Timestamp;
+
+/**
+ * Fedex Shipment Services
+ *
+ * Implementation of Fedex shipment interface using Ship Manager Direct API
+ *
+ * TODO: FDXShipDeleteRequest/Reply (on error and via service call)
+ * TODO: FDXCloseRequest/Reply
+ * TODO: FDXRateRequest/Reply
+ * TODO: FDXTrackRequest/Reply
+ * TODO: International shipments
+ * TODO: Multi-piece shipments
+ * TODO: Freight shipments
+ */
+public class FedexServices {
+
+    public final static String module = FedexServices.class.getName();
+    public final static String shipmentPropertiesFile = "shipment.properties";
+
+    /**
+     * Opens a URL to Fedex and makes a request.
+     *
+     * @param xmlString XML message to send
+     * @return XML string response from FedEx
+     * @throws FedexConnectException
+     */
+    public static String sendFedexRequest(String xmlString) throws FedexConnectException {
+        String url = UtilProperties.getPropertyValue(shipmentPropertiesFile, "shipment.fedex.connect.url");
+        if (url == null) {
+            throw new FedexConnectException("Incomplete connection URL; check your Fedex configuration");
+        }
+
+        // xmlString should contain the auth document at the beginning
+        // all documents require an <?xml version="1.0" encoding="UTF-8" ?> header
+        if (! xmlString.matches( "^(?s)<\\?xml\\s+version=\"1\\.0\"\\s+encoding=\"UTF-8\"\\s*\\?>.*")) {
+            throw new FedexConnectException("XML header is malformed");
+        }
+
+        // prepare the connect string
+        url = url.trim();
+
+        String timeOutStr = UtilProperties.getPropertyValue(shipmentPropertiesFile, "shipment.fedex.connect.timeout", "60");
+        int timeout = 60;
+        try {
+            timeout = Integer.parseInt(timeOutStr);
+        } catch (NumberFormatException e) {
+            Debug.logError(e, "Unable to set timeout to " + timeOutStr + " using default " + timeout);
+        }
+
+        if (Debug.verboseOn()) {
+            Debug.logVerbose("Fedex Connect URL : " + url, module);
+            Debug.logVerbose("Fedex XML String : " + xmlString, module);
+        }
+
+        HttpClient http = new HttpClient(url);
+        http.setTimeout(timeout * 1000);
+        String response = null;
+        try {
+            response = http.post(xmlString);
+        } catch ( HttpClientException e) {
+            Debug.logError(e, "Problem connecting to Fedex server", module);
+            throw new FedexConnectException("URL Connection problem", e);
+        }
+
+        if (response == null) {
+            throw new FedexConnectException("Received a null response");
+        }
+        if (Debug.verboseOn()) {
+            Debug.logVerbose("Fedex Response : " + response, module);
+        }
+
+        return response;
+    }
+
+    /*
+    * Register a Fedex account for shipping by obtaining the meter number
+    */
+    public static Map fedexSubscriptionRequest( DispatchContext dctx, Map context) {
+        GenericDelegator delegator = dctx.getDelegator();
+        List errorList = new ArrayList();
+
+        Boolean replaceMeterNumber = (Boolean) context.get("replaceMeterNumber");
+
+        if(! replaceMeterNumber.booleanValue()) {
+            String meterNumber = UtilProperties.getPropertyValue(shipmentPropertiesFile, "shipment.fedex.access.meterNumber");
+            if (UtilValidate.isNotEmpty(meterNumber)) {
+                return ServiceUtil.returnError("MeterNumber already exists: " + shipmentPropertiesFile + ":shipment.fedex.access.meterNumber=" + meterNumber);
+            }
+        }
+
+        String companyPartyId = (String) context.get("companyPartyId");
+        String contactPartyName = (String) context.get("contactPartyName");
+
+        Map result = new HashMap();
+
+        String accountNumber = UtilProperties.getPropertyValue(shipmentPropertiesFile, "shipment.fedex.access.accountNbr");
+        if (UtilValidate.isEmpty(accountNumber)) {
+            return ServiceUtil.returnError("accountNbr not found for Fedex subscription request.");
+        }
+
+        if(UtilValidate.isEmpty(contactPartyName)) {
+            return ServiceUtil.returnError("Contact name can't be empty.");
+        }
+
+        String companyName = null;
+        GenericValue postalAddress = null;
+        String phoneNumber = null;
+        String faxNumber = null;
+        String emailAddress = null;
+        try {
+            
+            // Make sure the company exists
+            GenericValue companyParty = delegator.findByPrimaryKeyCache("Party", UtilMisc.toMap("partyId", companyPartyId));
+            if (companyParty == null) {
+                String errorMessage = "Party with partyId " + companyPartyId + " does not exist";
+                Debug.logError(errorMessage, module);
+                return ServiceUtil.returnError(errorMessage);
+            }
+
+            // Get the company name (required by Fedex)
+            companyName = PartyHelper.getPartyName(companyParty);
+            if (UtilValidate.isEmpty(companyName)) {
+                String errorMessage = "Party with partyId " + companyPartyId + " has no name";
+                Debug.logError(errorMessage, module);
+                return ServiceUtil.returnError(errorMessage);
+            }
+
+            // Get the contact information for the company
+            List partyContactDetails = delegator.findByAnd("PartyContactDetailByPurpose", UtilMisc.toMap("partyId", companyPartyId));
+            partyContactDetails = EntityUtil.filterByDate(partyContactDetails);
+            partyContactDetails = EntityUtil.filterByDate(partyContactDetails, UtilDateTime.nowTimestamp(), "purposeFromDate", "purposeThruDate", true);
+
+            // Get the first valid postal address (address1, city, postalCode and countryGeoId are required by Fedex)
+            List postalAddressConditions = new ArrayList();
+            postalAddressConditions.add(new EntityExpr("contactMechTypeId", EntityOperator.EQUALS, "POSTAL_ADDRESS"));
+            postalAddressConditions.add(new EntityExpr("address1", EntityOperator.NOT_EQUAL, null));
+            postalAddressConditions.add(new EntityExpr("address1", EntityOperator.NOT_EQUAL, ""));
+            postalAddressConditions.add(new EntityExpr("city", EntityOperator.NOT_EQUAL, null));
+            postalAddressConditions.add(new EntityExpr("city", EntityOperator.NOT_EQUAL, ""));
+            postalAddressConditions.add(new EntityExpr("postalCode", EntityOperator.NOT_EQUAL, null));
+            postalAddressConditions.add(new EntityExpr("postalCode", EntityOperator.NOT_EQUAL, ""));
+            postalAddressConditions.add(new EntityExpr("countryGeoId", EntityOperator.NOT_EQUAL, null));
+            postalAddressConditions.add(new EntityExpr("countryGeoId", EntityOperator.NOT_EQUAL, ""));
+            List postalAddresses = EntityUtil.filterByCondition(partyContactDetails, new EntityConditionList(postalAddressConditions, EntityOperator.AND));
+
+            // Fedex requires USA or Canada addresses to have a state/province ID, so filter out the ones without
+            postalAddressConditions.clear();
+            postalAddressConditions.add(new EntityExpr("countryGeoId", EntityOperator.IN, UtilMisc.toList("CAN", "USA")));
+            postalAddressConditions.add(new EntityExpr("stateProvinceGeoId", EntityOperator.EQUALS, null));
+            postalAddresses = EntityUtil.filterOutByCondition(postalAddresses, new EntityConditionList(postalAddressConditions, EntityOperator.AND));
+            postalAddressConditions.clear();
+            postalAddressConditions.add(new EntityExpr("countryGeoId", EntityOperator.IN, UtilMisc.toList("CAN", "USA")));
+            postalAddressConditions.add(new EntityExpr("stateProvinceGeoId", EntityOperator.EQUALS, ""));
+            postalAddresses = EntityUtil.filterOutByCondition(postalAddresses, new EntityConditionList(postalAddressConditions, EntityOperator.AND));
+
+            postalAddress = EntityUtil.getFirst(postalAddresses);
+            if (UtilValidate.isEmpty(postalAddress)) {
+                String errorMessage = "Party with partyId " + companyPartyId + " does not have a current, fully populated postal address";
+                Debug.logError(errorMessage, module);
+                return ServiceUtil.returnError(errorMessage);
+            }
+            GenericValue countryGeo = delegator.findByPrimaryKeyCache("Geo", UtilMisc.toMap("geoId", postalAddress.getString("countryGeoId")));
+            String countryCode = countryGeo.getString("geoCode");
+            String stateOrProvinceCode = null;
+            // Only add the StateOrProvinceCode element if the address is in USA or Canada
+            if (countryCode.equals("CA") || countryCode.equals("US")) {
+                GenericValue stateProvinceGeo = delegator.findByPrimaryKeyCache("Geo", UtilMisc.toMap("geoId", postalAddress.getString("stateProvinceGeoId")));
+                stateOrProvinceCode = stateProvinceGeo.getString("geoCode");
+            }
+
+            // Get the first valid primary phone number (required by Fedex)
+            List phoneNumberConditions = new ArrayList();
+            phoneNumberConditions.add(new EntityExpr("contactMechTypeId", EntityOperator.EQUALS, "TELECOM_NUMBER"));
+            phoneNumberConditions.add(new EntityExpr("contactMechPurposeTypeId", EntityOperator.EQUALS, "PRIMARY_PHONE"));
+            phoneNumberConditions.add(new EntityExpr("areaCode", EntityOperator.NOT_EQUAL, null));
+            phoneNumberConditions.add(new EntityExpr("areaCode", EntityOperator.NOT_EQUAL, ""));
+            phoneNumberConditions.add(new EntityExpr("contactNumber", EntityOperator.NOT_EQUAL, null));
+            phoneNumberConditions.add(new EntityExpr("contactNumber", EntityOperator.NOT_EQUAL, ""));
+            List phoneNumbers = EntityUtil.filterByCondition(partyContactDetails, new EntityConditionList(phoneNumberConditions, EntityOperator.AND));
+            GenericValue phoneNumberValue = EntityUtil.getFirst(phoneNumbers);
+            if (UtilValidate.isEmpty(phoneNumberValue)) {
+                String errorMessage = "Party with partyId " + companyPartyId + " does not have a current, fully populated primary phone number";
+                Debug.logError(errorMessage, module);
+                return ServiceUtil.returnError(errorMessage);
+            }
+            phoneNumber = phoneNumberValue.getString("areaCode") + phoneNumberValue.getString("contactNumber");
+            if (UtilValidate.isNotEmpty(phoneNumberValue.getString("countryCode"))) phoneNumber = phoneNumberValue.getString("countryCode") + phoneNumber;
+            phoneNumber = phoneNumber.replaceAll("[^+\\d]", "");
+
+            // Get the first valid fax number
+            List faxNumberConditions = new ArrayList();
+            faxNumberConditions.add(new EntityExpr("contactMechTypeId", EntityOperator.EQUALS, "TELECOM_NUMBER"));
+            faxNumberConditions.add(new EntityExpr("contactMechPurposeTypeId", EntityOperator.EQUALS, "FAX_NUMBER"));
+            faxNumberConditions.add(new EntityExpr("areaCode", EntityOperator.NOT_EQUAL, null));
+            faxNumberConditions.add(new EntityExpr("areaCode", EntityOperator.NOT_EQUAL, ""));
+            faxNumberConditions.add(new EntityExpr("contactNumber", EntityOperator.NOT_EQUAL, null));
+            faxNumberConditions.add(new EntityExpr("contactNumber", EntityOperator.NOT_EQUAL, ""));
+            List faxNumbers = EntityUtil.filterByCondition(partyContactDetails, new EntityConditionList(faxNumberConditions, EntityOperator.AND));
+            GenericValue faxNumberValue = EntityUtil.getFirst(faxNumbers);
+            if(! UtilValidate.isEmpty(faxNumberValue)) {
+                faxNumber = faxNumberValue.getString("areaCode") + faxNumberValue.getString("contactNumber");
+                if (UtilValidate.isNotEmpty(faxNumberValue.getString("countryCode"))) faxNumber = faxNumberValue.getString("countryCode") + faxNumber;
+                faxNumber = faxNumber.replaceAll("[^+\\d]", "");
+            }
+
+            // Get the first valid email address
+            List emailConditions = new ArrayList();
+            emailConditions.add(new EntityExpr("contactMechTypeId", EntityOperator.EQUALS, "EMAIL_ADDRESS"));
+            emailConditions.add(new EntityExpr("infoString", EntityOperator.NOT_EQUAL, null));
+            emailConditions.add(new EntityExpr("infoString", EntityOperator.NOT_EQUAL, ""));
+            List emailAddresses = EntityUtil.filterByCondition(partyContactDetails, new EntityConditionList(emailConditions, EntityOperator.AND));
+            GenericValue emailAddressValue = EntityUtil.getFirst(emailAddresses);
+            if(! UtilValidate.isEmpty(emailAddressValue)) {
+                emailAddress = emailAddressValue.getString("infoString");
+            }
+
+            // Get the location of the Freemarker (XML) template for the FDXSubscriptionRequest
+            String templateLocation = UtilProperties.getPropertyValue(shipmentPropertiesFile, "shipment.template.fedex.subscription.location");
+            if (UtilValidate.isEmpty(templateLocation)) {
+                return ServiceUtil.returnError("Can't find location for FDXSubscriptionRequest template - should be in " + shipmentPropertiesFile + ":shipment.template.fedex.subscription.location");
+            }
+            
+            // Populate the Freemarker context
+            Map subscriptionRequestContext = new HashMap();
+            subscriptionRequestContext.put("AccountNumber", accountNumber);
+            subscriptionRequestContext.put("PersonName", contactPartyName);
+            subscriptionRequestContext.put("CompanyName", companyName);
+            subscriptionRequestContext.put("PhoneNumber", phoneNumber);
+            if (UtilValidate.isNotEmpty(faxNumber)) {
+                subscriptionRequestContext.put("FaxNumber", faxNumber);
+            }
+            if (UtilValidate.isNotEmpty(emailAddress)) {
+                subscriptionRequestContext.put("EMailAddress", emailAddress);
+            }
+            subscriptionRequestContext.put("Line1", postalAddress.getString("address1"));
+            if (UtilValidate.isNotEmpty(postalAddress.getString("address2"))) {
+                subscriptionRequestContext.put("Line2", postalAddress.getString("address2"));
+            }
+            subscriptionRequestContext.put("City", postalAddress.getString("city"));
+            if (UtilValidate.isNotEmpty(stateOrProvinceCode)) {
+                subscriptionRequestContext.put("StateOrProvinceCode", stateOrProvinceCode);
+            }
+            subscriptionRequestContext.put("PostalCode", postalAddress.getString("postalCode"));
+            subscriptionRequestContext.put("CountryCode", countryCode);
+
+            StringWriter outWriter = new StringWriter();
+            try {
+                FreeMarkerWorker.renderTemplateAtLocation(templateLocation, subscriptionRequestContext, outWriter);
+            } catch (Exception e) {
+                String errorMessage = "Cannot send Fedex subscription request: Failed to render Fedex XML Subscription Request Template [" + templateLocation + "].";
+                Debug.logError(e, errorMessage, module);
+                return ServiceUtil.returnError(errorMessage + ": " + e.getMessage());
+            }
+            String fDXSubscriptionRequestString = outWriter.toString();
+
+            // Send the request
+            String fDXSubscriptionReplyString = null;
+            try {
+                fDXSubscriptionReplyString = sendFedexRequest(fDXSubscriptionRequestString);
+                Debug.log("Fedex response for FDXSubscriptionRequest:" + fDXSubscriptionReplyString);
+            } catch (FedexConnectException e) {
+                String errorMessage = "Error sending Fedex request for FDXSubscriptionRequest: " + e.toString();
+                Debug.logError(e, errorMessage, module);
+                return ServiceUtil.returnError(errorMessage);
+            }
+
+            Document fDXSubscriptionReplyDocument = null;
+            try {
+                fDXSubscriptionReplyDocument = UtilXml.readXmlDocument(fDXSubscriptionReplyString, false);
+                Debug.log("Fedex response for FDXSubscriptionRequest:" + fDXSubscriptionReplyString);
+            } catch ( SAXException se) {
+                String errorMessage = "Error parsing the FDXSubscriptionRequest response: " + se.toString();
+                Debug.logError(se, errorMessage, module);
+                return ServiceUtil.returnError(errorMessage);
+            } catch ( ParserConfigurationException pce) {
+                String errorMessage = "Error parsing the FDXSubscriptionRequest response: " + pce.toString();
+                Debug.logError(pce, errorMessage, module);
+                return ServiceUtil.returnError(errorMessage);
+            } catch (IOException ioe) {
+                String errorMessage = "Error parsing the FDXSubscriptionRequest response: " + ioe.toString();
+                Debug.logError(ioe, errorMessage, module);
+                return ServiceUtil.returnError(errorMessage);
+            }
+
+            Element fedexSubscriptionReplyElement = fDXSubscriptionReplyDocument.getDocumentElement();
+            handleErrors(fedexSubscriptionReplyElement, errorList);
+    
+            if (UtilValidate.isNotEmpty(errorList)) {
+                return ServiceUtil.returnError(errorList);
+            }
+    
+            String meterNumber = UtilXml.childElementValue(fedexSubscriptionReplyElement, "MeterNumber");
+    
+            result.put("meterNumber", meterNumber);
+
+        } catch( GenericEntityException e ) {
+            Debug.logError(e, module);
+            return ServiceUtil.returnError(e.getMessage());
+        }
+
+        return result;
+    }
+
+    /**
+     *
+     * Send a FDXShipRequest via the Ship Manager Direct API
+     */
+    public static Map fedexShipRequest( DispatchContext dctx, Map context) {
+        GenericDelegator delegator = dctx.getDelegator();
+        LocalDispatcher dispatcher = dctx.getDispatcher();
+        GenericValue userLogin = (GenericValue) context.get("userLogin");        
+        Locale locale = (Locale) context.get("locale");        
+        Map result = ServiceUtil.returnSuccess();
+        
+        String shipmentId = (String) context.get("shipmentId");
+        String shipmentRouteSegmentId = (String) context.get("shipmentRouteSegmentId");
+
+        // Get the location of the Freemarker (XML) template for the FDXShipRequest
+        String templateLocation = UtilProperties.getPropertyValue(shipmentPropertiesFile, "shipment.template.fedex.ship.location");
+        if (UtilValidate.isEmpty(templateLocation)) {
+            return ServiceUtil.returnError("Can't find location for FDXShipRequest template - should be in " + shipmentPropertiesFile + ":shipment.template.fedex.ship.location");
+        }
+
+        // Get the Fedex account number
+        String accountNumber = UtilProperties.getPropertyValue(shipmentPropertiesFile, "shipment.fedex.access.accountNbr");
+        if (UtilValidate.isEmpty(accountNumber)) {
+            return ServiceUtil.returnError("accountNbr not found for Fedex ship request.");
+        }
+
+        // Get the Fedex meter number
+        String meterNumber = UtilProperties.getPropertyValue(shipmentPropertiesFile, "shipment.fedex.access.meterNumber");
+        if (UtilValidate.isEmpty(meterNumber)) {
+            return ServiceUtil.returnError("Meter number not found for Fedex ship request - should be in " + shipmentPropertiesFile + ":shipment.fedex.access.meterNumber (run the fedexSubscriptionRequest service).");
+        }
+
+        // Get the weight units to be used in the request
+        String weightUomId = UtilProperties.getPropertyValue(shipmentPropertiesFile, "shipment.default.weight.uom");
+        if (UtilValidate.isEmpty(weightUomId)) {
+            return ServiceUtil.returnError("Default weightUomId not found for Fedex ship request - should be in " + shipmentPropertiesFile + ":shipment.default.weight.uom.");
+        } else if (! ("WT_lb".equals(weightUomId) || "WT_kg".equals(weightUomId))) {
+            return ServiceUtil.returnError("WeightUomId in " + shipmentPropertiesFile + ":shipment.default.weight.uom must be either WT_lb or WT_kg.");
+        }
+
+        // Get the dimension units to be used in the request
+        String dimensionsUomId = UtilProperties.getPropertyValue(shipmentPropertiesFile, "shipment.default.dimension.uom");
+        if (UtilValidate.isEmpty(dimensionsUomId)) {
+            return ServiceUtil.returnError("Default dimensionUomId not found for Fedex ship request - should be in " + shipmentPropertiesFile + ":shipment.default.dimension.uom.");
+        } else if (! ("LEN_in".equals(dimensionsUomId) || "LEN_cm".equals(dimensionsUomId))) {
+            return ServiceUtil.returnError("WeightUomId in " + shipmentPropertiesFile + ":shipment.default.dimension.uom must be either LEN_in or LEN_cm.");
+        }
+
+        // Get the label image type to be returned
+        String labelImageType = UtilProperties.getPropertyValue(shipmentPropertiesFile, "shipment.fedex.labelImageType");
+        if (UtilValidate.isEmpty(labelImageType)) {
+            return ServiceUtil.returnError("LabelImageType not found for Fedex ship request - should be in " + shipmentPropertiesFile + ":shipment.fedex.labelImageType.");
+        } else if (! ("PDF".equals(labelImageType) || "PNG".equals(labelImageType))) {
+            return ServiceUtil.returnError("LabelImageType in " + shipmentPropertiesFile + ":shipment.fedex.labelImageType must be either PDF or PNG.");
+        }
+
+        // Get the default dropoff type
+        String dropoffType = UtilProperties.getPropertyValue(shipmentPropertiesFile, "shipment.fedex.default.dropoffType");
+        if (UtilValidate.isEmpty(dropoffType)) {
+            return ServiceUtil.returnError("Default dropoff type not found for Fedex ship request - should be in " + shipmentPropertiesFile + ":shipment.fedex.default.dropoffType.");
+        }
+        
+        try {
+
+            Map shipRequestContext = new HashMap();
+
+            // Get the shipment and the shipmentRouteSegment
+            GenericValue shipment = delegator.findByPrimaryKey("Shipment", UtilMisc.toMap("shipmentId", shipmentId));
+            if (UtilValidate.isEmpty(shipment)) {
+                return ServiceUtil.returnError("Shipment not found with ID " + shipmentId);
+            }
+            GenericValue shipmentRouteSegment = delegator.findByPrimaryKey("ShipmentRouteSegment", UtilMisc.toMap("shipmentId", shipmentId, "shipmentRouteSegmentId", shipmentRouteSegmentId));
+            if (UtilValidate.isEmpty(shipmentRouteSegment)) {
+                return ServiceUtil.returnError("ShipmentRouteSegment not found with shipmentId " + shipmentId + " and shipmentRouteSegmentId " + shipmentRouteSegmentId);
+            }
+            
+            // Determine the Fedex carrier
+            String carrierPartyId = shipmentRouteSegment.getString("carrierPartyId");
+            if (! "FEDEX".equals(carrierPartyId)) {
+                return ServiceUtil.returnError("ERROR: The Carrier for ShipmentRouteSegment " + shipmentRouteSegmentId + " of Shipment " + shipmentId + ", is not Fedex.");
+            }
+            
+            // Check the shipmentRouteSegment's carrier status
+            if (UtilValidate.isNotEmpty(shipmentRouteSegment.getString("carrierServiceStatusId")) && !"SHRSCS_NOT_STARTED".equals(shipmentRouteSegment.getString("carrierServiceStatusId"))) {
+                return ServiceUtil.returnError("ERROR: The Carrier Service Status for ShipmentRouteSegment " + shipmentRouteSegmentId + " of Shipment " + shipmentId + ", is [" + shipmentRouteSegment.getString("carrierServiceStatusId") + "], but must be not-set or [SHRSCS_NOT_STARTED] to perform the Fedex Shipment Confirm operation.");
+            }
+            
+            // Translate shipmentMethodTypeId to Fedex service code and carrier code
+            String shipmentMethodTypeId = shipmentRouteSegment.getString("shipmentMethodTypeId");
+            GenericValue carrierShipmentMethod = delegator.findByPrimaryKey("CarrierShipmentMethod", UtilMisc.toMap("shipmentMethodTypeId", shipmentMethodTypeId, "partyId", "FEDEX", "roleTypeId", "CARRIER"));
+            if (UtilValidate.isEmpty(carrierShipmentMethod)) {
+                return ServiceUtil.returnError("No CarrierShipmentMethod entry for carrier Fedex shipmentMethodTypeId " + shipmentMethodTypeId);
+            }
+            if (UtilValidate.isEmpty(carrierShipmentMethod.getString("carrierServiceCode"))) {
+                return ServiceUtil.returnError("No Carrier service code for carrier Fedex shipmentMethodTypeId " + shipmentMethodTypeId);
+            }
+            String service = carrierShipmentMethod.getString("carrierServiceCode");
+            
+            // CarrierCode is FDXG only for FEDEXGROUND and GROUNDHOMEDELIVERY services.
+            boolean isGroundService = service.equals("FEDEXGROUND") || service.equals("GROUNDHOMEDELIVERY");
+            String carrierCode = isGroundService ? "FDXG" : "FDXE";
+
+            // 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");
+            }
+            
+            // Get and validate origin postal address
+            GenericValue originPostalAddress = shipmentRouteSegment.getRelatedOne("OriginPostalAddress");
+            if (UtilValidate.isEmpty(originPostalAddress)) {
+                return ServiceUtil.returnError("OriginPostalAddress not found for ShipmentRouteSegment with shipmentId " + shipmentId + " and shipmentRouteSegmentId " + shipmentRouteSegmentId);
+            } else if (UtilValidate.isEmpty(originPostalAddress.getString("address1"))      ||
+                       UtilValidate.isEmpty(originPostalAddress.getString("city"))          ||
+                       UtilValidate.isEmpty(originPostalAddress.getString("postalCode"))    ||
+                       UtilValidate.isEmpty(originPostalAddress.getString("countryGeoId"))) {
+                return ServiceUtil.returnError("OriginPostalAddress not complete for ShipmentRouteSegment with shipmentId " + shipmentId + " and shipmentRouteSegmentId " + shipmentRouteSegmentId + " (missing address1, city, postalCode and/or countryGeoId).");
+            }
+            GenericValue originCountryGeo = originPostalAddress.getRelatedOne("CountryGeo");
+            if (UtilValidate.isEmpty(originCountryGeo)) {
+                return ServiceUtil.returnError("OriginCountryGeo not found for ShipmentRouteSegment with shipmentId " + shipmentId + " and shipmentRouteSegmentId " + shipmentRouteSegmentId);
+            }
+            String originAddressCountryCode = originCountryGeo.getString("geoCode");
+            String originAddressStateOrProvinceCode = null;
+            
+            // Only add the StateOrProvinceCode element if the address is in USA or Canada
+            if (originAddressCountryCode.equals("CA") || originAddressCountryCode.equals("US")) {
+                if (UtilValidate.isEmpty(originPostalAddress.getString("stateProvinceGeoId"))) {
+                    return ServiceUtil.returnError("OriginStateProvinceGeoId required in contactMechId " + originPostalAddress.getString("contactMechId") + " for ShipmentRouteSegment with shipmentId " + shipmentId + " and shipmentRouteSegmentId " + shipmentRouteSegmentId);
+                }
+                GenericValue stateProvinceGeo = delegator.findByPrimaryKeyCache("Geo", UtilMisc.toMap("geoId", originPostalAddress.getString("stateProvinceGeoId")));
+                originAddressStateOrProvinceCode = stateProvinceGeo.getString("geoCode");
+            }
+            
+            // Get and validate origin telecom number
+            GenericValue originTelecomNumber = shipmentRouteSegment.getRelatedOne("OriginTelecomNumber");
+            if (UtilValidate.isEmpty(originTelecomNumber)) {
+                return ServiceUtil.returnError("OriginTelecomNumber not found for ShipmentRouteSegment with shipmentId " + shipmentId + " and shipmentRouteSegmentId " + shipmentRouteSegmentId);
+            }
+            String originContactPhoneNumber = originTelecomNumber.getString("areaCode") + originTelecomNumber.getString("contactNumber");
+            
+            // Fedex doesn't want the North American country code
+            if (UtilValidate.isNotEmpty(originTelecomNumber.getString("countryCode")) && !(originAddressCountryCode.equals("CA") || originAddressCountryCode.equals("US"))) {
+                originContactPhoneNumber = originTelecomNumber.getString("countryCode") + originContactPhoneNumber;
+            }
+            originContactPhoneNumber = originContactPhoneNumber.replaceAll("[^+\\d]", "");
+            
+            // Get the origin contact name from the owner of the origin facility
+            GenericValue partyFrom = null;
+            GenericValue originFacility = shipment.getRelatedOne("OriginFacility");
+            if (UtilValidate.isEmpty(originFacility)) {
+                return ServiceUtil.returnError("Shipment.originFacilityId is required for Fedex shipments: shipmentId " + shipmentId + ", shipmentRouteSegmentId " + shipmentRouteSegmentId);
+            } else {
+                partyFrom = originFacility.getRelatedOne("OwnerParty");
+                if (UtilValidate.isEmpty(partyFrom)) {
+                    return ServiceUtil.returnError("Facility.ownerPartyId is required for Fedex shipments: shipmentId " + shipmentId + ", shipmentRouteSegmentId " + shipmentRouteSegmentId + ", facilityId " + originFacility.getString("facilityId"));
+                }
+            }
+            
+            String originContactKey = "PERSON".equals(partyFrom.getString("partyTypeId")) ? "OriginContactPersonName" : "OriginContactCompanyName";
+            String originContactName = PartyHelper.getPartyName(partyFrom, false);
+            if (UtilValidate.isEmpty(originContactName)) {
+                return ServiceUtil.returnError("partyIdFrom for shipmentId " + shipmentId + ", shipmentRouteSegmentId " + shipmentRouteSegmentId + " has no name (required for Fedex shipments)" );
+            }
+            
+            // Get and validate destination postal address
+            GenericValue destinationPostalAddress = shipmentRouteSegment.getRelatedOne("DestPostalAddress");
+            if (UtilValidate.isEmpty(destinationPostalAddress)) {
+                return ServiceUtil.returnError("destinationPostalAddress not found for ShipmentRouteSegment with shipmentId " + shipmentId + " and shipmentRouteSegmentId " + shipmentRouteSegmentId);
+            } else if (UtilValidate.isEmpty(destinationPostalAddress.getString("address1"))      ||
+                       UtilValidate.isEmpty(destinationPostalAddress.getString("city"))          ||
+                       UtilValidate.isEmpty(destinationPostalAddress.getString("postalCode"))    ||
+                       UtilValidate.isEmpty(destinationPostalAddress.getString("countryGeoId"))) {
+                return ServiceUtil.returnError("destinationPostalAddress not complete for ShipmentRouteSegment with shipmentId " + shipmentId + " and shipmentRouteSegmentId " + shipmentRouteSegmentId + " (missing address1, city, postalCode and/or countryGeoId).");
+            }
+            GenericValue destinationCountryGeo = destinationPostalAddress.getRelatedOne("CountryGeo");
+            if (UtilValidate.isEmpty(destinationCountryGeo)) {
+                return ServiceUtil.returnError("destinationCountryGeo not found for ShipmentRouteSegment with shipmentId " + shipmentId + " and shipmentRouteSegmentId " + shipmentRouteSegmentId);
+            }
+            String destinationAddressCountryCode = destinationCountryGeo.getString("geoCode");
+            String destinationAddressStateOrProvinceCode = null;
+            
+            // Only add the StateOrProvinceCode element if the address is in USA or Canada
+            if (destinationAddressCountryCode.equals("CA") || destinationAddressCountryCode.equals("US")) {
+                if (UtilValidate.isEmpty(destinationPostalAddress.getString("stateProvinceGeoId"))) {
+                    return ServiceUtil.returnError("destinationStateProvinceGeoId required in contactMechId " + destinationPostalAddress.getString("contactMechId") + " for ShipmentRouteSegment with shipmentId " + shipmentId + " and shipmentRouteSegmentId " + shipmentRouteSegmentId);
+                }
+                GenericValue stateProvinceGeo = delegator.findByPrimaryKeyCache("Geo", UtilMisc.toMap("geoId", destinationPostalAddress.getString("stateProvinceGeoId")));
+                destinationAddressStateOrProvinceCode = stateProvinceGeo.getString("geoCode");
+            }
+            
+            // Get and validate destination telecom number
+            GenericValue destinationTelecomNumber = shipmentRouteSegment.getRelatedOne("DestTelecomNumber");
+            if (UtilValidate.isEmpty(destinationTelecomNumber)) {
+                return ServiceUtil.returnError("destinationTelecomNumber not found for ShipmentRouteSegment with shipmentId " + shipmentId + " and shipmentRouteSegmentId " + shipmentRouteSegmentId);
+            }
+            String destinationContactPhoneNumber = destinationTelecomNumber.getString("areaCode") + destinationTelecomNumber.getString("contactNumber");
+
+            // Fedex doesn't want the North American country code
+            if (UtilValidate.isNotEmpty(destinationTelecomNumber.getString("countryCode")) && !(destinationAddressCountryCode.equals("CA") || destinationAddressCountryCode.equals("US"))) {
+                destinationContactPhoneNumber = destinationTelecomNumber.getString("countryCode") + destinationContactPhoneNumber;
+            }
+            destinationContactPhoneNumber = destinationContactPhoneNumber.replaceAll("[^+\\d]", "");
+            
+            // Get the destination contact name
+            String destinationPartyId = shipment.getString("partyIdTo");
+            if (UtilValidate.isEmpty(destinationPartyId)) {
+                return ServiceUtil.returnError("Shipment.partyIdTo is required for Fedex shipments: shipmentId " + shipmentId + ", shipmentRouteSegmentId " + shipmentRouteSegmentId);
+            }
+            GenericValue partyTo = delegator.findByPrimaryKey("Party", UtilMisc.toMap("partyId", destinationPartyId));
+            String destinationContactKey = "PERSON".equals(partyTo.getString("partyTypeId")) ? "DestinationContactPersonName" : "DestinationContactCompanyName";
+            String destinationContactName = PartyHelper.getPartyName(partyTo, false);
+            if (UtilValidate.isEmpty(destinationContactName)) {
+                return ServiceUtil.returnError("partyTo for shipmentId " + shipmentId + ", shipmentRouteSegmentId " + shipmentRouteSegmentId + " has no name (required for Fedex shipments)" );
+            }
+
+            String homeDeliveryType = null;
+            Timestamp homeDeliveryDate = null;
+            if ("GROUNDHOMEDELIVERY".equals(service)) {
+