Skip to content

Commit de6419c

Browse files
author
Greg Taylor
committed
Shipment creation is in and ready for refinement. It's in decent proof-of-concept shape, though.
1 parent 3867c76 commit de6419c

File tree

5 files changed

+183
-97
lines changed

5 files changed

+183
-97
lines changed

examples/create_shipment.py

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
#!/usr/bin/env python
2+
"""
3+
This example shows how to ship shipments.
4+
"""
5+
import logging
6+
from fedex.services.ship_service import FedexProcessShipmentRequest
7+
from fedex.config import FedexConfig
8+
9+
# Set this to the INFO level to see the response from Fedex printed in stdout.
10+
logging.basicConfig(level=logging.ERROR)
11+
12+
# FedexConfig objects should generally only be instantiated once and re-used
13+
# amongst different queries. They hold static data like account number.
14+
config_obj = FedexConfig(key='ZyNQQFdcxUATOx9L',
15+
password='GtngmKzs4Dk4RYmrlAjrLykwi',
16+
account_number='510087780',
17+
meter_number='118501898',
18+
use_test_server=True)
19+
20+
# This is the object that will be handling our tracking request.
21+
ship = FedexProcessShipmentRequest(config_obj)
22+
23+
# This is very generalized, top-level information.
24+
# REGULAR_PICKUP, REQUEST_COURIER, DROP_BOX, BUSINESS_SERVICE_CENTER or STATION
25+
ship.RequestedShipment.DropoffType = 'REGULAR_PICKUP'
26+
27+
# See page 355 in WS_ShipService.pdf for a full list. Here are the common ones:
28+
# STANDARD_OVERNIGHT, PRIORITY_OVERNIGHT, FEDEX_GROUND, FEDEX_EXPRESS_SAVER
29+
ship.RequestedShipment.ServiceType = 'PRIORITY_OVERNIGHT'
30+
31+
# What kind of package this will be shipped in.
32+
# FEDEX_BOX, FEDEX_PAK, FEDEX_TUBE, YOUR_PACKAGING
33+
ship.RequestedShipment.PackagingType = 'FEDEX_PAK'
34+
35+
# No idea what this is.
36+
# INDIVIDUAL_PACKAGES, PACKAGE_GROUPS, PACKAGE_SUMMARY
37+
ship.RequestedShipment.PackageDetail = 'INDIVIDUAL_PACKAGES'
38+
39+
# Shipper contact info.
40+
ship.ShipperContact.PersonName = 'Sender Name'
41+
ship.ShipperContact.CompanyName = 'Some Company'
42+
ship.ShipperContact.PhoneNumber = '9012638716'
43+
44+
# Shipper address.
45+
ship.ShipperAddress.StreetLines = ['Address Line 1']
46+
ship.ShipperAddress.City = 'Herndon'
47+
ship.ShipperAddress.StateOrProvinceCode = 'VA'
48+
ship.ShipperAddress.PostalCode = '20171'
49+
ship.ShipperAddress.CountryCode = 'US'
50+
ship.ShipperAddress.Residential = True
51+
52+
# Recipient contact info.
53+
ship.RecipientContact.PersonName = 'Recipient Name'
54+
ship.RecipientContact.CompanyName = 'Recipient Company'
55+
ship.RecipientContact.PhoneNumber = '9012637906'
56+
57+
# Recipient address
58+
ship.RecipientAddress.StreetLines = ['Address Line 1']
59+
ship.RecipientAddress.City = 'Herndon'
60+
ship.RecipientAddress.StateOrProvinceCode = 'VA'
61+
ship.RecipientAddress.PostalCode = '20171'
62+
ship.RecipientAddress.CountryCode = 'US'
63+
# This is needed to ensure an accurate rate quote with the response.
64+
ship.RecipientAddress.Residential = True
65+
66+
# RECIPIENT, SENDER or THIRD_PARTY
67+
ship.ShippingChargesPayment.PaymentType = 'SENDER'
68+
69+
# These are example label values. You'll want to adjust these to fit your
70+
# usage case.
71+
ship.LabelSpecification.LabelFormatType = 'COMMON2D'
72+
ship.LabelSpecification.ImageType = 'PNG'
73+
ship.LabelSpecification.LabelStockType = 'PAPER_7X4.75'
74+
75+
package1_weight = ship.create_wsdl_object_of_type('Weight')
76+
# Weight, in pounds.
77+
package1_weight.Value = 1.0
78+
package1_weight.Units = "LB"
79+
80+
package1 = ship.create_wsdl_object_of_type('RequestedPackageLineItem')
81+
package1.Weight = package1_weight
82+
#print package1
83+
84+
ship.add_package(package1)
85+
86+
# If you'd like to see some documentation on the ship service WSDL, un-comment
87+
# this line. (Spammy).
88+
#print ship.client
89+
90+
# Un-comment this to see your complete, ready-to-send request as it stands
91+
# before it is actually sent. This is useful for seeing what values you can
92+
# change.
93+
#print ship.RequestedShipment
94+
95+
# Fires off the request, sets the 'response' attribute on the object.
96+
ship.send_request()
97+
98+
# This will show the reply to your shipment being sent. You can access the
99+
# attributes through the response attribute on the request object.
100+
print ship.response

