154 lines
6.9 KiB
Python
154 lines
6.9 KiB
Python
|
# 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}
|
||
|
|
||
|
price_unit = self._compute_currency(order, price_unit, 'company_to_pricelist')
|
||
|
|
||
|
return {'success': True,
|
||
|
'price': price_unit,
|
||
|
'error_message': False,
|
||
|
'warning_message': False}
|
||
|
|
||
|
def _get_conversion_currencies(self, order, conversion):
|
||
|
company_currency = (self.company_id or self.env['res.company']._get_main_company()).currency_id
|
||
|
pricelist_currency = order.currency_id
|
||
|
|
||
|
if conversion == 'company_to_pricelist':
|
||
|
return company_currency, pricelist_currency
|
||
|
elif conversion == 'pricelist_to_company':
|
||
|
return pricelist_currency, company_currency
|
||
|
|
||
|
def _compute_currency(self, order, price, conversion):
|
||
|
from_currency, to_currency = self._get_conversion_currencies(order, conversion)
|
||
|
if from_currency.id == to_currency.id:
|
||
|
return price
|
||
|
return from_currency._convert(price, to_currency, order.company_id, order.date_order or fields.Date.today())
|
||
|
|
||
|
def _get_price_available(self, order):
|
||
|
self.ensure_one()
|
||
|
self = self.sudo()
|
||
|
order = order.sudo()
|
||
|
total = weight = volume = quantity = wv = 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
|
||
|
if line.product_id.type == "service":
|
||
|
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
|
||
|
wv += (line.product_id.weight or 0.0) * (line.product_id.volume or 0.0) * qty
|
||
|
quantity += qty
|
||
|
total = (order.amount_total or 0.0) - total_delivery
|
||
|
|
||
|
total = self._compute_currency(order, total, 'pricelist_to_company')
|
||
|
|
||
|
return self.with_context(wv=wv)._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': self.env.context.get('wv') or 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()
|