1
# -*- coding: utf-8 -*-
2
##############################################################################
4
# OpenERP, Open Source Management Solution
5
# Copyright (C) 2011 NovaPoint Group LLC (<http://www.novapointgroup.com>)
6
# Copyright (C) 2004-2010 OpenERP SA (<http://www.openerp.com>)
8
# This program is free software: you can redistribute it and/or modify
9
# it under the terms of the GNU General Public License as published by
10
# the Free Software Foundation, either version 3 of the License, or
11
# (at your option) any later version.
13
# This program is distributed in the hope that it will be useful,
14
# but WITHOUT ANY WARRANTY; without even the implied warranty of
15
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
# GNU General Public License for more details.
18
# You should have received a copy of the GNU General Public License
19
# along with this program. If not, see <http://www.gnu.org/licenses/>
21
##############################################################################
24
from osv import fields,osv
25
from xml.dom.minidom import Document
26
from tools.translate import _
32
from urlparse import urlparse
43
from base64 import b64decode
47
class logistic_company(osv.osv):
48
_inherit="logistic.company"
49
def _get_company_code(self, cr, user, context=None):
50
res = super(logistic_company, self)._get_company_code(cr, user, context=context)
51
res.append(('usps', 'USPS'))
56
'ship_company_code' : fields.selection(_get_company_code, 'Ship Company', method=True, required=True, size=64),
57
'usps_userid' : fields.char('User ID', size=128),
58
'usps_url_test' : fields.char('Test Url', size=512),
59
'usps_url' : fields.char('Production URL', size=512),
60
'usps_url_secure_test' : fields.char('Test Url SSL', size=512),
61
'usps_url_secure' : fields.char('Production URL SSL', size=512),
66
class stock_packages(osv.osv):
67
_inherit = "stock.packages"
69
'usps_confirmation_number': fields.char('USPS Confirm Number', size=64, readonly=True),
74
class stock_picking(osv.osv):
77
_inherit = "stock.picking"
78
def _get_company_code(self, cr, user, context=None):
79
res = super(stock_picking, self)._get_company_code(cr, user, context=context)
80
res.append(('usps', 'USPS'))
83
def _get_service_type_usps(self, cr, uid, context=None):
85
('First Class', 'First Class'),
86
('First Class HFP Commercial', 'First Class HFP Commercial'),
87
('FirstClassMailInternational', 'First Class Mail International'),
88
('Priority', 'Priority'),
89
('Priority Commercial', 'Priority Commercial'),
90
('Priority HFP Commercial', 'Priority HFP Commercial'),
91
('PriorityMailInternational', 'Priority Mail International'),
92
('Express', 'Express'),
93
('Express Commercial', 'Express Commercial'),
94
('Express SH', 'Express SH'),
95
('Express SH Commercial', 'Express SH Commercial'),
96
('Express HFP', 'Express HFP'),
97
('Express HFP Commercial', 'Express HFP Commercial'),
98
('ExpressMailInternational', 'Express Mail International'),
99
('ParcelPost', 'Parcel Post'),
100
('ParcelSelect', 'Parcel Select'),
101
('StandardMail', 'Standard Mail'),
102
('CriticalMail', 'Critical Mail'),
104
('Library', 'Library'),
106
('Online', 'Online'),
109
def _get_first_class_mail_type_usps(self, cr, uid, context=None):
111
('Letter', 'Letter'),
113
('Parcel', 'Parcel'),
114
('Postcard', 'Postcard'),
117
def _get_container_usps(self, cr, uid, context=None):
119
('Variable', 'Variable'),
121
('Letter', 'Letter'),
123
('Parcel', 'Parcel'),
124
('Large Parcel', 'Large Parcel'),
125
('Irregular Parcel', 'Irregular Parcel'),
126
('Oversized Parcel', 'Oversized Parcel'),
127
('Flat Rate Envelope', 'Flat Rate Envelope'),
128
('Padded Flat Rate Envelope', 'Padded Flat Rate Envelope'),
129
('Legal Flat Rate Envelope', 'Legal Flat Rate Envelope'),
130
('SM Flat Rate Envelope', 'SM Flat Rate Envelope'),
131
('Window Flat Rate Envelope', 'Window Flat Rate Envelope'),
132
('Gift Card Flat Rate Envelope', 'Gift Card Flat Rate Envelope'),
133
('Cardboard Flat Rate Envelope', 'Cardboard Flat Rate Envelope'),
134
('Flat Rate Box', 'Flat Rate Box'),
135
('SM Flat Rate Box', 'SM Flat Rate Box'),
136
('MD Flat Rate Box', 'MD Flat Rate Box'),
137
('LG Flat Rate Box', 'LG Flat Rate Box'),
138
('RegionalRateBoxA', 'RegionalRateBoxA'),
139
('RegionalRateBoxB', 'RegionalRateBoxB'),
140
('Rectangular', 'Rectangular'),
141
('Non-Rectangular', 'Non-Rectangular'),
144
def _get_size_usps(self, cr, uid, context=None):
146
('REGULAR', 'Regular'),
150
'ship_company_code': fields.selection(_get_company_code, 'Ship Company', method=True, size=64),
151
'usps_confirmation_number' : fields.char('Confirmation Number', size=64, readonly=True),
152
'usps_service_type' : fields.selection(_get_service_type_usps, 'Service Type', size=100),
153
'usps_package_location' : fields.selection([
154
('Front Door','Front Door'),
155
('Back Door','Back Door'),
156
('Side Door','Side Door'),
157
('Knock on Door/Ring Bell','Knock on Door/Ring Bell'),
158
('Mail Room','Mail Room'),
160
('Reception','Reception'),
161
('In/At Mailbox','In/At Mailbox'),
163
],'Package Location'),
164
'usps_first_class_mail_type' : fields.selection(_get_first_class_mail_type_usps, 'First Class Mail Type', size=50),
165
'usps_container' : fields.selection(_get_container_usps,'Container', size=100),
166
'usps_size' : fields.selection(_get_size_usps,'Size'),
167
'usps_length' : fields.float('Length'),
168
'usps_width' : fields.float('Width'),
169
'usps_height' : fields.float('Height'),
170
'usps_girth' : fields.float('Girth'),
173
'usps_service_type' : 'Priority',
174
'usps_package_location' : 'Front Door',
175
'usps_first_class_mail_type': 'Parcel',
176
'usps_size' : 'REGULAR',
177
'usps_container' : 'Variable',
180
def process_ship(self,cr, uid, ids, context=None):
181
do = self.browse(cr, uid, type(ids)==type([]) and ids[0] or ids, context=context)
182
user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
183
if do.ship_company_code != 'usps':
184
return super(stock_picking, self).process_ship(cr, uid, ids, context=context)
186
if not (do.logis_company and do.logis_company.ship_company_code=='usps'):
187
return super(stock_picking, self).process_ship(cr, uid, ids, context=context)
188
userid = do.logis_company.usps_userid
189
url = do.logis_company.test_mode and do.logis_company.usps_url_secure_test or do.logis_company.usps_url_secure
190
url_prd = do.logis_company.usps_url
191
url_prd_secure = do.logis_company.usps_url_secure
192
test=do.logis_company.test_mode
196
for package in do.packages_ids:
199
#@Changing to production URL SINCE DelivConfirmCertifyV3.0Request works only with production url and test data
200
url = do.logis_company.usps_url_secure
202
request_xml = """<DelivConfirmCertifyV3.0Request USERID="%(user_id)s">
204
<ImageParameters></ImageParameters>
205
<FromName>Joe Smith</FromName>
206
<FromFirm>ABD Corp.</FromFirm>
207
<FromAddress1>Apt. 3C</FromAddress1>
208
<FromAddress2>6406 Ivy Lane</FromAddress2>
209
<FromCity>Greenbelt</FromCity>
210
<FromState>MD</FromState>
211
<FromZip5>20770</FromZip5>
212
<FromZip4>1234</FromZip4>
213
<ToName>Tom Collins</ToName>
214
<ToFirm>XYZ Corp.</ToFirm>
215
<ToAddress1>Suite 4D</ToAddress1>
216
<ToAddress2>8 Wildwood Drive</ToAddress2>
217
<ToCity>Old Lyme</ToCity>
218
<ToState>CT</ToState>
219
<ToZip5>06371</ToZip5>
221
<WeightInOunces>1</WeightInOunces>
222
<ServiceType>Priority</ServiceType>
223
<SeparateReceiptPage></SeparateReceiptPage>
224
<POZipCode></POZipCode>
225
<ImageType>TIF</ImageType>
226
<LabelDate></LabelDate>
227
<CustomerRefNo></CustomerRefNo>
228
<AddressServiceRequested></AddressServiceRequested>
229
<SenderName></SenderName>
230
<SenderEMail></SenderEMail>
231
<RecipientName></RecipientName>
232
<RecipientEMail></RecipientEMail>
233
</DelivConfirmCertifyV3.0Request>
237
request_url = url + '?API=DelivConfirmCertifyV3&XML=' + request_xml
238
elif do.company_id.partner_id.address:
239
from_address = do.company_id.partner_id.address[0]
240
request_xml = """<DeliveryConfirmationV3.0Request USERID="%(user_id)s">
243
<FromName>%(from_name)s</FromName>
244
<FromFirm>%(from_firm)s</FromFirm>
246
<FromAddress2>%(from_address2)s</FromAddress2>
247
<FromCity>%(from_city)s</FromCity>
248
<FromState>%(from_state)s</FromState>
249
<FromZip5>%(from_zip5)s</FromZip5>
250
<FromZip4>%(from_zip4)s</FromZip4>
251
<ToName>%(to_name)s</ToName>
252
<ToFirm>%(to_firm)s</ToFirm>
253
<ToAddress1>%(to_address1)s</ToAddress1>
254
<ToAddress2>%(to_address2)s</ToAddress2>
255
<ToCity>%(to_city)s</ToCity>
256
<ToState>%(to_state)s</ToState>
257
<ToZip5>%(to_zip5)s</ToZip5>
258
<ToZip4>%(to_zip4)s</ToZip4>
259
<WeightInOunces>%(weight)s</WeightInOunces>
260
<ServiceType>%(service_type)s</ServiceType>
261
<POZipCode></POZipCode>
262
<ImageType>TIF</ImageType>
263
<LabelDate></LabelDate>
264
<CustomerRefNo></CustomerRefNo>
265
<AddressServiceRequested>TRUE</AddressServiceRequested>
266
</DeliveryConfirmationV3.0Request>
269
'from_name' : from_address.name,
271
'from_address2' : from_address.street or '',
272
'from_city' : from_address.city or '',
273
'from_state' : from_address.state_id and from_address.state_id.code or '',
274
'from_zip5' : from_address.zip_id and from_address.zip_id.zipcode or '',
275
'from_zip4' : from_address.zip or '',
276
'to_name' : do.address_id.name ,
278
'to_address1' : do.address_id.street,
279
'to_address2' : do.address_id.street2,
280
'to_city' : do.address_id.city,
281
'to_state' : do.address_id.state_id and do.address_id.state_id.code or '',
282
'to_zip5' : do.address_id.zip_id and do.address_id.zip_id.zipcode or '',
283
'to_zip4' : do.address_id.zip,
284
'weight' : package.weight,
285
'service_type' : do.usps_service_type,
287
request_url = url + '?API=DeliveryConfirmationV3&XML=' + request_xml
290
f = urllib.urlopen(request_url)
291
from xml.dom.minidom import parse, parseString
294
str_response = f.read()
295
xml_response = parseString(str_response)
296
xml_dic = xml2dic.main(str_response)
298
if 'Error' in xml_dic.keys():
300
for item in xml_dic.get('Error'):
302
if item.get('Number'):
304
str_error = str_error+ "\n----------------------"
305
str_error= str_error + "\nNumber : " + item['Number']
306
if item.get('Description'):
307
str_error = str_error + "\nDescription : " + item['Description']
310
confirmation_number = xml_dic['DelivConfirmCertifyV3.0Response'][0]['DeliveryConfirmationNumber']
311
label_data = xml_dic['DelivConfirmCertifyV3.0Response'][1]['DeliveryConfirmationLabel']
312
#logo = binascii.b2a_base64(str(b64decode(label_data)))
313
#logo = str(b64decode(label_data))
315
logo=base64.decodestring(label_data)
319
dir_temp = tempfile.gettempdir()
321
f = open(dir_temp + '/usps.tif', 'w+')
327
if os.name == 'posix' or 'nt':
329
os.system("tiffcp -c none "+ dir_temp +"/usps.tif " + dir_temp +"/usps_temp.tif")
332
str_error = "Please install tiffcp."
334
im = Image.open(dir_temp + '/usps_temp.tif')
335
im.thumbnail(im.size)
336
im.save(dir_temp+'/usps_temp.jpg', "JPEG", quality=100)
337
label_from_file=open(dir_temp+'/usps_temp.jpg','rb')
338
label_image=base64.encodestring(label_from_file.read())
340
self.pool.get('stock.packages').write(cr, uid, [package.id], {'logo': label_image, 'tracking_no': confirmation_number, 'usps_confirmation_number': confirmation_number,'ship_message': 'Shipment has processed'})
347
self.pool.get('stock.packages').write(cr, uid, do.id, {'ship_message': str_error}, context=context)
352
self.write(cr, uid, do.id, {'ship_state':'ready_pick','ship_message': 'Shipment has been processed.'}, context=context)
354
'type': 'ir.actions.report.xml',
355
'report_name':'multiple.label.print',
357
'model':'stock.picking',
358
'id': ids and ids[0] or False,
359
'ids': ids and ids or [],
365
self.write(cr, uid, do.id, {'ship_message': 'Error occured on processing some of packages, for details please see the status packages.'}, context=context)
366
#@todo: raise appropriate error msg
367
raise osv.except_osv(_('Error'), _('%s' % ('No package lines are created for shippment process.')))
371
def process_void(self,cr, uid, ids, context=None):
373
do = self.browse(cr, uid, type(ids)==type([]) and ids[0] or ids, context=context)
374
if do.ship_company_code != 'usps':
375
return super(stock_picking, self).process_void(cr, uid, ids, context=context)
377
if not (do.logis_company and do.logis_company.ship_company_code=='usps'):
378
return super(stock_picking, self).process_void(cr, uid, ids, context=context)
380
userid = do.logis_company.usps_userid
381
url = do.logis_company.test_mode and do.logis_company.usps_url_secure_test or do.logis_company.usps_url_secure
382
url_prd = do.logis_company.usps_url
383
url_prd_secure = do.logis_company.usps_url_secure
384
test=do.logis_company.test_mode
389
for pack in do.packages_ids:
391
url = test and do.logis_company.usps_url_secure_test or do.logis_company.usps_url_secure
392
url_sec = test and do.logis_company.usps_url_secure_test or do.logis_company.usps_url_secure
394
request_xml = """<CarrierPickupCancelRequest USERID="%(user_id)s">
395
<FirmName>ABC Corp.</FirmName>
396
<SuiteOrApt>Suite 777</SuiteOrApt>
397
<Address2>1390 Market Street</Address2>
398
<Urbanization></Urbanization>
403
<ConfirmationNumber>WTC123456789</ConfirmationNumber>
404
</CarrierPickupCancelRequest>
405
"""%{'user_id': do.logis_company.usps_userid}
407
request_xml = """<CarrierPickupCancelRequest USERID="%(user_id)">
408
<FirmName>ABC Corp.</FirmName>
409
<SuiteOrApt>Suite 777</SuiteOrApt>
410
<Address2>1390 Market Street</Address2>
411
<Urbanization></Urbanization>
416
<ConfirmationNumber>%(confirmation_number)</ConfirmationNumber>
417
</CarrierPickupCancelRequest>
419
'user_id': do.logis_company.usps_userid,
420
'confirmation_number' : pack.tracking_no,
422
request_url = url + '?API=CarrierPickupCancel&XML=' + request_xml
425
f = urllib.urlopen(request_url)
426
from xml.dom.minidom import parse, parseString
429
str_response = f.read()
431
self.pool.get('stock.packages').write(cr, uid, pack.id, {'ship_message': str(Exception)}, context=context)
433
print "Shipment Cancel response :", str_response
435
xml_response = parseString(str_response)
436
xml_dic = xml2dic.main(str_response)
438
if 'Error' in xml_dic.keys():
440
for item in xml_dic.get('Error'):
441
self.pool.get('stock.packages').write(cr, uid, pack.id, {'ship_message': str_error}, context=context)
444
self.pool.get('stock.packages').write(cr, uid, pack.id, {
445
'negotiated_rates' : 0.00,
446
'shipment_identific_no' :'',
450
'ship_message' : 'Shipment Cancelled'}, context=context)
453
self.write(cr, uid, do.id, {'ship_state' :'draft', 'ship_message' : 'Shipment has been cancelled.'}, context=context)
455
self.write(cr, uid, do.id, { 'ship_message' : 'Cancellation of some of shipment has failed, please check the status of pakages.'}, context=context)