examples/track_shipment.py

100644100755
File mode changed.

fedex/base_service.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,12 @@ def _check_response_for_request_errors(self):
175175
"""
176176
pass
177177

178+
def create_wsdl_object_of_type(self, type_name):
179+
"""
180+
Creates and returns a WSDL object of the specified type.
181+
"""
182+
return self.client.factory.create(type_name)
183+
178184
def send_request(self):
179185
"""
180186
Sends the assembled request on the child object.

fedex/services/ship_service.py

Lines changed: 75 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from datetime import datetime
99
from .. base_service import FedexBaseService
1010

11-
class FedexShipRequest(FedexBaseService):
11+
class FedexProcessShipmentRequest(FedexBaseService):
1212
"""
1313
This class allows you to track shipments by providing a tracking
1414
number or other identifying features. By default, you
@@ -17,128 +17,100 @@ class FedexShipRequest(FedexBaseService):
1717
want to read the documentation for the L{__init__} method.
1818
Particularly, the tracking_value and package_identifier arguments.
1919
"""
20-
def __init__(self, config_obj, tracking_value,
21-
package_identifier='TRACKING_NUMBER_OR_DOORTAG',
22-
*args, **kwargs):
20+
def __init__(self, config_obj, *args, **kwargs):
2321
"""
2422
Sends a shipment tracking request. The optional keyword args
2523
detailed on L{FedexBaseService} apply here as well.
26-
27-
@type tracking_value: L{str}
28-
@param tracking_value: Based on the value of package_identifier,
29-
this will be anything from a tracking number to a purchase order
30-
number.
31-
@type package_identifier: L{str}
32-
@keyword package_identifier: Determines what you are using to query for
33-
the shipment. The default assumes that tracking_value will be a Fedex
34-
tracking number.
24+
25+
@type config_obj: L{FedexConfig}
26+
@param config_obj: A valid FedexConfig object.
3527
"""
3628
self._config_obj = config_obj
3729

3830
# Holds version info for the VersionId SOAP object.
3931
self._version_info = {'service_id': 'ship', 'major': '7',
4032
'intermediate': '0', 'minor': '0'}
4133
# Call the parent FedexBaseService class for basic setup work.
42-
super(FedexShipRequest, self).__init__(self._config_obj,
43-
'ShipService_v7.wsdl',
44-
*args, **kwargs)
34+
super(FedexProcessShipmentRequest, self).__init__(self._config_obj,
35+
'ShipService_v7.wsdl',
36+
*args, **kwargs)
37+
# Prepare the data structures.
38+
self.__set_requested_shipment()
4539

4640
def __set_requested_shipment(self):
4741
"""
4842
This is the data that will be used to create your shipment. Create
4943
the data structure and get it ready for the WSDL request.
5044
"""
51-
RequestedShipment = self.client.factory.create('RequestedShipment')
52-
RequestedShipment.ShipTimestamp = '2009-09-17T09:35:36-04:00'
53-
RequestedShipment.DropoffType = 'REGULAR_PICKUP' # REGULAR_PICKUP, REQUEST_COURIER, DROP_BOX, BUSINESS_SERVICE_CENTER and STATION
54-
RequestedShipment.ServiceType = 'PRIORITY_OVERNIGHT' # valid values STANDARD_OVERNIGHT, PRIORITY_OVERNIGHT, FEDEX_GROUND
55-
RequestedShipment.PackagingType = 'FEDEX_PAK' # valid values FEDEX_BOX, FEDEX_PAK, FEDEX_TUBE, YOUR_PACKAGING
45+
# This is the primary data structure for processShipment requests.
46+
self.RequestedShipment = self.client.factory.create('RequestedShipment')
47+
self.RequestedShipment.ShipTimestamp = datetime.now()
5648

