Skip to content

Commit 8edc332

Browse files
committed
Merge pull request #31 from mwcbrent/master
Adds shipping service (v13) and examples for FedEx Freight
2 parents 4db5551 + 4a64f7a commit 8edc332

File tree

10 files changed

+12892
-19
lines changed

10 files changed

+12892
-19
lines changed
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
#!/usr/bin/env python
2+
"""
3+
This example shows how to create shipments. The variables populated below
4+
represents the minimum required values. You will need to fill all of these, or
5+
risk seeing a SchemaValidationError exception thrown.
6+
7+
Near the bottom of the module, you'll see some different ways to handle the
8+
label data that is returned with the reply.
9+
"""
10+
import logging
11+
import binascii
12+
from example_config import CONFIG_OBJ
13+
from fedex.services.ship_service import FedexProcessShipmentRequest
14+
15+
# Set this to the INFO level to see the response from Fedex printed in stdout.
16+
#logging.basicConfig(filename="suds.log", level=logging.DEBUG)
17+
logging.basicConfig(level=logging.INFO)
18+
# This is the object that will be handling our tracking request.
19+
# We're using the FedexConfig object from example_config.py in this dir.
20+
shipment = FedexProcessShipmentRequest(CONFIG_OBJ)
21+
shipment.RequestedShipment.DropoffType = 'REGULAR_PICKUP'
22+
shipment.RequestedShipment.ServiceType = 'FEDEX_FREIGHT_ECONOMY'
23+
shipment.RequestedShipment.PackagingType = 'YOUR_PACKAGING'
24+
25+
shipment.RequestedShipment.FreightShipmentDetail.FedExFreightAccountNumber = CONFIG_OBJ.freight_account_number
26+
27+
# Shipper contact info.
28+
shipment.RequestedShipment.Shipper.Contact.PersonName = 'Sender Name'
29+
shipment.RequestedShipment.Shipper.Contact.CompanyName = 'Some Company'
30+
shipment.RequestedShipment.Shipper.Contact.PhoneNumber = '9012638716'
31+
32+
# Shipper address.
33+
shipment.RequestedShipment.Shipper.Address.StreetLines = ['1202 Chalet Ln']
34+
shipment.RequestedShipment.Shipper.Address.City = 'Harrison'
35+
shipment.RequestedShipment.Shipper.Address.StateOrProvinceCode = 'AR'
36+
shipment.RequestedShipment.Shipper.Address.PostalCode = '72601'
37+
shipment.RequestedShipment.Shipper.Address.CountryCode = 'US'
38+
shipment.RequestedShipment.Shipper.Address.Residential = True
39+
40+
# Recipient contact info.
41+
shipment.RequestedShipment.Recipient.Contact.PersonName = 'Recipient Name'
42+
shipment.RequestedShipment.Recipient.Contact.CompanyName = 'Recipient Company'
43+
shipment.RequestedShipment.Recipient.Contact.PhoneNumber = '9012637906'
44+
45+
# Recipient address
46+
shipment.RequestedShipment.Recipient.Address.StreetLines = ['2000 Freight LTL Testing']
47+
shipment.RequestedShipment.Recipient.Address.City = 'Harrison'
48+
shipment.RequestedShipment.Recipient.Address.StateOrProvinceCode = 'AR'
49+
shipment.RequestedShipment.Recipient.Address.PostalCode = '72601'
50+
shipment.RequestedShipment.Recipient.Address.CountryCode = 'US'
51+
52+
# This is needed to ensure an accurate rate quote with the response.
53+
shipment.RequestedShipment.Recipient.Address.Residential = False
54+
shipment.RequestedShipment.FreightShipmentDetail.TotalHandlingUnits = 1
55+
shipment.RequestedShipment.ShippingChargesPayment.Payor.ResponsibleParty.AccountNumber = CONFIG_OBJ.freight_account_number
56+
57+
shipment.RequestedShipment.FreightShipmentDetail.FedExFreightBillingContactAndAddress.Contact.PersonName = 'Sender Name'
58+
shipment.RequestedShipment.FreightShipmentDetail.FedExFreightBillingContactAndAddress.Contact.CompanyName = 'Some Company'
59+
shipment.RequestedShipment.FreightShipmentDetail.FedExFreightBillingContactAndAddress.Contact.PhoneNumber = '9012638716'
60+
61+
shipment.RequestedShipment.FreightShipmentDetail.FedExFreightBillingContactAndAddress.Address.StreetLines = ['2000 Freight LTL Testing']
62+
shipment.RequestedShipment.FreightShipmentDetail.FedExFreightBillingContactAndAddress.Address.City = 'Harrison'
63+
shipment.RequestedShipment.FreightShipmentDetail.FedExFreightBillingContactAndAddress.Address.StateOrProvinceCode = 'AR'
64+
shipment.RequestedShipment.FreightShipmentDetail.FedExFreightBillingContactAndAddress.Address.PostalCode = '72601'
65+
shipment.RequestedShipment.FreightShipmentDetail.FedExFreightBillingContactAndAddress.Address.CountryCode = 'US'
66+
shipment.RequestedShipment.FreightShipmentDetail.FedExFreightBillingContactAndAddress.Address.Residential = False
67+
spec = shipment.create_wsdl_object_of_type('ShippingDocumentSpecification')
68+
69+
spec.ShippingDocumentTypes = [spec.CertificateOfOrigin]
70+
# shipment.RequestedShipment.ShippingDocumentSpecification = spec
71+
72+
role = shipment.create_wsdl_object_of_type('FreightShipmentRoleType')
73+
74+
shipment.RequestedShipment.FreightShipmentDetail.Role = role.SHIPPER
75+
shipment.RequestedShipment.FreightShipmentDetail.CollectTermsType = 'STANDARD'
76+
77+
78+
# Specifies the label type to be returned.
79+
shipment.RequestedShipment.LabelSpecification.LabelFormatType = 'FEDEX_FREIGHT_STRAIGHT_BILL_OF_LADING'
80+
81+
# Specifies which format the label file will be sent to you in.
82+
# DPL, EPL2, PDF, PNG, ZPLII
83+
shipment.RequestedShipment.LabelSpecification.ImageType = 'PDF'
84+
85+
# To use doctab stocks, you must change ImageType above to one of the
86+
# label printer formats (ZPLII, EPL2, DPL).
87+
# See documentation for paper types, there quite a few.
88+
shipment.RequestedShipment.LabelSpecification.LabelStockType = 'PAPER_LETTER'
89+
90+
# This indicates if the top or bottom of the label comes out of the
91+
# printer first.
92+
# BOTTOM_EDGE_OF_TEXT_FIRST or TOP_EDGE_OF_TEXT_FIRST
93+
shipment.RequestedShipment.LabelSpecification.LabelPrintingOrientation = 'BOTTOM_EDGE_OF_TEXT_FIRST'
94+
shipment.RequestedShipment.EdtRequestType = 'NONE'
95+
96+
package1_weight = shipment.create_wsdl_object_of_type('Weight')
97+
package1_weight.Value = 500.0
98+
package1_weight.Units = "LB"
99+
100+
shipment.RequestedShipment.FreightShipmentDetail.PalletWeight = package1_weight
101+
102+
package1 = shipment.create_wsdl_object_of_type('FreightShipmentLineItem')
103+
package1.Weight = package1_weight
104+
package1.Packaging = 'PALLET'
105+
package1.Description = 'Products'
106+
package1.FreightClass = 'CLASS_500'
107+
package1.HazardousMaterials = None
108+
package1.Pieces = 12
109+
110+
111+
shipment.RequestedShipment.FreightShipmentDetail.LineItems = package1
112+
113+
# If you'd like to see some documentation on the ship service WSDL, un-comment
114+
# this line. (Spammy).
115+
#print shipment.client
116+
117+
# Un-comment this to see your complete, ready-to-send request as it stands
118+
# before it is actually sent. This is useful for seeing what values you can
119+
# change.
120+
#print shipment.RequestedShipment
121+
122+
# If you want to make sure that all of your entered details are valid, you
123+
# can call this and parse it just like you would via send_request(). If
124+
# shipment.response.HighestSeverity == "SUCCESS", your shipment is valid.
125+
#shipment.send_validation_request()
126+
127+
# Fires off the request, sets the 'response' attribute on the object.
128+
shipment.send_request()
129+
130+
# This will show the reply to your shipment being sent. You can access the
131+
# attributes through the response attribute on the request object. This is
132+
# good to un-comment to see the variables returned by the Fedex reply.
133+
print shipment.response
134+
# Here is the overall end result of the query.
135+
# print "HighestSeverity:", shipment.response.HighestSeverity
136+
# # Getting the tracking number from the new shipment.
137+
# print "Tracking #:", shipment.response.CompletedShipmentDetail.CompletedPackageDetails[0].TrackingIds[0].TrackingNumber
138+
# # Net shipping costs.
139+
# print "Net Shipping Cost (US$):", shipment.response.CompletedShipmentDetail.CompletedPackageDetails[0].PackageRating.PackageRateDetails[0].NetCharge.Amount
140+
141+
# # Get the label image in ASCII format from the reply. Note the list indices
142+
# we're using. You'll need to adjust or iterate through these if your shipment
143+
# has multiple packages.
144+
145+
ascii_label_data = shipment.response.CompletedShipmentDetail.ShipmentDocuments[0].Parts[0].Image
146+
147+
# Convert the ASCII data to binary.
148+
label_binary_data = binascii.a2b_base64(ascii_label_data)
149+
150+
"""
151+
This is an example of how to dump a label to a PNG file.
152+
"""
153+
# This will be the file we write the label out to.
154+
pdf_file = open('example_shipment_label.pdf', 'wb')
155+
pdf_file.write(label_binary_data)
156+
pdf_file.close()
157+
158+
"""
159+
This is an example of how to print the label to a serial printer. This will not
160+
work for all label printers, consult your printer's documentation for more
161+
details on what formats it can accept.
162+
"""
163+
# Pipe the binary directly to the label printer. Works under Linux
164+
# without requiring PySerial. This WILL NOT work on other platforms.
165+
#label_printer = open("/dev/ttyS0", "w")
166+
#label_printer.write(label_binary_data)
167+
#label_printer.close()
168+
169+
"""
170+
This is a potential cross-platform solution using pySerial. This has not been
171+
tested in a long time and may or may not work. For Windows, Mac, and other
172+
platforms, you may want to go this route.
173+
"""
174+
#import serial
175+
#label_printer = serial.Serial(0)
176+
#print "SELECTED SERIAL PORT: "+ label_printer.portstr
177+
#label_printer.write(label_binary_data)
178+
#label_printer.close()

