forked from OCA/delivery-carrier
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathstock_picking.py
248 lines (201 loc) · 7.52 KB
/
stock_picking.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
# coding: utf-8
# @author Raphael Reverdy <raphael.reverdy@akretion.com>
# David BEAL <david.beal@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from datetime import datetime, timedelta
import logging
from odoo import models, fields, api
from odoo.tools.translate import _
from odoo.exceptions import UserError
from ..decorator import implemented_by_carrier
_logger = logging.getLogger(__name__)
try:
from roulier import roulier
except ImportError:
_logger.debug('Cannot `import roulier`.')
class StockPicking(models.Model):
_inherit = 'stock.picking'
customs_category = fields.Selection(
selection=[
('gift', _("Gift")),
('sample', _("Samples")),
('commercial', _("Commercial Goods")),
('document', _("Documents")),
('other', _("Other")),
('return', _("Goods return")),
],
default='commercial',
help="Type of sending for the customs")
# base_delivery_carrier_label API implementataion
# def generate_default_label(self, package_ids=None):
# useless method
# API:
@implemented_by_carrier
def _get_sender(self, package):
pass
@implemented_by_carrier
def _get_receiver(self, package):
pass
@implemented_by_carrier
def _get_shipping_date(self, package):
pass
@implemented_by_carrier
def _get_account(self, package):
pass
@implemented_by_carrier
def _get_auth(self, package):
pass
@implemented_by_carrier
def _get_service(self, package):
pass
@implemented_by_carrier
def _convert_address(self, partner):
pass
# End of API.
# Implementations for base_delivery_carrier_label
@api.multi
def _is_roulier(self):
self.ensure_one()
return self.carrier_type in roulier.get_carriers()
@api.multi
def generate_labels(self, package_ids=None):
"""See base_delivery_carrier_label/models/stock_picking.py."""
# entry point
self.ensure_one()
if self._is_roulier():
return self._roulier_generate_labels()
_super = super(StockPicking, self)
return _super.generate_labels(package_ids=package_ids)
@api.multi
def generate_shipping_labels(self, package_ids=None):
"""See base_delivery_carrier_label/stock.py."""
self.ensure_one()
if self._is_roulier():
raise UserError(_("Don't call me directly"))
_super = super(StockPicking, self)
return _super.generate_shipping_labels(package_ids=package_ids)
@api.multi
def _roulier_generate_labels(self):
"""Create as many labels as package_ids or in self."""
self.ensure_one()
packages = self._get_packages_from_picking()
if not packages:
# It's not our responsibility to create the packages
raise UserError(_('No package found for this picking'))
self.number_of_packages = len(packages)
self.carrier_tracking_ref = True # display button in view
return packages._generate_labels(self)
# Default implementations of _roulier_*()
def _roulier_get_auth(self, package):
"""Login/password of the carrier account.
Returns:
a dict with login and password keys
"""
account = self._get_account(package)
auth = {
'login': account.login,
'password': account.get_password(),
}
return auth
def _roulier_get_account(self, package):
"""Return an 'account'.
By default, the first account encoutered for this type.
Depending on your case, you may store it on the picking or
compute it from your business rules.
Accounts are resolved at runtime (can be != for dev/prod)
"""
keychain = self.env['keychain.account']
if self.env.user.has_group('stock.group_stock_user'):
retrieve = keychain.suspend_security().retrieve
else:
retrieve = keychain.retrieve
accounts = retrieve(
[['namespace', '=', 'roulier_%s' % self.carrier_type]])
return accounts[0]
def _roulier_get_sender(self, package):
"""Sender of the picking (for the label).
Return:
(res.partner)
"""
return self.company_id.partner_id
def _roulier_get_receiver(self, package):
"""The guy whom the shippment is for.
At home or at a distribution point, it's always
the same receiver address.
Return:
(res.partner)
"""
return self.partner_id
def _roulier_get_shipping_date(self, package):
"""Choose a shipping date.
By default, it's tomorrow."""
tomorrow = datetime.now() + timedelta(1)
return tomorrow.strftime('%Y-%m-%d')
def _roulier_convert_address(self, partner):
"""Convert a partner to an address for roulier.
params:
partner: a res.partner
return:
dict
"""
address = {}
extract_fields = [
'company', 'name', 'zip', 'city', 'phone', 'mobile',
'email', 'street2']
for elm in extract_fields:
if elm in partner:
# because a value can't be None in odoo's ORM
# you don't want to mix (bool) False and None
if partner._fields[elm].type != fields.Boolean.type:
if partner[elm]:
address[elm] = partner[elm]
# else:
# it's a None: nothing to do
else: # it's a boolean: keep the value
address[elm] = partner[elm]
if not address.get('company', False) and partner.parent_id.is_company:
address['company'] = partner.parent_id.name
# Roulier needs street1 (mandatory) not street
address['street1'] = partner.street
# Codet ISO 3166-1-alpha-2 (2 letters code)
address['country'] = partner.country_id.code
for tel in ['mobile', 'phone']:
if address.get(tel):
address[tel] = address[tel].replace(u'\u00A0', '')
address['phone'] = address.get('mobile', address.get('phone'))
return address
def _roulier_get_service(self, package):
"""Return a basic dict.
The carrier implementation may add stuff
like agency or options.
return:
dict
"""
shipping_date = self._get_shipping_date(package)
service = {
'product': self.carrier_code,
'shippingDate': shipping_date,
}
return service
@api.multi
def open_website_url(self):
"""Open tracking page.
More than 1 tracking number: display a list of packages
Else open directly the tracking page
"""
self.ensure_one()
if not self._is_roulier():
return super(StockPicking, self).open_website_url()
packages = self._get_packages_from_picking()
if len(packages) == 0:
raise UserError(_('No packages found for this picking'))
elif len(packages) == 1:
return packages.open_website_url() # shortpath
# display a list of pickings
action = self.env.ref('stock.action_package_view').read()[0]
action['res_id'] = packages.ids
action['domain'] = "[('id', 'in', [%s])]" % (
",".join(map(str, packages.ids))
)
action['context'] = "{'picking_id': %s }" % str(self.id)
return action