57-
Weight = self.client.factory.create('Weight')
58-
Weight.Value = 1.0
59-
Weight.Units = 'LB' # LB or KG
60-
# Assemble
61-
RequestedShipment.TotalWeight = Weight
49+
self.Weight = self.client.factory.create('Weight')
50+
# Start at nothing.
51+
self.Weight.Value = 0.0
52+
# Default to pounds.
53+
self.Weight.Units = 'LB'
54+
# This is the total weight of the entire shipment. Shipments may
55+
# contain more than one package.
56+
self.RequestedShipment.TotalWeight = self.Weight
6257

6358
"""
6459
Begin shipper info.
6560
"""
66-
ShipperParty = self.client.factory.create('Party')
67-
ShipperContact = self.client.factory.create('Contact')
68-
ShipperContact.PersonName = 'Sender Name'
69-
ShipperContact.CompanyName = 'Some Company'
70-
ShipperContact.PhoneNumber = '9012638716'
71-
# Assemble
72-
ShipperParty.Contact = ShipperContact
73-
74-
ShipperAddress = self.client.factory.create('Address')
75-
ShipperAddress.StreetLines = ['Address Line 1']
76-
ShipperAddress.City = 'Herndon'
77-
ShipperAddress.StateOrProvinceCode = 'VA'
78-
ShipperAddress.PostalCode = '20171'
79-
ShipperAddress.CountryCode = 'US'
80-
ShipperAddress.Residential = True
81-
# Assemble
82-
ShipperParty.Address = ShipperAddress
83-
# Assemble
84-
RequestedShipment.Shipper = ShipperParty
61+
self.ShipperContact = self.client.factory.create('Contact')
62+
self.ShipperAddress = self.client.factory.create('Address')
63+
64+
# This is the top level data structure for Shipper information.
65+
self.ShipperParty = self.client.factory.create('Party')
66+
self.ShipperParty.Address = self.ShipperAddress
67+
self.ShipperParty.Contact = self.ShipperContact
68+
69+
# Link the ShipperParty to our master data structure.
70+
self.RequestedShipment.Shipper = self.ShipperParty
8571
"""
8672
End shipper info.
8773
"""
8874

8975
"""
9076
Begin recipient info.
9177
"""
92-
RecipientParty = self.client.factory.create('Party')
93-
RecipientContact = self.client.factory.create('Contact')
94-
RecipientContact.PersonName = 'Recipient Name'
95-
RecipientContact.CompanyName = 'Recipient Company'
96-
RecipientContact.PhoneNumber = '9012637906'
97-
# Assemble
98-
RecipientParty.Contact = RecipientContact
99-
100-
RecipientAddress = self.client.factory.create('Address')
101-
RecipientAddress.StreetLines = ['Address Line 1']
102-
RecipientAddress.City = 'Herndon'
103-
RecipientAddress.StateOrProvinceCode = 'VA'
104-
RecipientAddress.PostalCode = '20171'
105-
RecipientAddress.CountryCode = 'US'
106-
RecipientAddress.Residential = True
107-
# Assemble
108-
RecipientParty.Address = RecipientAddress
109-
# Assemble
110-
RequestedShipment.Recipient = RecipientParty
78+
self.RecipientContact = self.client.factory.create('Contact')
79+
self.RecipientAddress = self.client.factory.create('Address')
80+
81+
# This is the top level data structure for Recipient information.
82+
self.RecipientParty = self.client.factory.create('Party')
83+
self.RecipientParty.Contact = self.RecipientContact
84+
self.RecipientParty.Address = self.RecipientAddress
85+
86+
# Link the RecipientParty object to our master data structure.
87+
self.RequestedShipment.Recipient = self.RecipientParty
11188
"""
11289
End recipient info.
11390
"""
91+
92+
self.Payor = self.client.factory.create('Payor')
93+
# Grab the account number from the FedexConfig object by default.
94+
self.Payor.AccountNumber = self._config_obj.account_number
95+
# Assume US.
96+
self.Payor.CountryCode = 'US'
11497

