Site Sections: Satchmo Main | Wiki | Demo Store |

root/satchmo/trunk/satchmo/shipping/modules/ups/shipper.py

Revision 1424, 6.9 kB (checked in by bkroeze, 2 months ago)

Merged multishop to trunk. Revs 1363:1423

  • Property svn:eol-style set to native
  • Property svn:executable set to *
Line 
1 """
2 UPS Shipping Module
3 You must have a UPS account to use this module.
4 You may register at ups.com
5
6 This module uses the XML online tools for maximum flexibility.  It is
7 primarily created for use in the US but reconfiguring for international
8 shipping or more advanced uses should be straightforward.
9
10 It is recommended that you refer to the UPS shipper developer documents
11 (available when you register at UPS) in order to tailor this to your
12 unique needs.
13 """
14
15 # Note, make sure you use decimal math everywhere!
16 try:
17     from decimal import Decimal
18 except:
19     from django.utils._decimal import Decimal
20
21 from django.utils.translation import ugettext as _
22 from satchmo.shipping.modules.base import BaseShipper
23 from django.template import Context, loader
24 from satchmo.configuration import config_get_group, config_value
25 import urllib2
26 from django.core.cache import cache
27 import logging
28 try:
29     from xml.etree.ElementTree import fromstring, tostring
30 except ImportError:
31     from elementtree.ElementTree import fromstring, tostring
32
33 log = logging.getLogger('ups.shipper')
34 class Shipper(BaseShipper):
35    
36     def __init__(self, cart=None, contact=None, service_type=None):
37         self._calculated = False
38         self.cart = cart
39         self.contact = contact
40         if service_type:       
41             self.service_type_code = service_type[0]
42             self.service_type_text = service_type[1]
43         else:
44             self.service_type_code = "99"
45             self.service_type_text = "Uninitialized"
46         self.id = u"UPS-%s-%s" % (self.service_type_code, self.service_type_text)
47         self.raw = "NO DATA"
48         #if cart or contact:
49         #    self.calculate(cart, contact)
50    
51     def __str__(self):
52         """
53         This is mainly helpful for debugging purposes
54         """
55         return "UPS"
56        
57     def description(self):
58         """
59         A basic description that will be displayed to the user when selecting their shipping options
60         """
61         return _("UPS - %s" % self.service_type_text)
62
63     def cost(self):
64         """
65         Complex calculations can be done here as long as the return value is a decimal figure
66         """
67         assert(self._calculated)
68         return(Decimal(self.charges))
69
70     def method(self):
71         """
72         Describes the actual delivery service (Mail, FedEx, DHL, UPS, etc)
73         """
74         return _("UPS")
75
76     def expectedDelivery(self):
77         """
78         Can be a plain string or complex calcuation returning an actual date
79         """
80         if self.delivery_days <> "1":
81             return _("%s business days" % self.delivery_days)
82         else:
83             return _("%s business day" % self.delivery_days)
84        
85     def valid(self, order=None):
86         """
87         Can do complex validation about whether or not this option is valid.
88         For example, may check to see if the recipient is in an allowed country
89         or location.
90         """
91         return self.is_valid
92
93     def _process_request(self, connection, request):
94         """
95         Post the data and return the XML response
96         """
97         conn = urllib2.Request(url=connection, data=request.encode("utf-8"))
98         f = urllib2.urlopen(conn)
99         all_results = f.read()
100         self.raw = all_results
101         return(fromstring(all_results))
102        
103     def calculate(self, cart, contact):
104         """
105         Based on the chosen UPS method, we will do our call to UPS and see how much it will
106         cost.
107         We will also need to store the results for further parsing and return via the
108         methods above
109         """
110         from satchmo.shop.models import Config
111        
112         settings =  config_get_group('satchmo.shipping.modules.ups')
113         self.delivery_days = _("3 - 4") #Default setting for ground delivery
114         shop_details = Config.objects.get_current()
115         configuration = {
116             'xml_key': settings.XML_KEY.value,
117             'account': settings.ACCOUNT.value,
118             'userid': settings.USER_ID.value,
119             'password': settings.USER_PASSWORD.value,
120             'container': settings.SHIPPING_CONTAINER.value,
121             'pickup': settings.PICKUP_TYPE.value,
122             'ship_type': self.service_type_code,
123             'shop_details':shop_details,
124         }
125         c = Context({
126                 'config': configuration,
127                 'cart': cart,
128                 'contact': contact
129             })
130         t = loader.get_template('shipping/ups/request.xml')
131         request = t.render(c)
132         self.is_valid = False
133         if settings.LIVE.value:
134             connection = settings.CONNECTION.value
135         else:
136             connection = settings.CONNECTION_TEST.value
137         cache_key_response = "ups-cart-%s-response" % int(cart.id)
138         cache_key_request = "ups-cart-%s-request" % int(cart.id)
139         last_request = cache.get(cache_key_request)
140         tree = cache.get(cache_key_response)
141
142         if (last_request != request) or tree is None:
143             self.verbose_log("Requesting from UPS [%s]\n%s", cache_key_request, request)
144             cache.set(cache_key_request, request, 60)
145             tree = self._process_request(connection, request)
146             self.verbose_log("Got from UPS [%s]:\n%s", cache_key_response, self.raw)
147             needs_cache = True
148         else:
149             needs_cache = False
150
151         try:
152             status_code = tree.getiterator('ResponseStatusCode')
153             status_val = status_code[0].text
154             self.verbose_log("UPS Status Code for cart #%s = %s", int(cart.id), status_val)
155         except AttributeError:
156             status_val = "-1"
157        
158         if status_val == '1':
159             self.is_valid = False
160             self._calculated = False
161             all_rates = tree.getiterator('RatedShipment')
162             for response in all_rates:
163                 if self.service_type_code == response.find('.//Service/Code/').text:
164                     self.charges = response.find('.//TotalCharges/MonetaryValue').text
165                     if response.find('.//GuaranteedDaysToDelivery').text:
166                         self.delivery_days = response.find('.//GuaranteedDaysToDelivery').text
167                     self.is_valid = True
168                     self._calculated = True
169                     if needs_cache:
170                         cache.set(cache_key_response, tree, 60)
171                        
172             if not self.is_valid:
173                 self.verbose_log("UPS Cannot find rate for code: %s [%s]", self.service_type_code, self.service_type_text)
174        
175         else:
176             self.is_valid = False
177             self._calculated = False
178
179             try:
180                 errors = tree.find('.//Error')
181                 log.info("UPS %s Error: Code %s - %s" % (errors[0].text, errors[1].text, errors[2].text))
182             except AttributeError:
183                 log.info("UPS error - cannot parse response:\n %s", self.raw)
184            
185     def verbose_log(self, *args, **kwargs):
186         if config_value('satchmo.shipping.modules.ups', 'VERBOSE_LOG'):
187             log.debug(*args, **kwargs)
188        
189        
Note: See TracBrowser for help on using the browser.