KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > ofbiz > shipment > thirdparty > usps > UspsServices


1 /*
2  * $Id: UspsServices.java 5462 2005-08-05 18:35:48Z jonesde $
3  *
4  * Copyright (c) 2004 The Open For Business Project - www.ofbiz.org
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a
7  * copy of this software and associated documentation files (the "Software"),
8  * to deal in the Software without restriction, including without limitation
9  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10  * and/or sell copies of the Software, and to permit persons to whom the
11  * Software is furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included
14  * in all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
21  * OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
22  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23  */

24 package org.ofbiz.shipment.thirdparty.usps;
25
26 import java.io.ByteArrayOutputStream JavaDoc;
27 import java.io.FileOutputStream JavaDoc;
28 import java.io.IOException JavaDoc;
29 import java.io.OutputStream JavaDoc;
30 import java.text.DecimalFormat JavaDoc;
31 import java.util.ArrayList JavaDoc;
32 import java.util.HashMap JavaDoc;
33 import java.util.Iterator JavaDoc;
34 import java.util.LinkedList JavaDoc;
35 import java.util.List JavaDoc;
36 import java.util.ListIterator JavaDoc;
37 import java.util.Map JavaDoc;
38
39 import javax.xml.parsers.ParserConfigurationException JavaDoc;
40
41 import org.ofbiz.base.util.*;
42 import org.ofbiz.entity.GenericDelegator;
43 import org.ofbiz.entity.GenericEntityException;
44 import org.ofbiz.entity.GenericValue;
45 import org.ofbiz.entity.util.EntityUtil;
46 import org.ofbiz.product.store.ProductStoreWorker;
47 import org.ofbiz.service.DispatchContext;
48 import org.ofbiz.service.GenericServiceException;
49 import org.ofbiz.service.LocalDispatcher;
50 import org.ofbiz.service.ModelService;
51 import org.ofbiz.service.ServiceUtil;
52
53 import org.apache.xml.serialize.OutputFormat;
54 import org.apache.xml.serialize.XMLSerializer;
55 import org.w3c.dom.Document JavaDoc;
56 import org.w3c.dom.Element JavaDoc;
57 import org.xml.sax.SAXException JavaDoc;
58
59 /**
60  * USPS Webtools API Services
61  *
62  * @author <a HREF="mailto:eckardjf@pobox.com">J. Eckard</a>
63  * @version $Rev: 5462 $
64  * @since 3.2
65  */

