sale/wizard/sale_order_discount.py

139 lines
5.3 KiB
Python
Raw Permalink Normal View History

# 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()