forked from OCA/delivery-carrier
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsale_order.py
235 lines (207 loc) · 9.15 KB
/
sale_order.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
# Copyright 2018 Tecnativa - Pedro M. Baeza
# Copyright 2021 Tecnativa - Carlos Roca
# Copyright 2024 Jacques-Etienne Baudoux (BCIM) <je@bcim.be>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import api, fields, models
class SaleOrder(models.Model):
_inherit = "sale.order"
# Migration note: This field is not used anymore and can be dropped in later versions
available_carrier_ids = fields.Many2many(
comodel_name="delivery.carrier",
compute="_compute_available_carrier_ids",
)
@api.depends("partner_shipping_id")
def _compute_available_carrier_ids(self):
"""We want to apply the same carriers filter in the header as in the wizard"""
for sale in self:
wizard = self.env["choose.delivery.carrier"].new({"order_id": sale.id})
sale.available_carrier_ids = wizard.available_carrier_ids._origin
# End migration note
# Migration Note 17.0: move this section to module sale_order_carrier_auto_assign
def _set_delivery_carrier(self, set_delivery_line=True):
for order in self:
delivery_wiz_action = order.action_open_delivery_wizard()
delivery_wiz_context = delivery_wiz_action.get("context", {})
if not delivery_wiz_context.get("default_carrier_id"):
continue
delivery_wiz = (
self.env[delivery_wiz_action.get("res_model")]
.with_context(**delivery_wiz_context)
.new({})
)
# Do not override carrier
if order.carrier_id:
delivery_wiz.carrier_id = order.carrier_id
# If the carrier isn't allowed, we won't default to it
if (
delivery_wiz.carrier_id
not in delivery_wiz.available_carrier_ids._origin
):
continue
if not set_delivery_line or order.is_all_service:
# Only set the carrier
if order.carrier_id != delivery_wiz.carrier_id:
order.carrier_id = delivery_wiz.carrier_id
else:
delivery_wiz._get_shipment_rate()
delivery_wiz.button_confirm()
@api.onchange("partner_id", "partner_shipping_id")
def _add_delivery_carrier_on_partner_change(self):
partner = self.partner_shipping_id or self.partner_id
if not partner:
return
if self.company_id.sale_auto_assign_carrier_on_create:
self._set_delivery_carrier(set_delivery_line=False)
def _is_auto_set_carrier_on_create(self):
self.ensure_one()
if self.state not in ("draft", "sent"):
return False
return self.company_id.sale_auto_assign_carrier_on_create
# End migration note
def _get_param_auto_add_delivery_line(self):
# When we have the context 'website_id' it means that we are doing the order from
# e-commerce. So we don't want to add the delivery line automatically.
if self.env.context.get("website_id"):
return False
return (
self.env["ir.config_parameter"]
.sudo()
.get_param("delivery_auto_refresh.auto_add_delivery_line")
)
def _update_delivery_line(self, delivery_line, price_unit):
"""Update the existing delivery line"""
values = self._prepare_delivery_line_vals(self.carrier_id, price_unit)
new_vals = {}
for f, val in values.items():
field_def = delivery_line._fields.get(f)
if isinstance(field_def, (fields.One2many, fields.Many2many)):
# Tax is set with a SET command
clear = update = False
for cmd in val:
if cmd[0] == fields.Command.SET:
if delivery_line[f].ids != cmd[2]:
update = True
else:
clear = True
if clear:
new_vals[f] = [fields.Command.CLEAR] + val
elif update:
new_vals[f] = val
elif isinstance(field_def, fields.Many2one):
if delivery_line[f].id != val:
new_vals[f] = val
elif f == "sequence":
# sequence is last sequence + 1. As the delivery line already
# exists, substract 1
if delivery_line[f] != val - 1:
new_vals[f] = val
elif delivery_line[f] != val:
new_vals[f] = val
if new_vals:
delivery_line.write(new_vals)
def _auto_refresh_delivery(self):
self.ensure_one()
if self.env.context.get("auto_refresh_delivery"):
return
if not self._get_param_auto_add_delivery_line():
return
if self.state not in ("draft", "sent"):
return
self = self.with_context(auto_refresh_delivery=True)
if not self.carrier_id:
self._set_delivery_carrier()
if not self.carrier_id or self.is_all_service:
self._remove_delivery_line()
else:
price_unit = self.carrier_id.rate_shipment(self)["price"]
delivery_lines = self.order_line.filtered("is_delivery")
if not delivery_lines:
self._create_delivery_line(self.carrier_id, price_unit)
elif len(delivery_lines) > 1:
delivery_discount = delivery_lines[-1:].discount
self._remove_delivery_line()
sol = self._create_delivery_line(self.carrier_id, price_unit)
if delivery_discount and sol:
sol.discount = delivery_discount
else:
self._update_delivery_line(delivery_lines[0], price_unit)
if self.recompute_delivery_price:
self.recompute_delivery_price = False
@api.model_create_multi
def create(self, vals_list):
# Prevent to refresh delivery in the call to super
orders = (
super(SaleOrder, self.with_context(auto_refresh_delivery=True))
.create(vals_list)
.with_context(auto_refresh_delivery=False)
)
for order in orders:
# Migration Note 17.0: move this to module sale_order_carrier_auto_assign
if not order.carrier_id and order._is_auto_set_carrier_on_create():
order._set_delivery_carrier()
# End migration note
order._auto_refresh_delivery()
return orders
def write(self, vals):
# Prevent to refresh delivery in the call to super
res = super(SaleOrder, self.with_context(auto_refresh_delivery=True)).write(
vals
)
for order in self:
order._auto_refresh_delivery()
return res
def set_delivery_line(self, carrier, amount):
if self._get_param_auto_add_delivery_line() and self.state in ("draft", "sent"):
# This will trigger an _auto_refresh_delivery in write
self.carrier_id = carrier.id
else:
return super().set_delivery_line(carrier, amount)
def _is_delivery_line_voidable(self):
"""If the picking is returned before being invoiced, like when the picking
is delivered but immediately return because the customer refused the order,
so no delivery charges should be applied."""
# There are invoiceable lines so there's no full return or the lines
# were not set to refund.
qty_delivered = sum(
self.order_line.filtered(
lambda x: not x.is_delivery and x.product_id.type != "service"
).mapped("qty_delivered")
)
# There must be validated pickings
pickings = self.picking_ids.filtered(lambda x: x.state == "done")
# If there aren't delivery lines or the order is a quotation there's
# nothing to be done either. If there are more than one delivery lines
# we won't be doing anything as well.
if (
self.state not in ("done", "sale")
or self.invoice_ids
or not self.order_line.filtered("is_delivery")
or len(self.order_line.filtered("is_delivery")) > 1
or qty_delivered
or not pickings
):
return False
return True
class SaleOrderLine(models.Model):
_inherit = "sale.order.line"
def _get_protected_fields(self):
# Avoid locked orders validation error when voiding the delivery line
fields = super()._get_protected_fields()
if self.env.context.get("delivery_auto_refresh_override_locked"):
return [x for x in fields if x not in ["product_uom_qty", "price_unit"]]
return fields
@api.model_create_multi
def create(self, vals_list):
lines = super(
SaleOrderLine, self.with_context(auto_refresh_delivery=True)
).create(vals_list)
for order in lines.order_id:
order._auto_refresh_delivery()
return lines
def write(self, vals):
res = super(SaleOrderLine, self.with_context(auto_refresh_delivery=True)).write(
vals
)
for order in self.order_id:
order._auto_refresh_delivery()
return res