66 public class UspsServices {
67
68     public final static String JavaDoc module = UspsServices.class.getName();
69
70     public static Map JavaDoc uspsRateInquire(DispatchContext dctx, Map JavaDoc context) {
71
72         GenericDelegator delegator = dctx.getDelegator();
73
74         // check for 0 weight
75
Double JavaDoc shippableWeight = (Double JavaDoc) context.get("shippableWeight");
76         if (shippableWeight.doubleValue() == 0) {
77             // TODO: should we return an error, or $0.00 ?
78
return ServiceUtil.returnError("shippableWeight must be greater than 0");
79         }
80
81         // get the origination ZIP
82
String JavaDoc originationZip = null;
83         GenericValue productStore = ProductStoreWorker.getProductStore(((String JavaDoc) context.get("productStoreId")), delegator);
84         if (productStore != null && productStore.get("inventoryFacilityId") != null) {
85             try {
86                 List JavaDoc shipLocs = delegator.findByAnd("FacilityContactMechPurpose",
87                         UtilMisc.toMap("facilityId", productStore.getString("inventoryFacilityId"),
88                                 "contactMechPurposeTypeId", "SHIP_ORIG_LOCATION"), UtilMisc.toList("-fromDate"));
89                 if (UtilValidate.isNotEmpty(shipLocs)) {
90                     shipLocs = EntityUtil.filterByDate(shipLocs);
91                     GenericValue purp = EntityUtil.getFirst(shipLocs);
92                     if (purp != null) {
93                         GenericValue shipFromAddress = delegator.findByPrimaryKey("PostalAddress",
94                                 UtilMisc.toMap("contactMechId", purp.getString("contactMechId")));
95                         if (shipFromAddress != null) {
96                             originationZip = shipFromAddress.getString("postalCode");
97                         }
98                     }
99                 }
100             } catch (GenericEntityException e) {
101                 Debug.logError(e, module);
102             }
103         }
104         if (UtilValidate.isEmpty(originationZip)) {
105             return ServiceUtil.returnError("Unable to determine the origination ZIP");
106         }
107
108         // get the destination ZIP
109
String JavaDoc destinationZip = null;
110         String JavaDoc shippingContactMechId = (String JavaDoc) context.get("shippingContactMechId");
111         if (UtilValidate.isNotEmpty(shippingContactMechId)) {
112             try {
113                 GenericValue shipToAddress = delegator.findByPrimaryKey("PostalAddress", UtilMisc.toMap("contactMechId", shippingContactMechId));
114                 if (shipToAddress != null) {
115                     destinationZip = shipToAddress.getString("postalCode");
116                 }
117             } catch (GenericEntityException e) {
118                 Debug.logError(e, module);
119             }
120         }
121         if (UtilValidate.isEmpty(destinationZip)) {
122             return ServiceUtil.returnError("Unable to determine the destination ZIP");
123         }
124
125         // get the service code
126
String JavaDoc serviceCode = null;
127         try {
128             GenericValue carrierShipmentMethod = delegator.findByPrimaryKey("CarrierShipmentMethod",
129                     UtilMisc.toMap("shipmentMethodTypeId", (String JavaDoc) context.get("shipmentMethodTypeId"),
130                             "partyId", (String JavaDoc) context.get("carrierPartyId"), "roleTypeId", (String JavaDoc) context.get("carrierRoleTypeId")));
131             if (carrierShipmentMethod != null) {
132                 serviceCode = carrierShipmentMethod.getString("carrierServiceCode");
133             }
134         } catch (GenericEntityException e) {
135             Debug.logError(e, module);
136         }
137         if (UtilValidate.isEmpty(serviceCode)) {
138             return ServiceUtil.returnError("Unable to determine the service code");
139         }
140
141         // create the request document
142
Document JavaDoc requestDocument = createUspsRequestDocument("RateRequest");
143
144         // TODO: 70 lb max is valid for Express, Priority and Parcel only - handle other methods
145
double maxWeight = 70;
146         String JavaDoc maxWeightStr = UtilProperties.getPropertyValue((String JavaDoc) context.get("serviceConfigProps"),
147                 "shipment.usps.max.estimate.weight", "70");
148         try {
149             maxWeight = Double.parseDouble(maxWeightStr);
150         } catch (NumberFormatException JavaDoc e) {
151             Debug.logWarning("Error parsing max estimate weight string [" + maxWeightStr + "], using default instead", module);
152             maxWeight = 70;
153         }
154
155         List JavaDoc shippableItemInfo = (List JavaDoc) context.get("shippableItemInfo");
156         List JavaDoc packages = getPackageSplit(shippableItemInfo, maxWeight);
157         // TODO: Up to 25 packages can be included per request - handle more than 25
158
for (ListIterator JavaDoc li = packages.listIterator(); li.hasNext();) {
159             Map JavaDoc packageMap = (Map JavaDoc) li.next();
160
161             double packageWeight = calcPackageWeight(packageMap, shippableItemInfo, 0);
162             if (packageWeight == 0) {
163                 continue;
164             }
165
166             Element JavaDoc packageElement = UtilXml.addChildElement(requestDocument.getDocumentElement(), "Package", requestDocument);
167             packageElement.setAttribute("ID", String.valueOf(li.nextIndex() - 1)); // use zero-based index (see examples)
168

169             UtilXml.addChildElementValue(packageElement, "Service", serviceCode, requestDocument);
170             UtilXml.addChildElementValue(packageElement, "ZipOrigination", originationZip, requestDocument);
171             UtilXml.addChildElementValue(packageElement, "ZipDestination", destinationZip, requestDocument);
172
173             double weightPounds = Math.floor(packageWeight);
174             UtilXml.addChildElementValue(packageElement, "Pounds", String.valueOf(weightPounds), requestDocument);
175
176             double weightOunces = Math.ceil(packageWeight % weightPounds * 16);
177             UtilXml.addChildElementValue(packageElement, "Ounces", String.valueOf(weightOunces), requestDocument);
178
179             // TODO: handle other container types, package sizes, and machinabile packages
180
UtilXml.addChildElementValue(packageElement, "Container", "None", requestDocument);
181             UtilXml.addChildElementValue(packageElement, "Size", "Regular", requestDocument);
182             UtilXml.addChildElementValue(packageElement, "Machinable", "False", requestDocument);
183         }
184
185         // send the request
186
Document JavaDoc responseDocument = null;
187         try {
188             responseDocument = sendUspsRequest("Rate", requestDocument);
189         } catch (UspsRequestException e) {
190             Debug.log(e, module);
191             return ServiceUtil.returnError("Error sending request for USPS Domestic Rate Calculation service: " + e.getMessage());
192         }
193
194         List JavaDoc rates = UtilXml.childElementList(responseDocument.getDocumentElement(), "Package");
195         if (UtilValidate.isEmpty(rates)) {
196             return ServiceUtil.returnError("No rate available at this time");
197         }
198
199         double estimateAmount = 0.00;
200         for (Iterator JavaDoc i = rates.iterator(); i.hasNext();) {
201             Element JavaDoc packageElement = (Element JavaDoc) i.next();
202             try {
203                 double packageAmount = Double.parseDouble(UtilXml.childElementValue(packageElement, "Postage"));
204                 estimateAmount += packageAmount;
205             } catch (NumberFormatException JavaDoc e) {
206                 Debug.log(e, module);
207             }
208         }
209
210         Map JavaDoc result = ServiceUtil.returnSuccess();
211         result.put("shippingEstimateAmount", new Double JavaDoc(estimateAmount));
212         return result;
213     }
214
215     // lifted from UpsServices with no changes - 2004.09.06 JFE
216
private static List JavaDoc getPackageSplit(List JavaDoc shippableItemInfo, double maxWeight) {
217         // create the package list w/ the first pacakge
218
List JavaDoc packages = new LinkedList JavaDoc();
219
220         if (shippableItemInfo != null) {
221             Iterator JavaDoc sii = shippableItemInfo.iterator();
222             while (sii.hasNext()) {
223                 Map JavaDoc itemInfo = (Map JavaDoc) sii.next();
224                 long pieces = ((Long JavaDoc) itemInfo.get("piecesIncluded")).longValue();
225                 double totalQuantity = ((Double JavaDoc) itemInfo.get("quantity")).doubleValue();
226                 double totalWeight = ((Double JavaDoc) itemInfo.get("weight")).doubleValue();
227                 String JavaDoc productId = (String JavaDoc) itemInfo.get("productId");
228
229                 // sanity check
230
if (pieces < 1) {
231                     pieces = 1; // can NEVER be less than one
232
}
233                 double weight = totalWeight / pieces;
234
235                 for (int z = 1; z <= totalQuantity; z++) {
236                     double partialQty = pieces > 1 ? 1.000 / pieces : 1;
237                     for (long x = 0; x < pieces; x++) {
238                         if (weight >= maxWeight) {
239                             Map JavaDoc newPackage = new HashMap JavaDoc();
240                             newPackage.put(productId, new Double JavaDoc(partialQty));
241                             packages.add(newPackage);
242                         } else if (totalWeight > 0) {
243                             // create the first package
244
if (packages.size() == 0) {
245                                 packages.add(new HashMap JavaDoc());
246                             }
247
248                             // package loop
249
int packageSize = packages.size();
250                             boolean addedToPackage = false;
251                             for (int pi = 0; pi < packageSize; pi++) {
252                                 if (!addedToPackage) {
253                                     Map JavaDoc packageMap = (Map JavaDoc) packages.get(pi);
254                                     double packageWeight = calcPackageWeight(packageMap, shippableItemInfo, weight);
255                                     if (packageWeight <= maxWeight) {
256                                         Double JavaDoc qtyD = (Double JavaDoc) packageMap.get(productId);
257                                         double qty = qtyD == null ? 0 : qtyD.doubleValue();
258                                         packageMap.put(productId, new Double JavaDoc(qty + partialQty));
259                                         addedToPackage = true;
260                                     }
261                                 }
262                             }
263                             if (!addedToPackage) {
264                                 Map JavaDoc packageMap = new HashMap JavaDoc();
265                                 packageMap.put(productId, new Double JavaDoc(partialQty));
266                                 packages.add(packageMap);
267                             }
268                         }
269                     }
270                 }
271             }
272         }
273         return packages;
274     }
275
276     // lifted from UpsServices with no changes - 2004.09.06 JFE
277
private static double calcPackageWeight(Map JavaDoc packageMap, List JavaDoc shippableItemInfo, double additionalWeight) {
278         double totalWeight = 0.00;
279         Iterator JavaDoc i = packageMap.keySet().iterator();
280         while (i.hasNext()) {
281             String JavaDoc productId = (String JavaDoc) i.next();
282             Map JavaDoc productInfo = getProductItemInfo(shippableItemInfo, productId);
283             double productWeight = ((Double JavaDoc) productInfo.get("weight")).doubleValue();
284             double quantity = ((Double JavaDoc) packageMap.get(productId)).doubleValue();
285             totalWeight += (productWeight * quantity);
286         }
287         return totalWeight + additionalWeight;
288     }
289
290     // lifted from UpsServices with no changes - 2004.09.06 JFE
291
private static Map JavaDoc getProductItemInfo(List JavaDoc shippableItemInfo, String JavaDoc productId) {
292         if (shippableItemInfo != null) {
293             Iterator JavaDoc i = shippableItemInfo.iterator();
294             while (i.hasNext()) {
295                 Map JavaDoc testMap = (Map JavaDoc) i.next();
296                 String JavaDoc id = (String JavaDoc) testMap.get("productId");
297                 if (productId.equals(id)) {
298                     return testMap;
299                 }
300             }
301         }
302         return null;
303     }
304
305     /*
306
307     Track/Confirm Samples: (API=TrackV2)
308
309     Request:
310     <TrackRequest USERID="xxxxxxxx" PASSWORD="xxxxxxxx">
311         <TrackID ID="EJ958083578US"></TrackID>
312     </TrackRequest>
313
314     Response:
315     <TrackResponse>
316         <TrackInfo ID="EJ958083578US">
317             <TrackSummary>Your item was delivered at 8:10 am on June 1 in Wilmington DE 19801.</TrackSummary>
318             <TrackDetail>May 30 11:07 am NOTICE LEFT WILMINGTON DE 19801.</TrackDetail>
319             <TrackDetail>May 30 10:08 am ARRIVAL AT UNIT WILMINGTON DE 19850.</TrackDetail>
320             <TrackDetail>May 29 9:55 am ACCEPT OR PICKUP EDGEWATER NJ 07020.</TrackDetail>
321         </TrackInfo>
322     </TrackResponse>
323
324     */

325
326     public static Map JavaDoc uspsTrackConfirm(DispatchContext dctx, Map JavaDoc context) {
327
328         Document JavaDoc requestDocument = createUspsRequestDocument("TrackRequest");
329
330         Element JavaDoc trackingElement = UtilXml.addChildElement(requestDocument.getDocumentElement(), "TrackID", requestDocument);
331         trackingElement.setAttribute("ID", (String JavaDoc) context.get("trackingId"));
332
333         Document JavaDoc responseDocument = null;
334         try {
335             responseDocument = sendUspsRequest("TrackV2", requestDocument);
336         } catch (UspsRequestException e) {
337             Debug.log(e, module);
338             return ServiceUtil.returnError("Error sending request for USPS Tracking service: " + e.getMessage());
339         }
340
341         Element JavaDoc trackInfoElement = UtilXml.firstChildElement(responseDocument.getDocumentElement(), "TrackInfo");
342         if (trackInfoElement == null) {
343             return ServiceUtil.returnError("Incomplete response from USPS Tracking service: no TrackInfo element found");
344         }
345
346         Map JavaDoc result = ServiceUtil.returnSuccess();
347
348         result.put("trackingSummary", UtilXml.childElementValue(trackInfoElement, "TrackSummary"));
349
350         List JavaDoc detailElementList = UtilXml.childElementList(trackInfoElement, "TrackDetail");
351         if (UtilValidate.isNotEmpty(detailElementList)) {
352             List JavaDoc trackingDetailList = new ArrayList JavaDoc();
353             for (Iterator JavaDoc iter = detailElementList.iterator(); iter.hasNext();) {
354                 trackingDetailList.add(UtilXml.elementValue((Element JavaDoc) iter.next()));
355             }
356             result.put("trackingDetailList", trackingDetailList);
357         }
358
359         return result;
360     }
361
362     /*
363
364     Address Standardization Samples: (API=Verify)
365
366     Request:
367     <AddressValidateRequest USERID="xxxxxxx" PASSWORD="xxxxxxx">
368         <Address ID="0">
369             <Address1></Address1>
370             <Address2>6406 Ivy Lane</Address2>
371             <City>Greenbelt</City>
372             <State>MD</State>
373             <Zip5></Zip5>
374             <Zip4></Zip4>
375         </Address>
376     </AddressValidateRequest>
377
378     Response:
379     <AddressValidateResponse>
380         <Address ID="0">
381             <Address2>6406 IVY LN</Address2>
382             <City>GREENBELT</City>
383             <State>MD</State>
384             <Zip5>20770</Zip5>
385             <Zip4>1440</Zip4>
386         </Address>
387     </AddressValidateResponse>
388
389     Note:
390         The service parameters address1 and addess2 follow the OFBiz naming convention,
391         and are converted to USPS conventions internally
392         (OFBiz address1 = USPS address2, OFBiz address2 = USPS address1)
393
394     */

395
396     public static Map JavaDoc uspsAddressValidation(DispatchContext dctx, Map JavaDoc context) {
397
398         Document JavaDoc requestDocument = createUspsRequestDocument("AddressValidateRequest");
399
400         Element JavaDoc addressElement = UtilXml.addChildElement(requestDocument.getDocumentElement(), "Address", requestDocument);
401         addressElement.setAttribute("ID", "0");
402
403         // 38 chars max
404
UtilXml.addChildElementValue(addressElement, "FirmName", (String JavaDoc) context.get("firmName"), requestDocument);
405         // 38 chars max
406
UtilXml.addChildElementValue(addressElement, "Address1", (String JavaDoc) context.get("address2"), requestDocument);
407         // 38 chars max
408
UtilXml.addChildElementValue(addressElement, "Address2", (String JavaDoc) context.get("address1"), requestDocument);
409         // 15 chars max
410
UtilXml.addChildElementValue(addressElement, "City", (String JavaDoc) context.get("city"), requestDocument);
411
412         UtilXml.addChildElementValue(addressElement, "State", (String JavaDoc) context.get("state"), requestDocument);
413         UtilXml.addChildElementValue(addressElement, "Zip5", (String JavaDoc) context.get("zip5"), requestDocument);
414         UtilXml.addChildElementValue(addressElement, "Zip4", (String JavaDoc) context.get("zip4"), requestDocument);
415
416         Document JavaDoc responseDocument = null;
417         try {
418             responseDocument = sendUspsRequest("Verify", requestDocument);
419         } catch (UspsRequestException e) {
420             Debug.log(e, module);
421             return ServiceUtil.returnError("Error sending request for USPS Address Validation service: " + e.getMessage());
422         }
423
424         Element JavaDoc respAddressElement = UtilXml.firstChildElement(responseDocument.getDocumentElement(), "Address");
425         if (respAddressElement == null) {
426             return ServiceUtil.returnError("Incomplete response from USPS Address Validation service: no Address element found");
427         }
428
429         Element JavaDoc respErrorElement = UtilXml.firstChildElement(respAddressElement, "Error");
430         if (respErrorElement != null) {
431             return ServiceUtil.returnError("The following error was returned by the USPS Address Validation service: " +
432                     UtilXml.childElementValue(respErrorElement, "Description"));
433         }
434
435         Map JavaDoc result = ServiceUtil.returnSuccess();
436
437         // Note: a FirmName element is not returned if empty
438
String JavaDoc firmName = UtilXml.childElementValue(respAddressElement, "FirmName");
439         if (UtilValidate.isNotEmpty(firmName)) {
440             result.put("firmName", firmName);
441         }
442
443         // Note: an Address1 element is not returned if empty
444
String JavaDoc address1 = UtilXml.childElementValue(respAddressElement, "Address1");
445         if (UtilValidate.isNotEmpty(address1)) {
446             result.put("address2", address1);
447         }
448
449         result.put("address1", UtilXml.childElementValue(respAddressElement, "Address2"));
450         result.put("city", UtilXml.childElementValue(respAddressElement, "City"));
451         result.put("state", UtilXml.childElementValue(respAddressElement, "State"));
452         result.put("zip5", UtilXml.childElementValue(respAddressElement, "Zip5"));
453         result.put("zip4", UtilXml.childElementValue(respAddressElement, "Zip4"));
454
455         return result;
456     }
457
458     /*
459
460     City/State Lookup Samples: (API=CityStateLookup)
461
462     Request:
463     <CityStateLookupRequest USERID="xxxxxxx" PASSWORD="xxxxxxx">
464         <ZipCode ID="0">
465             <Zip5>90210</Zip5>
466         </ZipCode>
467     </CityStateLookupRequest>
468
469     Response:
470     <CityStateLookupResponse>
471         <ZipCode ID="0">
472             <Zip5>90210</Zip5>
473             <City>BEVERLY HILLS</City>
474             <State>CA</State>
475         </ZipCode>
476     </CityStateLookupResponse>
477
478     */

479
480     public static Map JavaDoc uspsCityStateLookup(DispatchContext dctx, Map JavaDoc context) {
481
482         Document JavaDoc requestDocument = createUspsRequestDocument("CityStateLookupRequest");
483
484         Element JavaDoc zipCodeElement = UtilXml.addChildElement(requestDocument.getDocumentElement(), "ZipCode", requestDocument);
485         zipCodeElement.setAttribute("ID", "0");
486
487         String JavaDoc zipCode = ((String JavaDoc) context.get("zip5")).trim(); // trim leading/trailing spaces
488

489         // only the first 5 digits are used, the rest are ignored
490
UtilXml.addChildElementValue(zipCodeElement, "Zip5", zipCode, requestDocument);
491
492         Document JavaDoc responseDocument = null;
493         try {
494             responseDocument = sendUspsRequest("CityStateLookup", requestDocument);
495         } catch (UspsRequestException e) {
496             Debug.log(e, module);
497             return ServiceUtil.returnError("Error sending request for USPS City/State Lookup service: " + e.getMessage());
498         }
499
500         Element JavaDoc respAddressElement = UtilXml.firstChildElement(responseDocument.getDocumentElement(), "ZipCode");
501         if (respAddressElement == null) {
502             return ServiceUtil.returnError("Incomplete response from USPS City/State Lookup service: no ZipCode element found");
503         }
504
505         Element JavaDoc respErrorElement = UtilXml.firstChildElement(respAddressElement, "Error");
506         if (respErrorElement != null) {
507             return ServiceUtil.returnError("The following error was returned by the USPS City/State Lookup service: " +
508                     UtilXml.childElementValue(respErrorElement, "Description"));
509         }
510
511         Map JavaDoc result = ServiceUtil.returnSuccess();
512
513         String JavaDoc city = UtilXml.childElementValue(respAddressElement, "City");
514         if (UtilValidate.isEmpty(city)) {
515             return ServiceUtil.returnError("Incomplete response from USPS City/State Lookup service: no City element found");
516         }
517         result.put("city", city);
518
519         String JavaDoc state = UtilXml.childElementValue(respAddressElement, "State");
520         if (UtilValidate.isEmpty(state)) {
521             return ServiceUtil.returnError("Incomplete response from USPS City/State Lookup service: no State element found");
522         }
523         result.put("state", state);
524
525         return result;
526     }
527
528     /*
529
530     Service Standards Samples:
531
532     Priority Mail: (API=PriorityMail)
533
534         Request:
535         <PriorityMailRequest USERID="xxxxxxx" PASSWORD="xxxxxxx">
536             <OriginZip>4</OriginZip>
537             <DestinationZip>4</DestinationZip>
538         </PriorityMailRequest>
539
540         Response:
541         <PriorityMailResponse>
542             <OriginZip>4</OriginZip>
543             <DestinationZip>4</DestinationZip>
544             <Days>1</Days>
545         </PriorityMailResponse>
546
547     Package Services: (API=StandardB)
548
549         Request:
550         <StandardBRequest USERID="xxxxxxx" PASSWORD="xxxxxxx">
551             <OriginZip>4</OriginZip>
552             <DestinationZip>4</DestinationZip>
553         </StandardBRequest>
554
555         Response:
556         <StandardBResponse>
557             <OriginZip>4</OriginZip>
558             <DestinationZip>4</DestinationZip>
559             <Days>2</Days>
560         </StandardBResponse>
561
562     Note:
563         When submitting ZIP codes, only the first 3 digits are used.
564         If a 1- or 2-digit ZIP code is entered, leading zeros are implied.
565         If a 4- or 5-digit ZIP code is entered, the last digits will be ignored.
566
567     */

568
569     public static Map JavaDoc uspsPriorityMailStandard(DispatchContext dctx, Map JavaDoc context) {
570         context.put("serviceType", "PriorityMail");
571         return uspsServiceStandards(dctx, context);
572     }
573
574     public static Map JavaDoc uspsPackageServicesStandard(DispatchContext dctx, Map JavaDoc context) {
575         context.put("serviceType", "StandardB");
576         return uspsServiceStandards(dctx, context);
577     }
578
579     private static Map JavaDoc uspsServiceStandards(DispatchContext dctx, Map JavaDoc context) {
580
581         String JavaDoc type = (String JavaDoc) context.get("serviceType");
582         if (!type.matches("PriorityMail|StandardB")) {
583             return ServiceUtil.returnError("Unsupported service type: " + type);
584         }
585
586         Document JavaDoc requestDocument = createUspsRequestDocument(type + "Request");
587
588         UtilXml.addChildElementValue(requestDocument.getDocumentElement(), "OriginZip",
589                 (String JavaDoc) context.get("originZip"), requestDocument);
590         UtilXml.addChildElementValue(requestDocument.getDocumentElement(), "DestinationZip",
591                 (String JavaDoc) context.get("destinationZip"), requestDocument);
592
593         Document JavaDoc responseDocument = null;
594         try {
595             responseDocument = sendUspsRequest(type, requestDocument);
596         } catch (UspsRequestException e) {
597             Debug.log(e, module);
598             return ServiceUtil.returnError("Error sending request for USPS " + type + " Service Standards service: " +
599                     e.getMessage());
600         }
601
602         Map JavaDoc result = ServiceUtil.returnSuccess();
603
604         String JavaDoc days = UtilXml.childElementValue(responseDocument.getDocumentElement(), "Days");
605         if (UtilValidate.isEmpty(days)) {
606             return ServiceUtil.returnError("Incomplete response from USPS " + type + " Service Standards service: " +
607                     "no Days element found");
608         }
609         result.put("days", days);
610
611         return result;
612     }
613
614
615
616     /*
617
618     Domestic Rate Calculator Samples: (API=Rate)
619
620     Request:
621     <RateRequest USERID="xxxxxx" PASSWORD="xxxxxxx">
622         <Package ID="0">
623             <Service>Priority</Service>
624             <ZipOrigination>20770</ZipOrigination>
625             <ZipDestination>09021</ZipDestination>
626             <Pounds>5</Pounds>
627             <Ounces>1</Ounces>
628             <Container>None</Container>
629             <Size>Regular</Size>
630             <Machinable>False</Machinable>
631         </Package>
632     </RateRequest>
633
634     Response:
635     <RateResponse>
636         <Package ID="0">
637             <Service>Priority</Service>
638             <ZipOrigination>20770</ZipOrigination>
639             <ZipDestination>09021</ZipDestination>
640             <Pounds>5</Pounds>
641             <Ounces>1</Ounces>
642             <Container>None</Container>
643             <Size>REGULAR</Size>
644             <Machinable>FALSE</Machinable>
645             <Zone>3</Zone>
646             <Postage>7.90</Postage>
647             <RestrictionCodes>B-B1-C-D-U</RestrictionCodes>
648             <RestrictionDescription>
649             B. Form 2976-A is required for all mail weighing 16 ounces or more, with exceptions noted below.
650             In addition, mailers must properly complete required customs documentation when mailing any potentially
651             dutiable mail addressed to an APO or FPO regardless of weight. B1. Form 2976 or 2976-A is required.
652             Articles are liable for customs duty and/or purchase tax unless they are bona fide gifts intended for
653             use by military personnel or their dependents. C. Cigarettes and other tobacco products are prohibited.
654             D. Coffee is prohibited. U. Parcels must weigh less than 16 ounces when addressed to Box R.
655             </RestrictionDescription>
656         </Package>
657     </RateResponse>
658
659     */

660
661     public static Map JavaDoc uspsDomesticRate(DispatchContext dctx, Map JavaDoc context) {
662
663         Document JavaDoc requestDocument = createUspsRequestDocument("RateRequest");
664
665         Element JavaDoc packageElement = UtilXml.addChildElement(requestDocument.getDocumentElement(), "Package", requestDocument);
666         packageElement.setAttribute("ID", "0");
667
668         UtilXml.addChildElementValue(packageElement, "Service", (String JavaDoc) context.get("service"), requestDocument);
669         UtilXml.addChildElementValue(packageElement, "ZipOrigination", (String JavaDoc) context.get("originZip"), requestDocument);
670         UtilXml.addChildElementValue(packageElement, "ZipDestination", (String JavaDoc) context.get("destinationZip"), requestDocument);
671         UtilXml.addChildElementValue(packageElement, "Pounds", (String JavaDoc) context.get("pounds"), requestDocument);
672         UtilXml.addChildElementValue(packageElement, "Ounces", (String JavaDoc) context.get("ounces"), requestDocument);
673
674         String JavaDoc container = (String JavaDoc) context.get("container");
675         if (UtilValidate.isEmpty(container)) {
676             container = "None";
677         }
678         UtilXml.addChildElementValue(packageElement, "Container", container, requestDocument);
679
680         String JavaDoc size = (String JavaDoc) context.get("size");
681         if (UtilValidate.isEmpty(size)) {
682             size = "Regular";
683         }
684         UtilXml.addChildElementValue(packageElement, "Size", size, requestDocument);
685
686         String JavaDoc machinable = (String JavaDoc) context.get("machinable");
687         if (UtilValidate.isEmpty(machinable)) {
688             machinable = "False";
689         }
690         UtilXml.addChildElementValue(packageElement, "Machinable", machinable, requestDocument);
691
692         Document JavaDoc responseDocument = null;
693         try {
694             responseDocument = sendUspsRequest("Rate", requestDocument);
695         } catch (UspsRequestException e) {
696             Debug.log(e, module);
697             return ServiceUtil.returnError("Error sending request for USPS Domestic Rate Calculation service: " + e.getMessage());
698         }
699
700         Element JavaDoc respPackageElement = UtilXml.firstChildElement(responseDocument.getDocumentElement(), "Package");
701         if (respPackageElement == null) {
702             return ServiceUtil.returnError("Incomplete response from USPS Domestic Rate Calculation service: no Package element found");
703         }
704
705         Element JavaDoc respErrorElement = UtilXml.firstChildElement(respPackageElement, "Error");
706         if (respErrorElement != null) {
707             return ServiceUtil.returnError("The following error was returned by the USPS Domestic Rate Calculation service: " +
708                     UtilXml.childElementValue(respErrorElement, "Description"));
709         }
710
711         Map JavaDoc result = ServiceUtil.returnSuccess();
712
713         String JavaDoc zone = UtilXml.childElementValue(respPackageElement, "Zone");
714         if (UtilValidate.isEmpty(zone)) {
715             return ServiceUtil.returnError("Incomplete response from USPS Domestic Rate Calculation service: no Zone element found");
716         }
717         result.put("zone", zone);
718
719         String JavaDoc postage = UtilXml.childElementValue(respPackageElement, "Postage");
720         if (UtilValidate.isEmpty(postage)) {
721             return ServiceUtil.returnError("Incomplete response from USPS Domestic Rate Calculation service: no Postage element found");
722         }
723         result.put("postage", postage);
724
725         String JavaDoc restrictionCodes = UtilXml.childElementValue(respPackageElement, "RestrictionCodes");
726         if (UtilValidate.isNotEmpty(restrictionCodes)) {
727             result.put("restrictionCodes", restrictionCodes);
728         }
729
730         String JavaDoc restrictionDesc = UtilXml.childElementValue(respPackageElement, "RestrictionDescription");
731         if (UtilValidate.isNotEmpty(restrictionCodes)) {
732             result.put("restrictionDesc", restrictionDesc);
733         }
734
735         return result;
736     }
737
738     // Warning: I don't think the following 2 services were completed or fully tested - 2004.09.06 JFE
739

740     /* --- ShipmentRouteSegment services --------------------------------------------------------------------------- */
741
742     public static Map JavaDoc uspsUpdateShipmentRateInfo(DispatchContext dctx, Map JavaDoc context) {
743
744         GenericDelegator delegator = dctx.getDelegator();
745         LocalDispatcher dispatcher = dctx.getDispatcher();
746
747         String JavaDoc shipmentId = (String JavaDoc) context.get("shipmentId");
748         String JavaDoc shipmentRouteSegmentId = (String JavaDoc) context.get("shipmentRouteSegmentId");
749
750         // ShipmentRouteSegment identifier - used in error messages
751
String JavaDoc srsKeyString = "[" + shipmentId + "," + shipmentRouteSegmentId + "]";
752
753         try {
754             GenericValue shipmentRouteSegment = delegator.findByPrimaryKey("ShipmentRouteSegment",
755                     UtilMisc.toMap("shipmentId", shipmentId, "shipmentRouteSegmentId", shipmentRouteSegmentId));
756             if (shipmentRouteSegment == null) {
757                 return ServiceUtil.returnError("ShipmentRouteSegment " + srsKeyString + " not found");
758             }
759
760             // ensure the carrier is USPS
761
if (!"USPS".equals(shipmentRouteSegment.getString("carrierPartyId"))) {
762                 return ServiceUtil.returnError("The Carrier for ShipmentRouteSegment " + srsKeyString + ", is not USPS");
763             }
764
765             // get the origin address
766
GenericValue originAddress = shipmentRouteSegment.getRelatedOne("OriginPostalAddress");
767             if (originAddress == null) {
768                 return ServiceUtil.returnError("OriginPostalAddress not found for ShipmentRouteSegment [" +
769                         shipmentId + ":" + shipmentRouteSegmentId + "]");
770             }
771             if (!"USA".equals(originAddress.getString("countryGeoId"))) {
772                 return ServiceUtil.returnError("ShipmentRouteSeqment " + srsKeyString + " does not originate from a US address");
773             }
774             String JavaDoc originZip = originAddress.getString("postalCode");
775             if (UtilValidate.isEmpty(originZip)) {
776                 return ServiceUtil.returnError("ZIP code is missing from the origin postal address" +
777                         " (contactMechId " + originAddress.getString("contactMechId") + ")");
778             }
779
780             // get the destination address
781
GenericValue destinationAddress = shipmentRouteSegment.getRelatedOne("DestPostalAddress");
782             if (destinationAddress == null) {
783                 return ServiceUtil.returnError("DestPostalAddress not found for ShipmentRouteSegment " + srsKeyString);
784             }
785             if (!"USA".equals(destinationAddress.getString("countryGeoId"))) {
786                 return ServiceUtil.returnError("ShipmentRouteSeqment " + srsKeyString + " is not destined for a US address");
787             }
788             String JavaDoc destinationZip = destinationAddress.getString("postalCode");
789             if (UtilValidate.isEmpty(destinationZip)) {
790                 return ServiceUtil.returnError("ZIP code is missing from the destination postal address" +
791                         " (contactMechId " + originAddress.getString("contactMechId") + ")");
792             }
793
794             // get the service type from the CarrierShipmentMethod
795
String JavaDoc shipmentMethodTypeId = shipmentRouteSegment.getString("shipmentMethodTypeId");
796             String JavaDoc partyId = shipmentRouteSegment.getString("carrierPartyId");
797             String JavaDoc csmKeystring = "[" + shipmentMethodTypeId + "," + partyId + ",CARRIER]";
798
799             GenericValue carrierShipmentMethod = delegator.findByPrimaryKey("CarrierShipmentMethod",
800                     UtilMisc.toMap("partyId", partyId, "roleTypeId", "CARRIER", "shipmentMethodTypeId", shipmentMethodTypeId));
801             if (carrierShipmentMethod == null) {
802                 return ServiceUtil.returnError("CarrierShipmentMethod " + csmKeystring +
803                         " not found for ShipmentRouteSegment " + srsKeyString);
804             }
805             String JavaDoc serviceType = carrierShipmentMethod.getString("carrierServiceCode");
806             if (UtilValidate.isEmpty(serviceType)) {
807                 return ServiceUtil.returnError("carrierServiceCode not found for CarrierShipmentMethod" + csmKeystring);
808             }
809
810             // get the packages for this shipment route segment
811
List JavaDoc shipmentPackageRouteSegList = shipmentRouteSegment.getRelated("ShipmentPackageRouteSeg", null,
812                     UtilMisc.toList("+shipmentPackageSeqId"));
813             if (UtilValidate.isEmpty(shipmentPackageRouteSegList)) {
814                 return ServiceUtil.returnError("No packages found for ShipmentRouteSegment " + srsKeyString);
815             }
816
817             double actualTransportCost = 0;
818
819             String JavaDoc carrierDeliveryZone = null;
820             String JavaDoc carrierRestrictionCodes = null;
821             String JavaDoc carrierRestrictionDesc = null;
822
823             // send a new request for each package
824
for (Iterator JavaDoc i = shipmentPackageRouteSegList.iterator(); i.hasNext();) {
825
826                 GenericValue shipmentPackageRouteSeg = (GenericValue) i.next();
827                 String JavaDoc sprsKeyString = "[" + shipmentPackageRouteSeg.getString("shipmentId") + "," +
828                         shipmentPackageRouteSeg.getString("shipmentPackageSeqId") + "," +
829                         shipmentPackageRouteSeg.getString("shipmentRouteSegmentId") + "]";
830
831                 Document JavaDoc requestDocument = createUspsRequestDocument("RateRequest");
832
833                 Element JavaDoc packageElement = UtilXml.addChildElement(requestDocument.getDocumentElement(), "Package", requestDocument);
834                 packageElement.setAttribute("ID", "0");
835
836                 UtilXml.addChildElementValue(packageElement, "Service", serviceType, requestDocument);
837                 UtilXml.addChildElementValue(packageElement, "ZipOrigination", originZip, requestDocument);
838                 UtilXml.addChildElementValue(packageElement, "ZipDestination", destinationZip, requestDocument);
839
840                 GenericValue shipmentPackage = null;
841                 shipmentPackage = shipmentPackageRouteSeg.getRelatedOne("ShipmentPackage");
842
843                 String JavaDoc spKeyString = "[" + shipmentPackage.getString("shipmentId") + "," +
844                         shipmentPackage.getString("shipmentPackageSeqId") + "]";
845
846                 // weight elements - Pounds, Ounces
847
String JavaDoc weightStr = shipmentPackage.getString("weight");
848                 if (UtilValidate.isEmpty(weightStr)) {
849                     return ServiceUtil.returnError("weight not found for ShipmentPackage " + spKeyString);
850                 }
851
852                 double weight = 0;
853                 try {
854                     weight = Double.parseDouble(weightStr);
855                 } catch (NumberFormatException JavaDoc nfe) {
856                     nfe.printStackTrace(); // TODO: handle exception
857
}
858
859                 String JavaDoc weightUomId = shipmentPackage.getString("weightUomId");
860                 if (UtilValidate.isEmpty(weightUomId)) {
861                     weightUomId = "WT_lb"; // assume weight is in pounds
862
}
863                 if (!"WT_lb".equals(weightUomId)) {
864                     // attempt a conversion to pounds
865
Map JavaDoc result = new HashMap JavaDoc();
866                     try {
867                         result = dispatcher.runSync("convertUom", UtilMisc.toMap("uomId", weightUomId, "uomIdTo", "WT_lb", "originalValue", new Double JavaDoc(weight)));
868                     } catch (GenericServiceException ex) {
869                         return ServiceUtil.returnError(ex.getMessage());
870                     }
871                     
872                     if (result.get(ModelService.RESPONSE_MESSAGE).equals(ModelService.RESPOND_SUCCESS)) {
873                         weight *= ((Double JavaDoc) result.get("convertedValue")).doubleValue();
874                     } else {
875                         return ServiceUtil.returnError("Unsupported weightUom [" + weightUomId + "] for ShipmentPackage " +
876                                 spKeyString + ", could not find a conversion factor for WT_lb");
877                     }
878                     
879                 }
880
881                 double weightPounds = Math.floor(weight);
882                 double weightOunces = Math.ceil(weight % weightPounds * 16);
883
884                 DecimalFormat JavaDoc df = new DecimalFormat JavaDoc("#");
885                 UtilXml.addChildElementValue(packageElement, "Pounds", df.format(weightPounds), requestDocument);
886                 UtilXml.addChildElementValue(packageElement, "Ounces", df.format(weightOunces), requestDocument);
887
888                 // Container element
889
GenericValue carrierShipmentBoxType = null;
890                 List JavaDoc carrierShipmentBoxTypes = null;
891                 carrierShipmentBoxTypes = shipmentPackage.getRelated("CarrierShipmentBoxType",
892                         UtilMisc.toMap("partyId", "USPS"), null);
893
894                 if (carrierShipmentBoxTypes.size() > 0) {
895                     carrierShipmentBoxType = (GenericValue) carrierShipmentBoxTypes.get(0);
896                 }
897
898                 if (carrierShipmentBoxType != null &&
899                         UtilValidate.isNotEmpty(carrierShipmentBoxType.getString("packagingTypeCode"))) {
900                     UtilXml.addChildElementValue(packageElement, "Container",
901                             carrierShipmentBoxType.getString("packagingTypeCode"), requestDocument);
902                 } else {
903                     // default to "None", for customers using their own package
904
UtilXml.addChildElementValue(packageElement, "Container", "None", requestDocument);
905                 }
906
907                 // Size element
908
if (carrierShipmentBoxType != null && UtilValidate.isNotEmpty("oversizeCode")) {
909                     UtilXml.addChildElementValue(packageElement, "Size",
910                             carrierShipmentBoxType.getString("oversizeCode"), requestDocument);
911                 } else {
912                     // default to "Regular", length + girth measurement <= 84 inches
913
UtilXml.addChildElementValue(packageElement, "Size", "Regular", requestDocument);
914                 }
915
916                 // Although only applicable for Parcel Post, this tag is required for all requests
917
UtilXml.addChildElementValue(packageElement, "Machinable", "False", requestDocument);
918
919                 Document JavaDoc responseDocument = null;
920                 try {
921                     responseDocument = sendUspsRequest("Rate", requestDocument);
922                 } catch (UspsRequestException e) {
923                     Debug.log(e, module);
924                     return ServiceUtil.returnError("Error sending request for USPS Domestic Rate Calculation service: " +
925                             e.getMessage());
926                 }
927
928                 Element JavaDoc respPackageElement = UtilXml.firstChildElement(responseDocument.getDocumentElement(), "Package");
929                 if (respPackageElement == null) {
930                     return ServiceUtil.returnError("Incomplete response from USPS Domestic Rate Calculation service: " +
931                             "no Package element found");
932                 }
933
934                 Element JavaDoc respErrorElement = UtilXml.firstChildElement(respPackageElement, "Error");
935                 if (respErrorElement != null) {
936                     return ServiceUtil.returnError("The following error was returned by the USPS Domestic Rate Calculation " +
937                             "service for ShipmentPackage " + spKeyString + ": " +
938                             UtilXml.childElementValue(respErrorElement, "Description"));
939                 }
940
941                 // update the ShipmentPackageRouteSeg
942
String JavaDoc postageString = UtilXml.childElementValue(respPackageElement, "Postage");
943                 if (UtilValidate.isEmpty(postageString)) {
944                     return ServiceUtil.returnError("Incomplete response from USPS Domestic Rate Calculation service: " +
945                             "missing or empty Postage element");
946                 }
947
948                 double postage = 0;
949                 try {
950                     postage = Double.parseDouble(postageString);
951                 } catch (NumberFormatException JavaDoc nfe) {
952                     nfe.printStackTrace(); // TODO: handle exception
953
}
954                 actualTransportCost += postage;
955
956                 shipmentPackageRouteSeg.setString("packageTransportCost", postageString);
957                 shipmentPackageRouteSeg.store();
958
959                 // if this is the last package, get the zone and APO/FPO restrictions for the ShipmentRouteSegment
960
if (!i.hasNext()) {
961                     carrierDeliveryZone = UtilXml.childElementValue(respPackageElement, "Zone");
962                     carrierRestrictionCodes = UtilXml.childElementValue(respPackageElement, "RestrictionCodes");
963                     carrierRestrictionDesc = UtilXml.childElementValue(respPackageElement, "RestrictionDescription");
964                 }
965             }
966
967             // update the ShipmentRouteSegment
968
shipmentRouteSegment.set("carrierDeliveryZone", carrierDeliveryZone);
969             shipmentRouteSegment.set("carrierRestrictionCodes", carrierRestrictionCodes);
970             shipmentRouteSegment.set("carrierRestrictionDesc", carrierRestrictionDesc);
971             shipmentRouteSegment.setString("actualTransportCost", String.valueOf(actualTransportCost));
972             shipmentRouteSegment.store();
973
974         } catch (GenericEntityException gee) {
975             Debug.log(gee, module);
976             return ServiceUtil.returnError("Error reading or writing shipment data for the USPS " +
977                     "Domestic Rate Calculation service: " + gee.getMessage());
978         }
979
980         return ServiceUtil.returnSuccess();
981     }
982
983     /*
984
985     Delivery Confirmation Samples:
986
987     <DeliveryConfirmationV2.0Request USERID="xxxxxxx" PASSWORD="xxxxxxxx">
988         <Option>3</Option>
989         <ImageParameters></ImageParameters>
990         <FromName>John Smith</FromName>
991         <FromFirm>ABC Corp.</FromFirm>
992         <FromAddress1>Ste 4</FromAddress1>
993         <FromAddress2>6406 Ivy Lane</FromAddress2>
994         <FromCity>Greenbelt</FromCity>
995         <FromState>MD</FromState>
996         <FromZip5>20770</FromZip5>
997         <FromZip4>4354</FromZip4>
998         <ToName>Jane Smith</ToName>
999         <ToFirm>XYZ Corp.</ToFirm>
1000        <ToAddress1>Apt 303</ToAddress1>
1001        <ToAddress2>4411 Romlon Street</ToAddress2>
1002        <ToCity>Beltsville</ToCity>
1003        <ToState>MD</ToState>
1004        <ToZip5>20705</ToZip5>
1005        <ToZip4>5656</ToZip4>
1006        <WeightInOunces>22</WeightInOunces>
1007        <ServiceType>Parcel Post</ServiceType>
1008        <ImageType>TIF</ImageType>
1009    </DeliveryConfirmationV2.0Request>
1010
1011    <DeliveryConfirmationV2.0Response>
1012        <DeliveryConfirmationNumber>02805213907052510758</DeliveryConfirmationNumber>
1013        <DeliveryConfirmationLabel>(Base64 encoded data)</DeliveryConfirmationLabel>
1014    </DeliveryConfirmationV2.0Response>
1015
1016    */

1017
1018    public static Map JavaDoc uspsDeliveryConfirmation(DispatchContext dctx, Map JavaDoc context) {
1019
1020        GenericDelegator delegator = dctx.getDelegator();
1021
1022        String JavaDoc shipmentId = (String JavaDoc) context.get("shipmentId");
1023        String JavaDoc shipmentRouteSegmentId = (String JavaDoc) context.get("shipmentRouteSegmentId");
1024
1025        // ShipmentRouteSegment identifier - used in error messages
1026
String JavaDoc srsKeyString = "[" + shipmentId + "," + shipmentRouteSegmentId + "]";
1027
1028        try {
1029            GenericValue shipment = delegator.findByPrimaryKey("Shipment", UtilMisc.toMap("shipmentId", shipmentId));
1030            if (shipment == null) {
1031                return ServiceUtil.returnError("Shipment not found with ID " + shipmentId);
1032            }
1033
1034            GenericValue shipmentRouteSegment = delegator.findByPrimaryKey("ShipmentRouteSegment",
1035                    UtilMisc.toMap("shipmentId", shipmentId, "shipmentRouteSegmentId", shipmentRouteSegmentId));
1036            if (shipmentRouteSegment == null) {
1037                return ServiceUtil.returnError("ShipmentRouteSegment not found with shipmentId " + shipmentId +
1038                        " and shipmentRouteSegmentId " + shipmentRouteSegmentId);
1039            }
1040
1041            // ensure the carrier is USPS
1042
if (!"USPS".equals(shipmentRouteSegment.getString("carrierPartyId"))) {
1043                return ServiceUtil.returnError("The Carrier for ShipmentRouteSegment " + srsKeyString + ", is not USPS");
1044            }
1045
1046            // get the origin address
1047
GenericValue originAddress = shipmentRouteSegment.getRelatedOne("OriginPostalAddress");
1048            if (originAddress == null) {
1049                return ServiceUtil.returnError("OriginPostalAddress not found for ShipmentRouteSegment [" +
1050                        shipmentId + ":" + shipmentRouteSegmentId + "]");
1051            }
1052            if (!"USA".equals(originAddress.getString("countryGeoId"))) {
1053                return ServiceUtil.returnError("ShipmentRouteSeqment " + srsKeyString + " does not originate from a US address");
1054            }
1055
1056            // get the destination address
1057
GenericValue destinationAddress = shipmentRouteSegment.getRelatedOne("DestPostalAddress");
1058            if (destinationAddress == null) {
1059                return ServiceUtil.returnError("DestPostalAddress not found for ShipmentRouteSegment " + srsKeyString);
1060            }
1061            if (!"USA".equals(destinationAddress.getString("countryGeoId"))) {
1062                return ServiceUtil.returnError("ShipmentRouteSeqment " + srsKeyString + " is not destined for a US address");
1063            }
1064
1065            // get the service type from the CarrierShipmentMethod
1066
String JavaDoc shipmentMethodTypeId = shipmentRouteSegment.getString("shipmentMethodTypeId");
1067            String JavaDoc partyId = shipmentRouteSegment.getString("carrierPartyId");
1068
1069            String JavaDoc csmKeystring = "[" + shipmentMethodTypeId + "," + partyId + ",CARRIER]";
1070
1071            GenericValue carrierShipmentMethod = delegator.findByPrimaryKey("CarrierShipmentMethod",
1072                    UtilMisc.toMap("partyId", partyId, "roleTypeId", "CARRIER", "shipmentMethodTypeId", shipmentMethodTypeId));
1073            if (carrierShipmentMethod == null) {
1074                return ServiceUtil.returnError("CarrierShipmentMethod " + csmKeystring +
1075                        " not found for ShipmentRouteSegment " + srsKeyString);
1076            }
1077            String JavaDoc serviceType = carrierShipmentMethod.getString("carrierServiceCode");
1078            if (UtilValidate.isEmpty(serviceType)) {
1079                return ServiceUtil.returnError("carrierServiceCode not found for CarrierShipmentMethod" + csmKeystring);
1080            }
1081
1082            // get the packages for this shipment route segment
1083
List JavaDoc shipmentPackageRouteSegList = shipmentRouteSegment.getRelated("ShipmentPackageRouteSeg", null,
1084                    UtilMisc.toList("+shipmentPackageSeqId"));
1085            if (UtilValidate.isEmpty(shipmentPackageRouteSegList)) {
1086                return ServiceUtil.returnError("No packages found for ShipmentRouteSegment " + srsKeyString);
1087            }
1088
1089            for (Iterator JavaDoc i = shipmentPackageRouteSegList.iterator(); i.hasNext();) {
1090
1091                Document JavaDoc requestDocument = createUspsRequestDocument("DeliveryConfirmationV2.0Request");
1092                Element JavaDoc requestElement = requestDocument.getDocumentElement();
1093
1094                UtilXml.addChildElementValue(requestElement, "Option", "3", requestDocument);
1095                UtilXml.addChildElement(requestElement, "ImageParameters", requestDocument);
1096
1097                // From address
1098
if (UtilValidate.isNotEmpty(originAddress.getString("attnName"))) {
1099                    UtilXml.addChildElementValue(requestElement, "FromName", originAddress.getString("attnName"), requestDocument);
1100                    UtilXml.addChildElementValue(requestElement, "FromFirm", originAddress.getString("toName"), requestDocument);
1101                } else {
1102                    UtilXml.addChildElementValue(requestElement, "FromName", originAddress.getString("toName"), requestDocument);
1103                }
1104                // The following 2 assignments are not typos - USPS address1 = OFBiz address2, USPS address2 = OFBiz address1
1105
UtilXml.addChildElementValue(requestElement, "FromAddress1", originAddress.getString("address2"), requestDocument);
1106                UtilXml.addChildElementValue(requestElement, "FromAddress2", originAddress.getString("address1"), requestDocument);
1107                UtilXml.addChildElementValue(requestElement, "FromCity", originAddress.getString("city"), requestDocument);
1108                UtilXml.addChildElementValue(requestElement, "FromState", originAddress.getString("stateProvinceGeoId"), requestDocument);
1109                UtilXml.addChildElementValue(requestElement, "FromZip5", originAddress.getString("postalCode"), requestDocument);
1110                UtilXml.addChildElement(requestElement, "FromZip4", requestDocument);
1111
1112                // To address
1113
if (UtilValidate.isNotEmpty(destinationAddress.getString("attnName"))) {
1114                    UtilXml.addChildElementValue(requestElement, "ToName", destinationAddress.getString("attnName"), requestDocument);
1115                    UtilXml.addChildElementValue(requestElement, "ToFirm", destinationAddress.getString("toName"), requestDocument);
1116                } else {
1117                    UtilXml.addChildElementValue(requestElement, "ToName", destinationAddress.getString("toName"), requestDocument);
1118                }
1119                // The following 2 assignments are not typos - USPS address1 = OFBiz address2, USPS address2 = OFBiz address1
1120
UtilXml.addChildElementValue(requestElement, "ToAddress1", destinationAddress.getString("address2"), requestDocument);
1121                UtilXml.addChildElementValue(requestElement, "ToAddress2", destinationAddress.getString("address1"), requestDocument);
1122                UtilXml.addChildElementValue(requestElement, "ToCity", destinationAddress.getString("city"), requestDocument);
1123                UtilXml.addChildElementValue(requestElement, "ToState", destinationAddress.getString("stateProvinceGeoId"), requestDocument);
1124                UtilXml.addChildElementValue(requestElement, "ToZip5", destinationAddress.getString("postalCode"), requestDocument);
1125                UtilXml.addChildElement(requestElement, "ToZip4", requestDocument);
1126
1127                GenericValue shipmentPackageRouteSeg = (GenericValue) i.next();
1128                GenericValue shipmentPackage = shipmentPackageRouteSeg.getRelatedOne("ShipmentPackage");
1129                String JavaDoc spKeyString = "[" + shipmentPackage.getString("shipmentId") + "," +
1130                        shipmentPackage.getString("shipmentPackageSeqId") + "]";
1131
1132                // WeightInOunces
1133
String JavaDoc weightStr = shipmentPackage.getString("weight");
1134                if (UtilValidate.isEmpty(weightStr)) {
1135                    return ServiceUtil.returnError("weight not found for ShipmentPackage " + spKeyString);
1136                }
1137
1138                double weight = 0;
1139                try {
1140                    weight = Double.parseDouble(weightStr);
1141                } catch (NumberFormatException JavaDoc nfe) {
1142                    nfe.printStackTrace(); // TODO: handle exception
1143
}
1144
1145                String JavaDoc weightUomId = shipmentPackage.getString("weightUomId");
1146                if (UtilValidate.isEmpty(weightUomId)) {
1147                    // assume weight is in pounds for consistency (this assumption is made in uspsDomesticRate also)
1148
weightUomId = "WT_lb";
1149                }
1150                if (!"WT_oz".equals(weightUomId)) {
1151                    // attempt a conversion to pounds
1152
GenericValue uomConversion = delegator.findByPrimaryKey("UomConversion",
1153                            UtilMisc.toMap("uomId", weightUomId, "uomIdTo", "WT_oz"));
1154                    if (uomConversion == null || UtilValidate.isEmpty(uomConversion.getString("conversionFactor"))) {
1155                        return ServiceUtil.returnError("Unsupported weightUom [" + weightUomId + "] for ShipmentPackage " +
1156                                spKeyString + ", could not find a conversion factor for WT_oz");
1157                    }
1158                    weight *= uomConversion.getDouble("conversionFactor").doubleValue();
1159                }
1160
1161                DecimalFormat JavaDoc df = new DecimalFormat JavaDoc("#");
1162                UtilXml.addChildElementValue(requestElement, "WeightInOunces", df.format(Math.ceil(weight)), requestDocument);
1163
1164                UtilXml.addChildElementValue(requestElement, "ServiceType", serviceType, requestDocument);
1165                UtilXml.addChildElementValue(requestElement, "ImageType", "TIF", requestDocument);
1166                UtilXml.addChildElementValue(requestElement, "AddressServiceRequested", "True", requestDocument);
1167
1168                Document JavaDoc responseDocument = null;
1169                try {
1170                    responseDocument = sendUspsRequest("DeliveryConfirmationV2", requestDocument);
1171                } catch (UspsRequestException e) {
1172                    Debug.log(e, module);
1173                    return ServiceUtil.returnError("Error sending request for USPS Delivery Confirmation service: " +
1174                            e.getMessage());
1175                }
1176                Element JavaDoc responseElement = responseDocument.getDocumentElement();
1177
1178                Element JavaDoc respErrorElement = UtilXml.firstChildElement(responseElement, "Error");
1179                if (respErrorElement != null) {
1180                    return ServiceUtil.returnError("The following error was returned by the USPS Delivery Confirmation " +
1181                            "service for ShipmentPackage " + spKeyString + ": " +
1182                            UtilXml.childElementValue(respErrorElement, "Description"));
1183                }
1184
1185                String JavaDoc labelImageString = UtilXml.childElementValue(responseElement, "DeliveryConfirmationLabel");
1186                if (UtilValidate.isEmpty(labelImageString)) {
1187                    return ServiceUtil.returnError("Incomplete response from the USPS Delivery Confirmation service: " +
1188                            "missing or empty DeliveryConfirmationLabel element");
1189                }
1190                shipmentPackageRouteSeg.setBytes("labelImage", Base64.base64Decode(labelImageString.getBytes()));
1191                String JavaDoc trackingCode = UtilXml.childElementValue(responseElement, "DeliveryConfirmationNumber");
1192                if (UtilValidate.isEmpty(trackingCode)) {
1193                    return ServiceUtil.returnError("Incomplete response from the USPS Delivery Confirmation service: " +
1194                            "missing or empty DeliveryConfirmationNumber element");
1195                }
1196                shipmentPackageRouteSeg.set("trackingCode", trackingCode);
1197                shipmentPackageRouteSeg.store();
1198            }
1199
1200        } catch (GenericEntityException gee) {
1201            Debug.log(gee, module);
1202            return ServiceUtil.returnError("Error reading or writing shipment data for the USPS " +
1203                    "Delivery Confirmation service: " + gee.getMessage());
1204        }
1205
1206        return ServiceUtil.returnSuccess();
1207    }
1208
1209    /* ------------------------------------------------------------------------------------------------------------- */
1210
1211    // testing utility service - remove this
1212
public static Map JavaDoc uspsDumpShipmentLabelImages(DispatchContext dctx, Map JavaDoc context) {
1213
1214        GenericDelegator delegator = dctx.getDelegator();
1215
1216        try {
1217
1218            String JavaDoc shipmentId = (String JavaDoc) context.get("shipmentId");
1219            String JavaDoc shipmentRouteSegmentId = (String JavaDoc) context.get("shipmentRouteSegmentId");
1220
1221            GenericValue shipmentRouteSegment = delegator.findByPrimaryKey("ShipmentRouteSegment",
1222                    UtilMisc.toMap("shipmentId", shipmentId, "shipmentRouteSegmentId", shipmentRouteSegmentId));
1223
1224            List JavaDoc shipmentPackageRouteSegList = shipmentRouteSegment.getRelated("ShipmentPackageRouteSeg", null,
1225                    UtilMisc.toList("+shipmentPackageSeqId"));
1226
1227            for (Iterator JavaDoc i = shipmentPackageRouteSegList.iterator(); i.hasNext();) {
1228                GenericValue shipmentPackageRouteSeg = (GenericValue) i.next();
1229
1230                byte[] labelImageBytes = shipmentPackageRouteSeg.getBytes("labelImage");
1231
1232                String JavaDoc outFileName = "UspsLabelImage" + shipmentRouteSegment.getString("shipmentId") + "_" +
1233                        shipmentRouteSegment.getString("shipmentRouteSegmentId") + "_" +
1234                        shipmentPackageRouteSeg.getString("shipmentPackageSeqId") + ".gif";
1235
1236                FileOutputStream JavaDoc fileOut = new FileOutputStream JavaDoc(outFileName);
1237                fileOut.write(labelImageBytes);
1238                fileOut.flush();
1239                fileOut.close();
1240            }
1241
1242        } catch (GenericEntityException e) {
1243            Debug.log(e, module);
1244            return ServiceUtil.returnError(e.getMessage());
1245        } catch (IOException JavaDoc e) {
1246            Debug.log(e, module);
1247            return ServiceUtil.returnError(e.getMessage());
1248        }
1249
1250        return ServiceUtil.returnSuccess();
1251    }
1252
1253    private static Document JavaDoc createUspsRequestDocument(String JavaDoc rootElement) {
1254
1255        Document JavaDoc requestDocument = UtilXml.makeEmptyXmlDocument(rootElement);
1256
1257        Element JavaDoc requestElement = requestDocument.getDocumentElement();
1258        requestElement.setAttribute("USERID",
1259                UtilProperties.getPropertyValue("shipment.properties", "shipment.usps.access.userid"));
1260        requestElement.setAttribute("PASSWORD",
1261                UtilProperties.getPropertyValue("shipment.properties", "shipment.usps.access.password"));
1262
1263        return requestDocument;
1264    }
1265
1266    private static Document JavaDoc sendUspsRequest(String JavaDoc requestType, Document JavaDoc requestDocument) throws UspsRequestException {
1267        String JavaDoc conUrl = UtilProperties.getPropertyValue("shipment.properties", "shipment.usps.connect.url");
1268        if (UtilValidate.isEmpty(conUrl)) {
1269            throw new UspsRequestException("Connection URL not specified; please check your configuration");
1270        }
1271
1272        OutputStream JavaDoc os = new ByteArrayOutputStream JavaDoc();
1273
1274        OutputFormat format = new OutputFormat(requestDocument);
1275        format.setOmitDocumentType(true);
1276        format.setOmitXMLDeclaration(true);
1277        format.setIndenting(false);
1278
1279        XMLSerializer serializer = new XMLSerializer(os, format);
1280        try {
1281            serializer.asDOMSerializer();
1282            serializer.serialize(requestDocument.getDocumentElement());
1283        } catch (IOException JavaDoc e) {
1284            throw new UspsRequestException("Error serializing requestDocument: " + e.getMessage());
1285        }
1286
1287        String JavaDoc xmlString = os.toString();
1288
1289        Debug.logInfo("USPS XML request string: " + xmlString, module);
1290
1291        String JavaDoc timeOutStr = UtilProperties.getPropertyValue("shipment.properties", "shipment.usps.connect.timeout", "60");
1292        int timeout = 60;
1293        try {
1294            timeout = Integer.parseInt(timeOutStr);
1295        } catch (NumberFormatException JavaDoc e) {
1296            Debug.logError(e, "Unable to set timeout to " + timeOutStr + " using default " + timeout);
1297        }
1298
1299        HttpClient http = new HttpClient(conUrl);
1300        http.setTimeout(timeout * 1000);
1301        http.setParameter("API", requestType);
1302        http.setParameter("XML", xmlString);
1303
1304        String JavaDoc responseString = null;
1305        try {
1306            responseString = http.post();
1307        } catch (HttpClientException e) {
1308            throw new UspsRequestException("Problem connecting with USPS server", e);
1309        }
1310
1311        Debug.logInfo("USPS response: " + responseString, module);
1312
1313        Document JavaDoc responseDocument = null;
1314        try {
1315            responseDocument = UtilXml.readXmlDocument(responseString, false);
1316        } catch (SAXException JavaDoc se) {
1317            throw new UspsRequestException("Error reading request Document from a String: " + se.getMessage());
1318        } catch (ParserConfigurationException JavaDoc pce) {
1319            throw new UspsRequestException("Error reading request Document from a String: " + pce.getMessage());
1320        } catch (IOException JavaDoc xmlReadException) {
1321            throw new UspsRequestException("Error reading request Document from a String: " + xmlReadException.getMessage());
1322        }
1323
1324        // If a top-level error document is returned, throw exception
1325
// Other request-level errors should be handled by the caller
1326
Element JavaDoc responseElement = responseDocument.getDocumentElement();
1327        if ("Error".equals(responseElement.getNodeName())) {
1328            throw new UspsRequestException(UtilXml.childElementValue(responseElement, "Description"));
1329        }
1330
1331        return responseDocument;
1332    }
1333
1334}
1335
1336class UspsRequestException extends GeneralException {
1337    UspsRequestException() {
1338        super();
1339    }
1340
1341    UspsRequestException(String JavaDoc msg) {
1342        super(msg);
1343    }
1344
1345    UspsRequestException(Throwable JavaDoc t) {
1346        super(t);
1347    }
1348
1349    UspsRequestException(String JavaDoc msg, Throwable JavaDoc t) {
1350        super(msg, t);
1351    }
1352}
1353
Popular Tags