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
|
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import models, fields, api, _
from odoo.tools.safe_eval import safe_eval
from odoo.exceptions import UserError, ValidationError
class PriceRule(models.Model):
_name = "delivery.price.rule"
_description = "Delivery Price Rules"
_order = 'sequence, list_price, id'
@api.depends('variable', 'operator', 'max_value', 'list_base_price', 'list_price', 'variable_factor')
def _compute_name(self):
for rule in self:
name = 'if %s %s %.02f then' % (rule.variable, rule.operator, rule.max_value)
if rule.list_base_price and not rule.list_price:
name = '%s fixed price %.02f' % (name, rule.list_base_price)
elif rule.list_price and not rule.list_base_price:
name = '%s %.02f times %s' % (name, rule.list_price, rule.variable_factor)
else:
name = '%s fixed price %.02f plus %.02f times %s' % (name, rule.list_base_price, rule.list_price, rule.variable_factor)
rule.name = name
name = fields.Char(compute='_compute_name')
sequence = fields.Integer(required=True, default=10)
carrier_id = fields.Many2one('delivery.carrier', 'Carrier', required=True, ondelete='cascade')
variable = fields.Selection([('weight', 'Weight'), ('volume', 'Volume'), ('wv', 'Weight * Volume'), ('price', 'Price'), ('quantity', 'Quantity')], required=True, default='weight')
operator = fields.Selection([('==', '='), ('<=', '<='), ('<', '<'), ('>=', '>='), ('>', '>')], required=True, default='<=')
max_value = fields.Float('Maximum Value', required=True)
list_base_price = fields.Float(string='Sale Base Price', digits='Product Price', required=True, default=0.0)
list_price = fields.Float('Sale Price', digits='Product Price', required=True, default=0.0)
variable_factor = fields.Selection([('weight', 'Weight'), ('volume', 'Volume'), ('wv', 'Weight * Volume'), ('price', 'Price'), ('quantity', 'Quantity')], 'Variable Factor', required=True, default='weight')
class ProviderGrid(models.Model):
_inherit = 'delivery.carrier'
delivery_type = fields.Selection(selection_add=[
('base_on_rule', 'Based on Rules'),
], ondelete={'base_on_rule': lambda recs: recs.write({
'delivery_type': 'fixed', 'fixed_price': 0,
})})
price_rule_ids = fields.One2many('delivery.price.rule', 'carrier_id', 'Pricing Rules', copy=True)
def base_on_rule_rate_shipment(self, order):
carrier = self._match_address(order.partner_shipping_id)
if not carrier:
return {'success': False,
'price': 0.0,
'error_message': _('Error: this delivery method is not available for this address.'),
'warning_message': False}
try:
price_unit = self._get_price_available(order)
except UserError as e:
return {'success': False,
'price': 0.0,
'error_message': e.args[0],
'warning_message': False}
if order.company_id.currency_id.id != order.pricelist_id.currency_id.id:
price_unit = order.company_id.currency_id._convert(
price_unit, order.pricelist_id.currency_id, order.company_id, order.date_order or fields.Date.today())
return {'success': True,
'price': price_unit,
'error_message': False,
'warning_message': False}
def _get_price_available(self, order):
self.ensure_one()
self = self.sudo()
order = order.sudo()
total = weight = volume = quantity = 0
total_delivery = 0.0
for line in order.order_line:
if line.state == 'cancel':
continue
if line.is_delivery:
total_delivery += line.price_total
if not line.product_id or line.is_delivery:
continue
qty = line.product_uom._compute_quantity(line.product_uom_qty, line.product_id.uom_id)
weight += (line.product_id.weight or 0.0) * qty
volume += (line.product_id.volume or 0.0) * qty
quantity += qty
total = (order.amount_total or 0.0) - total_delivery
total = order.currency_id._convert(
total, order.company_id.currency_id, order.company_id, order.date_order or fields.Date.today())
return self._get_price_from_picking(total, weight, volume, quantity)
def _get_price_dict(self, total, weight, volume, quantity):
'''Hook allowing to retrieve dict to be used in _get_price_from_picking() function.
Hook to be overridden when we need to add some field to product and use it in variable factor from price rules. '''
return {
'price': total,
'volume': volume,
'weight': weight,
'wv': volume * weight,
'quantity': quantity
}
def _get_price_from_picking(self, total, weight, volume, quantity):
price = 0.0
criteria_found = False
price_dict = self._get_price_dict(total, weight, volume, quantity)
if self.free_over and total >= self.amount:
return 0
for line in self.price_rule_ids:
test = safe_eval(line.variable + line.operator + str(line.max_value), price_dict)
if test:
price = line.list_base_price + line.list_price * price_dict[line.variable_factor]
criteria_found = True
break
if not criteria_found:
raise UserError(_("No price rule matching this order; delivery cost cannot be computed."))
return price
def base_on_rule_send_shipping(self, pickings):
res = []
for p in pickings:
carrier = self._match_address(p.partner_id)
if not carrier:
raise ValidationError(_('There is no matching delivery rule.'))
res = res + [{'exact_price': p.carrier_id._get_price_available(p.sale_id) if p.sale_id else 0.0, # TODO cleanme
'tracking_number': False}]
return res
def base_on_rule_get_tracking_link(self, picking):
return False
def base_on_rule_cancel_shipment(self, pickings):
raise NotImplementedError()
|