115-
ShippingChargesPayment = self.client.factory.create('Payment')
116-
ShippingChargesPayment.PaymentType = 'SENDER' # RECIPIENT, SENDER and THIRD_PARTY
117-
Payor = self.client.factory.create('Payor')
118-
Payor.AccountNumber = self._config_obj.account_number
119-
Payor.CountryCode = 'US'
120-
ShippingChargesPayment.Payor = Payor
121-
# Assemble
122-
RequestedShipment.ShippingChargesPayment = ShippingChargesPayment
123-
124-
LabelSpecification = self.client.factory.create('LabelSpecification')
125-
LabelSpecification.LabelFormatType = 'COMMON2D'
126-
LabelSpecification.ImageType = 'PNG'
127-
LabelSpecification.LabelStockType = 'PAPER_7X4.75'
128-
RequestedShipment.LabelSpecification = LabelSpecification
98+
self.ShippingChargesPayment = self.client.factory.create('Payment')
99+
self.ShippingChargesPayment.Payor = self.Payor
100+
self.RequestedShipment.ShippingChargesPayment = self.ShippingChargesPayment
129101

130-
RequestedShipment.RateRequestTypes = ['ACCOUNT'] # ACCOUNT and LIST
131-
RequestedShipment.PackageCount = 1
132-
RequestedShipment.PackageDetail = 'INDIVIDUAL_PACKAGES'
102+
self.LabelSpecification = self.client.factory.create('LabelSpecification')
103+
self.RequestedShipment.LabelSpecification = self.LabelSpecification
133104

134-
# One package
135-
RequestedPackageLineItem = self.client.factory.create('RequestedPackageLineItem')
136-
RequestedPackageLineItem.Weight = Weight
105+
self.RequestedShipment.RateRequestTypes = ['ACCOUNT'] # ACCOUNT and LIST
137106

138-
RequestedShipment.RequestedPackageLineItems = [RequestedPackageLineItem]
107+
# Start with no packages, user must add them.
108+
self.RequestedShipment.PackageCount = 0
109+
self.RequestedShipment.RequestedPackageLineItems = []
139110

140-
self.logger.info(RequestedShipment)
141-
self.RequestedShipment = RequestedShipment
111+
# This is good to review if you'd like to see what the data structure
112+
# looks like.
113+
self.logger.info(self.RequestedShipment)
142114

143115
def _assemble_and_send_request(self):
144116
"""
@@ -147,19 +119,25 @@ def _assemble_and_send_request(self):
147119
@warning: NEVER CALL THIS METHOD DIRECTLY. CALL send_request(), WHICH RESIDES
148120
ON FedexBaseService AND IS INHERITED.
149121
"""
150-
self.__set_requested_shipment()
151-
client = self.client
152122
# Fire off the query.
153-
response = client.service.processShipment(WebAuthenticationDetail=self.WebAuthenticationDetail,
123+
response = self.client.service.processShipment(WebAuthenticationDetail=self.WebAuthenticationDetail,
154124
ClientDetail=self.ClientDetail,
155125
TransactionDetail=self.TransactionDetail,
156126
Version=self.VersionId,
157127
RequestedShipment=self.RequestedShipment)
128+
return response
129+
130+
def add_package(self, package_item):
158131
"""
159-
processShipment(WebAuthenticationDetail WebAuthenticationDetail,
160-
ClientDetail ClientDetail,
161-
TransactionDetail TransactionDetail,
162-
VersionId Version,
163-
RequestedShipment RequestedShipment)
164-
"""
165-
return response
132+
Adds a package to the ship request.
133+
134+
@type package_item: L{RequestedPackageLineItem}
135+
@keyword package_item: A L{RequestedPackageLineItem}, created by
136+
calling create_wsdl_object_of_type('RequestedPackageLineItem') on
137+
this ShipmentRequest object. See examples/create_shipment.py for
138+
more details.
139+
"""
140+
self.RequestedShipment.RequestedPackageLineItems.append(package_item)
141+
package_weight = package_item.Weight.Value
142+
self.RequestedShipment.TotalWeight.Value += package_weight
143+
self.RequestedShipment.PackageCount += 1

fedex/services/track_service.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ def __init__(self, config_obj, tracking_value,
3030
Sends a shipment tracking request. The optional keyword args
3131
detailed on L{FedexBaseService} apply here as well.
3232
33+
@type config_obj: L{FedexConfig}
34+
@param config_obj: A valid FedexConfig object.
3335
@type tracking_value: L{str}
3436
@param tracking_value: Based on the value of package_identifier,
3537
this will be anything from a tracking number to a purchase order

0 commit comments

Comments
 (0)