examples/create_shipment.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,6 @@
3131
# FEDEX_BOX, FEDEX_PAK, FEDEX_TUBE, YOUR_PACKAGING
3232
shipment.RequestedShipment.PackagingType = 'FEDEX_PAK'
3333

34-
# No idea what this is.
35-
# INDIVIDUAL_PACKAGES, PACKAGE_GROUPS, PACKAGE_SUMMARY
36-
shipment.RequestedShipment.PackageDetail = 'INDIVIDUAL_PACKAGES'
37-
3834
# Shipper contact info.
3935
shipment.RequestedShipment.Shipper.Contact.PersonName = 'Sender Name'
4036
shipment.RequestedShipment.Shipper.Contact.CompanyName = 'Some Company'
@@ -61,7 +57,9 @@
6157
shipment.RequestedShipment.Recipient.Address.CountryCode = 'US'
6258
# This is needed to ensure an accurate rate quote with the response.
6359
shipment.RequestedShipment.Recipient.Address.Residential = True
60+
shipment.RequestedShipment.EdtRequestType = 'NONE'
6461

62+
shipment.RequestedShipment.ShippingChargesPayment.Payor.ResponsibleParty.AccountNumber = CONFIG_OBJ.account_number
6563
# Who pays for the shipment?
6664
# RECIPIENT, SENDER or THIRD_PARTY
6765
shipment.RequestedShipment.ShippingChargesPayment.PaymentType = 'SENDER'
@@ -90,6 +88,7 @@
9088
package1_weight.Units = "LB"
9189

