Files
odoo_source/addons/delivery/models/sale_order.py
T
Oussama MESSAOUDI ae7e1f64f8 [ADD] website_sale_gift_card, gift_card: manage Gift Card in ecommerce
Add suport of gift card for ecommerce.
Split the generic part to allow others modules to us it (e.g. pos)

A customer buy 1 Gift Card, he receive a code by mail and see it into the
sale order.
Another customer can now use this code in the payment step to deduce this
amount.

Product Gift card is sold by default without tax, because we don't know on wich
ecommerce, the shipping address ans sot the tax that will be used.
So the tax is deduce from the sale order that use the gift card.
We considere to be in the MPV case from:
     https://www.vandelanotte.be/fr/actuel/actualites/624-les-bons-soumis-a-un-nouveau-regime-de-tva-a-partir-de-2019

Courtesy of @bvr-odoo for the email and product image

Co-Reviewed by @rde-odoo and @jke-be

task-2428004
pr-64195

closes odoo/odoo#64195

Signed-off-by: Jérémy Kersten (jke) <jke@openerp.com>
2021-02-10 16:24:36 +00:00

184 lines
7.7 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")
@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)
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.amount_total - delivery_cost
@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')
def onchange_order_line(self):
delivery_line = self.order_line.filtered('is_delivery')
if delivery_line:
self.recompute_delivery_price = True
def _remove_delivery_line(self):
self.env['sale.order.line'].search([('order_id', 'in', self.ids), ('is_delivery', '=', True)]).unlink()
def set_delivery_line(self, carrier, amount):
# Remove delivery products from the sales order
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,
}
}
def _create_delivery_line(self, carrier, price_unit):
SaleOrderLine = self.env['sale.order.line']
if self.partner_id:
# set delivery detail in the customer language
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, carrier.product_id, self.partner_id).ids
# Create the sales order line
carrier_with_partner_lang = carrier.with_context(lang=self.partner_id.lang)
if carrier_with_partner_lang.product_id.description_sale:
so_description = '%s: %s' % (carrier_with_partner_lang.name,
carrier_with_partner_lang.product_id.description_sale)
else:
so_description = carrier_with_partner_lang.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)
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 _get_invoice_status(self):
super()._get_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)
if all(line.product_id.invoice_policy == 'delivery' and line.invoice_status == 'no' for line in order_lines):
order.invoice_status = 'no'
def _get_estimated_weight(self):
self.ensure_one()
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)