Previously, when adding shipment to sale order, the user did not know the total weight of the order when getting rate of shipping method. This commit shows the total order weight to the user with the ability to set it to any value to get the rate with. Taskid: 2797613 Part-of: odoo/odoo#96660
157 lines
7.1 KiB
Python
157 lines
7.1 KiB
Python
# -*- 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}
|
|
|
|
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):
|
|
if conversion == 'company_to_pricelist':
|
|
from_currency, to_currency = order.company_id.currency_id, order.pricelist_id.currency_id
|
|
elif conversion == 'pricelist_to_company':
|
|
from_currency, to_currency = order.currency_id, order.company_id.currency_id
|
|
|
|
return from_currency, to_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 = 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
|
|
quantity += qty
|
|
total = (order.amount_total or 0.0) - total_delivery
|
|
|
|
total = self._compute_currency(order, total, 'pricelist_to_company')
|
|
# weight is either,
|
|
# 1- weight chosen by user in choose.delivery.carrier wizard passed by context
|
|
# 2- saved weight to use on sale order
|
|
# 3- total order line weight as fallback
|
|
weight = self.env.context.get('order_weight') or order.shipping_weight or weight
|
|
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()
|