139 lines
5.3 KiB
Python
139 lines
5.3 KiB
Python
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
from collections import defaultdict
|
|
|
|
from odoo import Command, _, api, fields, models
|
|
from odoo.exceptions import ValidationError
|
|
|
|
|
|
class SaleOrderDiscount(models.TransientModel):
|
|
_name = 'sale.order.discount'
|
|
_description = "Discount Wizard"
|
|
|
|
sale_order_id = fields.Many2one(
|
|
'sale.order', default=lambda self: self.env.context.get('active_id'), required=True)
|
|
company_id = fields.Many2one(related='sale_order_id.company_id')
|
|
currency_id = fields.Many2one(related='sale_order_id.currency_id')
|
|
discount_amount = fields.Monetary(string="Amount")
|
|
discount_percentage = fields.Float(string="Percentage")
|
|
discount_type = fields.Selection(
|
|
selection=[
|
|
('sol_discount', "On All Order Lines"),
|
|
('so_discount', "Global Discount"),
|
|
('amount', "Fixed Amount"),
|
|
],
|
|
default='sol_discount',
|
|
)
|
|
|
|
# CONSTRAINT METHODS #
|
|
|
|
@api.constrains('discount_type', 'discount_percentage')
|
|
def _check_discount_amount(self):
|
|
for wizard in self:
|
|
if (
|
|
wizard.discount_type in ('sol_discount', 'so_discount')
|
|
and wizard.discount_percentage > 1.0
|
|
):
|
|
raise ValidationError(_("Invalid discount amount"))
|
|
|
|
def _prepare_discount_product_values(self):
|
|
self.ensure_one()
|
|
return {
|
|
'name': _('Discount'),
|
|
'type': 'service',
|
|
'invoice_policy': 'order',
|
|
'list_price': 0.0,
|
|
'company_id': self.company_id.id,
|
|
'taxes_id': None,
|
|
}
|
|
|
|
def _prepare_discount_line_values(self, product, amount, taxes, description=None):
|
|
self.ensure_one()
|
|
|
|
vals = {
|
|
'order_id': self.sale_order_id.id,
|
|
'product_id': product.id,
|
|
'sequence': 999,
|
|
'price_unit': -amount,
|
|
'tax_id': [Command.set(taxes.ids)],
|
|
}
|
|
if description:
|
|
# If not given, name will fallback on the standard SOL logic (cf. _compute_name)
|
|
vals['name'] = description
|
|
|
|
return vals
|
|
|
|
def _get_discount_product(self):
|
|
"""Return product.product used for discount line"""
|
|
self.ensure_one()
|
|
discount_product = self.company_id.sale_discount_product_id
|
|
if not discount_product:
|
|
self.company_id.sale_discount_product_id = self.env['product.product'].create(
|
|
self._prepare_discount_product_values()
|
|
)
|
|
discount_product = self.company_id.sale_discount_product_id
|
|
return discount_product
|
|
|
|
def _create_discount_lines(self):
|
|
"""Create SOline(s) according to wizard configuration"""
|
|
self.ensure_one()
|
|
discount_product = self._get_discount_product()
|
|
|
|
if self.discount_type == 'amount':
|
|
vals_list = [
|
|
self._prepare_discount_line_values(
|
|
product=discount_product,
|
|
amount=self.discount_amount,
|
|
taxes=self.env['account.tax'],
|
|
)
|
|
]
|
|
else: # so_discount
|
|
total_price_per_tax_groups = defaultdict(float)
|
|
for line in self.sale_order_id.order_line:
|
|
if not line.product_uom_qty or not line.price_unit:
|
|
continue
|
|
|
|
total_price_per_tax_groups[line.tax_id] += line.price_subtotal
|
|
|
|
if not total_price_per_tax_groups:
|
|
# No valid lines on which the discount can be applied
|
|
return
|
|
elif len(total_price_per_tax_groups) == 1:
|
|
# No taxes, or all lines have the exact same taxes
|
|
taxes = next(iter(total_price_per_tax_groups.keys()))
|
|
subtotal = total_price_per_tax_groups[taxes]
|
|
vals_list = [{
|
|
**self._prepare_discount_line_values(
|
|
product=discount_product,
|
|
amount=subtotal * self.discount_percentage,
|
|
taxes=taxes,
|
|
description=_(
|
|
"Discount: %(percent)s%%",
|
|
percent=self.discount_percentage*100
|
|
),
|
|
),
|
|
}]
|
|
else:
|
|
vals_list = [
|
|
self._prepare_discount_line_values(
|
|
product=discount_product,
|
|
amount=subtotal * self.discount_percentage,
|
|
taxes=taxes,
|
|
description=_(
|
|
"Discount: %(percent)s%%"
|
|
"- On products with the following taxes %(taxes)s",
|
|
percent=self.discount_percentage*100,
|
|
taxes=", ".join(taxes.mapped('name'))
|
|
),
|
|
) for taxes, subtotal in total_price_per_tax_groups.items()
|
|
]
|
|
return self.env['sale.order.line'].create(vals_list)
|
|
|
|
def action_apply_discount(self):
|
|
self.ensure_one()
|
|
self = self.with_company(self.company_id)
|
|
if self.discount_type == 'sol_discount':
|
|
self.sale_order_id.order_line.write({'discount': self.discount_percentage*100})
|
|
else:
|
|
self._create_discount_lines()
|