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>
184 lines
7.7 KiB
Python
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)
|