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
204 lines
8.8 KiB
Python
204 lines
8.8 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.exceptions import UserError
|
|
|
|
|
|
class SaleOrder(models.Model):
|
|
_inherit = 'sale.order'
|
|
|
|
carrier_id = fields.Many2one('delivery.carrier', string="Delivery Method", domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]", help="Fill this field if you plan to invoice the shipping based on picking.")
|
|
delivery_message = fields.Char(readonly=True, copy=False)
|
|
delivery_rating_success = fields.Boolean(copy=False)
|
|
delivery_set = fields.Boolean(compute='_compute_delivery_state')
|
|
recompute_delivery_price = fields.Boolean('Delivery cost should be recomputed')
|
|
is_all_service = fields.Boolean("Service Product", compute="_compute_is_service_products")
|
|
shipping_weight = fields.Float("Shipping Weight", compute="_compute_shipping_weight", store=True, readonly=False)
|
|
|
|
@api.depends('order_line')
|
|
def _compute_is_service_products(self):
|
|
for so in self:
|
|
so.is_all_service = all(line.product_id.type == 'service' for line in so.order_line.filtered(lambda x: not x.display_type))
|
|
|
|
def _compute_amount_total_without_delivery(self):
|
|
self.ensure_one()
|
|
delivery_cost = sum([l.price_total for l in self.order_line if l.is_delivery])
|
|
return self.env['delivery.carrier']._compute_currency(self, self.amount_total - delivery_cost, 'pricelist_to_company')
|
|
|
|
@api.depends('order_line')
|
|
def _compute_delivery_state(self):
|
|
for order in self:
|
|
order.delivery_set = any(line.is_delivery for line in order.order_line)
|
|
|
|
@api.onchange('order_line', 'partner_id', 'partner_shipping_id')
|
|
def onchange_order_line(self):
|
|
self.ensure_one()
|
|
delivery_line = self.order_line.filtered('is_delivery')
|
|
if delivery_line:
|
|
self.recompute_delivery_price = True
|
|
|
|
def _remove_delivery_line(self):
|
|
"""Remove delivery products from the sales orders"""
|
|
delivery_lines = self.order_line.filtered("is_delivery")
|
|
if not delivery_lines:
|
|
return
|
|
to_delete = delivery_lines.filtered(lambda x: x.qty_invoiced == 0)
|
|
if not to_delete:
|
|
raise UserError(
|
|
_('You can not update the shipping costs on an order where it was already invoiced!\n\nThe following delivery lines (product, invoiced quantity and price) have already been processed:\n\n')
|
|
+ '\n'.join(['- %s: %s x %s' % (line.product_id.with_context(display_default_code=False).display_name, line.qty_invoiced, line.price_unit) for line in delivery_lines])
|
|
)
|
|
to_delete.unlink()
|
|
|
|
def set_delivery_line(self, carrier, amount):
|
|
self._remove_delivery_line()
|
|
for order in self:
|
|
order.carrier_id = carrier.id
|
|
order._create_delivery_line(carrier, amount)
|
|
return True
|
|
|
|
def action_open_delivery_wizard(self):
|
|
view_id = self.env.ref('delivery.choose_delivery_carrier_view_form').id
|
|
if self.env.context.get('carrier_recompute'):
|
|
name = _('Update shipping cost')
|
|
carrier = self.carrier_id
|
|
else:
|
|
name = _('Add a shipping method')
|
|
carrier = (
|
|
self.with_company(self.company_id).partner_shipping_id.property_delivery_carrier_id
|
|
or self.with_company(self.company_id).partner_shipping_id.commercial_partner_id.property_delivery_carrier_id
|
|
)
|
|
return {
|
|
'name': name,
|
|
'type': 'ir.actions.act_window',
|
|
'view_mode': 'form',
|
|
'res_model': 'choose.delivery.carrier',
|
|
'view_id': view_id,
|
|
'views': [(view_id, 'form')],
|
|
'target': 'new',
|
|
'context': {
|
|
'default_order_id': self.id,
|
|
'default_carrier_id': carrier.id,
|
|
'default_total_weight': self._get_estimated_weight()
|
|
}
|
|
}
|
|
|
|
def _create_delivery_line(self, carrier, price_unit):
|
|
SaleOrderLine = self.env['sale.order.line']
|
|
context = {}
|
|
if self.partner_id:
|
|
# set delivery detail in the customer language
|
|
context['lang'] = self.partner_id.lang
|
|
carrier = carrier.with_context(lang=self.partner_id.lang)
|
|
|
|
# Apply fiscal position
|
|
taxes = carrier.product_id.taxes_id.filtered(lambda t: t.company_id.id == self.company_id.id)
|
|
taxes_ids = taxes.ids
|
|
if self.partner_id and self.fiscal_position_id:
|
|
taxes_ids = self.fiscal_position_id.map_tax(taxes).ids
|
|
|
|
# Create the sales order line
|
|
|
|
if carrier.product_id.description_sale:
|
|
so_description = '%s: %s' % (carrier.name,
|
|
carrier.product_id.description_sale)
|
|
else:
|
|
so_description = carrier.name
|
|
values = {
|
|
'order_id': self.id,
|
|
'name': so_description,
|
|
'product_uom_qty': 1,
|
|
'product_uom': carrier.product_id.uom_id.id,
|
|
'product_id': carrier.product_id.id,
|
|
'tax_id': [(6, 0, taxes_ids)],
|
|
'is_delivery': True,
|
|
}
|
|
if carrier.invoice_policy == 'real':
|
|
values['price_unit'] = 0
|
|
values['name'] += _(' (Estimated Cost: %s )', self._format_currency_amount(price_unit))
|
|
else:
|
|
values['price_unit'] = price_unit
|
|
if carrier.free_over and self.currency_id.is_zero(price_unit) :
|
|
values['name'] += '\n' + _('Free Shipping')
|
|
if self.order_line:
|
|
values['sequence'] = self.order_line[-1].sequence + 1
|
|
sol = SaleOrderLine.sudo().create(values)
|
|
del context
|
|
return sol
|
|
|
|
def _format_currency_amount(self, amount):
|
|
pre = post = u''
|
|
if self.currency_id.position == 'before':
|
|
pre = u'{symbol}\N{NO-BREAK SPACE}'.format(symbol=self.currency_id.symbol or '')
|
|
else:
|
|
post = u'\N{NO-BREAK SPACE}{symbol}'.format(symbol=self.currency_id.symbol or '')
|
|
return u' {pre}{0}{post}'.format(amount, pre=pre, post=post)
|
|
|
|
@api.depends('order_line.is_delivery', 'order_line.is_downpayment')
|
|
def _compute_invoice_status(self):
|
|
super()._compute_invoice_status()
|
|
for order in self:
|
|
if order.invoice_status in ['no', 'invoiced']:
|
|
continue
|
|
order_lines = order.order_line.filtered(lambda x: not x.is_delivery and not x.is_downpayment and not x.display_type and x.invoice_status != 'invoiced')
|
|
if all(line.product_id.invoice_policy == 'delivery' and line.invoice_status == 'no' for line in order_lines):
|
|
order.invoice_status = 'no'
|
|
|
|
@api.depends('order_line.product_uom_qty', 'order_line.product_uom')
|
|
def _compute_shipping_weight(self):
|
|
for order in self:
|
|
order.shipping_weight = order._get_estimated_weight()
|
|
|
|
def _get_estimated_weight(self):
|
|
self.ensure_one()
|
|
if self.delivery_set:
|
|
return self.shipping_weight
|
|
weight = 0.0
|
|
for order_line in self.order_line.filtered(lambda l: l.product_id.type in ['product', 'consu'] and not l.is_delivery and not l.display_type):
|
|
weight += order_line.product_qty * order_line.product_id.weight
|
|
return weight
|
|
|
|
|
|
class SaleOrderLine(models.Model):
|
|
_inherit = 'sale.order.line'
|
|
|
|
is_delivery = fields.Boolean(string="Is a Delivery", default=False)
|
|
product_qty = fields.Float(compute='_compute_product_qty', string='Product Qty', digits='Product Unit of Measure')
|
|
recompute_delivery_price = fields.Boolean(related='order_id.recompute_delivery_price')
|
|
|
|
def _is_not_sellable_line(self):
|
|
return self.is_delivery or super(SaleOrderLine, self)._is_not_sellable_line()
|
|
|
|
@api.depends('product_id', 'product_uom', 'product_uom_qty')
|
|
def _compute_product_qty(self):
|
|
for line in self:
|
|
if not line.product_id or not line.product_uom or not line.product_uom_qty:
|
|
line.product_qty = 0.0
|
|
continue
|
|
line.product_qty = line.product_uom._compute_quantity(line.product_uom_qty, line.product_id.uom_id)
|
|
|
|
def unlink(self):
|
|
for line in self:
|
|
if line.is_delivery:
|
|
line.order_id.carrier_id = False
|
|
return super(SaleOrderLine, self).unlink()
|
|
|
|
def _is_delivery(self):
|
|
self.ensure_one()
|
|
return self.is_delivery
|
|
|
|
# override to allow deletion of delivery line in a confirmed order
|
|
def _check_line_unlink(self):
|
|
"""
|
|
Extend the allowed deletion policy of SO lines.
|
|
|
|
Lines that are delivery lines can be deleted from a confirmed order.
|
|
|
|
:rtype: recordset sale.order.line
|
|
:returns: set of lines that cannot be deleted
|
|
"""
|
|
|
|
undeletable_lines = super()._check_line_unlink()
|
|
return undeletable_lines.filtered(lambda line: not line.is_delivery)
|