[IMP] delivery: invoicing policy of delivery price
The delivery carriers get a new field to specify the invoicing policy. The two cases are : - Estimated cost: the delivery price is charged directly on the sale order based on the estimated fee - Real cost: the delivery price is estimated from the sale order but not added in the delivery line (we get a line with quantity=1 and price = 0). Once the delivery is validated, the delivery price is recomputed. The new price depends on the integration level. If it's 'rate', the price is the estimated one (carrier.rate_shipment()). If it's 'rate_and_ship, the price is the one given by the carrier at the delivery The bases carriers (fixed_on_rule and base) always use 'Estimated Cost' as their price should not be changed between the sale order creation and delivery validation Task : 1908654
This commit is contained in:
@@ -45,6 +45,12 @@ class DeliveryCarrier(models.Model):
|
||||
company_id = fields.Many2one('res.company', string='Company', related='product_id.company_id', store=True, readonly=False)
|
||||
product_id = fields.Many2one('product.product', string='Delivery Product', required=True, ondelete='restrict')
|
||||
|
||||
invoice_policy = fields.Selection([
|
||||
('estimated', 'Estimated cost'),
|
||||
('real', 'Real cost')
|
||||
], string='Invoicing Policy', default='estimated', required=True,
|
||||
help="Estimated Cost: the customer will be invoiced the estimated cost of the shipping.\nReal Cost: the customer will be invoiced the real cost of the shipping, the cost of the shipping will be updated on the SO after the delivery.")
|
||||
|
||||
country_ids = fields.Many2many('res.country', 'delivery_carrier_country_rel', 'carrier_id', 'country_id', 'Countries')
|
||||
state_ids = fields.Many2many('res.country.state', 'delivery_carrier_state_rel', 'carrier_id', 'state_id', 'States')
|
||||
zip_from = fields.Char('Zip From')
|
||||
|
||||
@@ -86,7 +86,7 @@ class SaleOrder(models.Model):
|
||||
order._create_delivery_line(order.carrier_id, price_unit)
|
||||
return True
|
||||
|
||||
def _create_delivery_line(self, carrier, price_unit):
|
||||
def _create_delivery_line(self, carrier, price_unit, price_unit_in_description=False):
|
||||
SaleOrderLine = self.env['sale.order.line']
|
||||
if self.partner_id:
|
||||
# set delivery detail in the customer language
|
||||
@@ -105,15 +105,28 @@ class SaleOrder(models.Model):
|
||||
'product_uom_qty': 1,
|
||||
'product_uom': carrier.product_id.uom_id.id,
|
||||
'product_id': carrier.product_id.id,
|
||||
'price_unit': price_unit,
|
||||
'tax_id': [(6, 0, taxes_ids)],
|
||||
'is_delivery': True,
|
||||
}
|
||||
if price_unit_in_description:
|
||||
values['price_unit'] = 0
|
||||
values['name'] += _(' (Estimated Cost: %s )') % self._format_currency_amount(price_unit)
|
||||
else:
|
||||
values['price_unit'] = price_unit
|
||||
|
||||
if self.order_line:
|
||||
values['sequence'] = self.order_line[-1].sequence + 1
|
||||
sol = SaleOrderLine.sudo().create(values)
|
||||
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('state', 'order_line.invoice_status', 'order_line.invoice_lines',
|
||||
'order_line.is_delivery', 'order_line.is_downpayment', 'order_line.product_id.invoice_policy')
|
||||
def _get_invoiced(self):
|
||||
|
||||
@@ -186,12 +186,33 @@ class StockPicking(models.Model):
|
||||
msg = _("Shipment sent to carrier %s for shipping with tracking number %s<br/>Cost: %.2f %s") % (self.carrier_id.name, self.carrier_tracking_ref, self.carrier_price, order_currency.name)
|
||||
self.message_post(body=msg)
|
||||
|
||||
def _get_new_delivery_price(self):
|
||||
if self.carrier_id.integration_level != 'rate_and_ship':
|
||||
res = self.carrier_id.rate_shipment(self.sale_id)
|
||||
if res['success']:
|
||||
self.carrier_price = res['price']
|
||||
else:
|
||||
raise UserError(_("Unable to update the delivery price because of: ") + res['error_message'])
|
||||
|
||||
@api.multi
|
||||
def _add_delivery_cost_to_so(self):
|
||||
self.ensure_one()
|
||||
sale_order = self.sale_id
|
||||
# if there isn't a delivery line on the SO yet
|
||||
if sale_order.invoice_shipping_on_delivery:
|
||||
sale_order._create_delivery_line(self.carrier_id, self.carrier_price)
|
||||
self._get_new_delivery_price() # fill `self.carrier_price` if needed
|
||||
sale_order._create_delivery_line(self.carrier_id, self.carrier_price, price_unit_in_description=False)
|
||||
else:
|
||||
# we only want to update the price of the delivery line if the invoice
|
||||
# policy is 'Real' but we chose not to if the user updated it in the meantime
|
||||
delivery_line = sale_order.order_line.filtered(lambda line: line.is_delivery)
|
||||
if self.carrier_id.invoice_policy == 'real' and delivery_line.currency_id.is_zero(delivery_line.price_unit):
|
||||
self._get_new_delivery_price()
|
||||
delivery_line.write({
|
||||
'price_unit': self.carrier_price,
|
||||
# remove the estimated price from the description
|
||||
'name': sale_order.carrier_id.with_context(lang=self.partner_id.lang).name,
|
||||
})
|
||||
|
||||
@api.multi
|
||||
def open_website_url(self):
|
||||
|
||||
@@ -84,6 +84,7 @@
|
||||
<field name="integration_level" widget="radio" attrs="{'invisible': ['|', ('delivery_type', '=', 'fixed'), ('delivery_type', '=', 'base_on_rule')]}"/>
|
||||
</group>
|
||||
<group name="delivery_details">
|
||||
<field name="invoice_policy" widget="radio" attrs="{'invisible': [('delivery_type', 'in', ('fixed', 'base_on_rule'))]}"/>
|
||||
<label for="margin" string="Margin on Rate"/>
|
||||
<div>
|
||||
<field name="margin" class="oe_inline"/>%
|
||||
|
||||
Reference in New Issue
Block a user