9290
package1 = shipment.create_wsdl_object_of_type('RequestedPackageLineItem')
91+
package1.PhysicalPackaging = 'BOX'
9392
package1.Weight = package1_weight
9493
# Un-comment this to see the other variables you may set on a package.
9594
#print package1

examples/example_config.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@
1111
from fedex.config import FedexConfig
1212

1313
# Change these values to match your testing account/meter number.
14-
CONFIG_OBJ = FedexConfig(key='xxxxxxxxxxxxxxxx',
15-
password='xxxxxxxxxxxxxxxxxxxxxxxxx',
16-
account_number='#########',
17-
meter_number='#########',
14+
CONFIG_OBJ = FedexConfig(key='xxxxxxxxxxx',
15+
password='xxxxxxxxxxx',
16+
account_number='xxxxxxxxxxx',
17+
meter_number='xxxxxxxxxxx',
18+
freight_account_number='xxxxxxxxxxx',
1819
use_test_server=True)

examples/freight_rate_request.py

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
#!/usr/bin/env python
2+
"""
3+
This example shows how to use the FedEx RateRequest service.
4+
The variables populated below represents the minimum required values.
5+
You will need to fill all of these, or risk seeing a SchemaValidationError
6+
exception thrown by suds.
7+
8+
TIP: Near the bottom of the module, see how to check the if the destination
9+
is Out of Delivery Area (ODA).
10+
"""
11+
import logging
12+
from example_config import CONFIG_OBJ
13+
from fedex.services.rate_service import FedexRateServiceRequest
14+
15+
# Set this to the INFO level to see the response from Fedex printed in stdout.
16+
logging.basicConfig(level=logging.INFO)
17+
18+
# This is the object that will be handling our tracking request.
19+
# We're using the FedexConfig object from example_config.py in this dir.
20+
rate_request = FedexRateServiceRequest(CONFIG_OBJ)
21+
22+
rate_request.RequestedShipment.ServiceType = 'FEDEX_FREIGHT'
23+
24+
rate_request.RequestedShipment.DropoffType = 'REGULAR_PICKUP'
25+
26+
rate_request.RequestedShipment.PackagingType = 'YOUR_PACKAGING'
27+
28+
rate_request.RequestedShipment.FreightShipmentDetail.TotalHandlingUnits = 1
29+
30+
rate_request.RequestedShipment.FreightShipmentDetail.FedExFreightAccountNumber = CONFIG_OBJ.freight_account_number
31+
32+
rate_request.RequestedShipment.Shipper.Address.PostalCode = '72601'
33+
rate_request.RequestedShipment.Shipper.Address.CountryCode = 'US'
34+
rate_request.RequestedShipment.Shipper.Address.City = 'Harrison'
35+
rate_request.RequestedShipment.Shipper.Address.StateOrProvinceCode = 'AR'
36+
rate_request.RequestedShipment.Shipper.Address.Residential = False
37+
rate_request.RequestedShipment.Recipient.Address.PostalCode = '72601'
38+
rate_request.RequestedShipment.Recipient.Address.CountryCode = 'US'
39+
rate_request.RequestedShipment.Recipient.Address.StateOrProvinceCode = 'AR'
40+
rate_request.RequestedShipment.Recipient.Address.City = 'Harrison'
41+
42+
#include estimated duties and taxes in rate quote, can be ALL or NONE
43+
rate_request.RequestedShipment.EdtRequestType = 'NONE'
44+
45+
rate_request.RequestedShipment.PackageDetail = 'PACKAGE_SUMMARY'
46+
47+
rate_request.RequestedShipment.FreightShipmentDetail.PaymentType = 'PREPAID'
48+
49+
# note: in order for this to work in test, you may need to use the
50+
# specially provided LTL addresses emailed to you when signing up.
51+
rate_request.RequestedShipment.FreightShipmentDetail.FedExFreightBillingContactAndAddress.Contact.PersonName = 'Sender Name'
52+
rate_request.RequestedShipment.FreightShipmentDetail.FedExFreightBillingContactAndAddress.Contact.CompanyName = 'Some Company'
53+
rate_request.RequestedShipment.FreightShipmentDetail.FedExFreightBillingContactAndAddress.Contact.PhoneNumber = '9012638716'
54+
rate_request.RequestedShipment.FreightShipmentDetail.FedExFreightBillingContactAndAddress.Address.StreetLines = ['2000 Freight LTL Testing']
55+
rate_request.RequestedShipment.FreightShipmentDetail.FedExFreightBillingContactAndAddress.Address.City = 'Harrison'
56+
rate_request.RequestedShipment.FreightShipmentDetail.FedExFreightBillingContactAndAddress.Address.StateOrProvinceCode = 'AR'
57+
rate_request.RequestedShipment.FreightShipmentDetail.FedExFreightBillingContactAndAddress.Address.PostalCode = '72601'
58+
rate_request.RequestedShipment.FreightShipmentDetail.FedExFreightBillingContactAndAddress.Address.CountryCode = 'US'
59+
rate_request.RequestedShipment.FreightShipmentDetail.FedExFreightBillingContactAndAddress.Address.Residential = False
60+
61+
spec = rate_request.create_wsdl_object_of_type('ShippingDocumentSpecification')
62+
63+
spec.ShippingDocumentTypes = [spec.CertificateOfOrigin]
64+
65+
rate_request.RequestedShipment.ShippingDocumentSpecification = spec
66+
67+
role = rate_request.create_wsdl_object_of_type('FreightShipmentRoleType')
68+
69+
rate_request.RequestedShipment.FreightShipmentDetail.Role = role.SHIPPER
70+
71+
package1_weight = rate_request.create_wsdl_object_of_type('Weight')
72+
package1_weight.Value = 500.0
73+
package1_weight.Units = "LB"
74+
75+
rate_request.RequestedShipment.FreightShipmentDetail.PalletWeight = package1_weight
76+
77+
package1 = rate_request.create_wsdl_object_of_type('FreightShipmentLineItem')
78+
package1.Weight = package1_weight
79+
package1.Packaging = 'PALLET'
80+
package1.Description = 'Products'
81+
package1.FreightClass = 'CLASS_500'
82+
83+
rate_request.RequestedShipment.FreightShipmentDetail.LineItems = package1
84+
85+
# If you'd like to see some documentation on the ship service WSDL, un-comment
86+
# this line. (Spammy).
87+
#print rate_request.client
88+
89+
# Un-comment this to see your complete, ready-to-send request as it stands
90+
# before it is actually sent. This is useful for seeing what values you can
91+
# change.
92+
#print rate_request.RequestedShipment
93+
94+
# Fires off the request, sets the 'response' attribute on the object.
95+
rate_request.send_request()
96+
97+
# This will show the reply to your rate_request being sent. You can access the
98+
# attributes through the response attribute on the request object. This is
99+
# good to un-comment to see the variables returned by the FedEx reply.
100+
#print rate_request.response
101+
102+
# Here is the overall end result of the query.
103+
print "HighestSeverity:", rate_request.response.HighestSeverity
104+
105+
# RateReplyDetails can contain rates for multiple ServiceTypes if ServiceType was set to None
106+
for service in rate_request.response.RateReplyDetails:
107+
for detail in service.RatedShipmentDetails:
108+
for surcharge in detail.ShipmentRateDetail.Surcharges:
109+
if surcharge.SurchargeType == 'OUT_OF_DELIVERY_AREA':
110+
print "%s: ODA rate_request charge %s" % (service.ServiceType, surcharge.Amount.Amount)
111+
112+
for rate_detail in service.RatedShipmentDetails:
113+
print "%s: Net FedEx Charge %s %s" % (service.ServiceType, rate_detail.ShipmentRateDetail.TotalNetFedExCharge.Currency,
114+
rate_detail.ShipmentRateDetail.TotalNetFedExCharge.Amount)
115+

examples/rate_request.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@
5656
# Who pays for the rate_request?
5757
# RECIPIENT, SENDER or THIRD_PARTY
5858
rate_request.RequestedShipment.ShippingChargesPayment.PaymentType = 'SENDER'
59-
59+
rate_request.RequestedShipment.ShippingChargesPayment.Payor.AccountNumber = CONFIG_OBJ.account_number
6060
package1_weight = rate_request.create_wsdl_object_of_type('Weight')
6161
# Weight, in LB.
6262
package1_weight.Value = 1.0

fedex/config.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ class FedexConfig(object):
1919
C{password} arguments and set the instance variables documented below
2020
at a later time if you must.
2121
"""
22-
def __init__(self, key, password, account_number=None, meter_number=None,
22+
def __init__(self, key, password, account_number=None, meter_number=None, freight_account_number=None,
2323
integrator_id=None, wsdl_path=None, express_region_code=None, use_test_server=False):
2424
"""
2525
@type key: L{str}
@@ -33,6 +33,9 @@ def __init__(self, key, password, account_number=None, meter_number=None,
3333
@type meter_number: L{str}
3434
@keyword meter_number: The meter number sent to you by Fedex after
3535
registering for Web Services.
36+
@type freight_account_number: L{str}
37+
@keyword freight_account_number: The freight account number sent to you
38+
by Fedex after registering for Web Services.
3639
@type integrator_id: L{str}
3740
@keyword integrator_id: The integrator string sent to you by Fedex after
3841
registering for Web Services.
@@ -53,6 +56,8 @@ def __init__(self, key, password, account_number=None, meter_number=None,
5356
"""@ivar: Web Services account number."""
5457
self.meter_number = meter_number
5558
"""@ivar: Web services meter number."""
59+
self.freight_account_number = freight_account_number
60+
"""@ivar: Web Services freight accountnumber."""
5661
self.integrator_id = integrator_id
5762
"""@ivar: Web services integrator ID."""
5863
self.express_region_code = express_region_code

fedex/services/rate_service.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -80,11 +80,6 @@ def _prepare_wsdl_objects(self):
8080
# Assume US.
8181
Payor.CountryCode = 'US'
8282

83-
ShippingChargesPayment = self.client.factory.create('Payment')
84-
ShippingChargesPayment.Payor = Payor
85-
86-
self.RequestedShipment.ShippingChargesPayment = ShippingChargesPayment
87-
8883
# ACCOUNT or LIST
8984
self.RequestedShipment.RateRequestTypes = ['ACCOUNT']
9085

0 commit comments

Comments
 (0)