oca-ocb-sale/odoo-bringout-oca-ocb-sale/sale/wizard/sale_order_discount.py
Ernad Husremovic 73afc09215 19.0 vanilla
2026-03-09 09:32:12 +01:00

166 lines
6.8 KiB
Python

# Part of Odoo. See LICENSE file for full copyright and licensing details.
from collections import defaultdict
from odoo import _, api, fields, models
from odoo.exceptions import ValidationError
from odoo.fields import Command
from odoo.tools import float_repr
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()
values = {
'name': _('Discount'),
'type': 'service',
'invoice_policy': 'order',
'list_price': 0.0,
'company_id': self.company_id.id,
'taxes_id': None,
}
services_category = self.env.ref('product.product_category_services', raise_if_not_found=False)
if services_category:
values['categ_id'] = services_category.id
return values
def _prepare_global_discount_so_lines(self, base_lines):
self.ensure_one()
AccountTax = self.env['account.tax']
discount_dp = self.env['decimal.precision'].precision_get('Discount')
has_multiple_tax_combinations = len(set(base_line['tax_ids'] for base_line in base_lines if base_line['tax_ids'])) > 1
so_line_values_list = []
for base_line in base_lines:
# The name of the so line.
if has_multiple_tax_combinations:
if self.discount_type == 'so_discount':
so_line_description = self.env._(
"Discount %(percent)s%%"
"- On products with the following taxes %(taxes)s",
percent=float_repr(self.discount_percentage * 100.0, discount_dp),
taxes=", ".join(base_line['tax_ids'].mapped('name')),
)
else:
so_line_description = self.env._(
"Discount"
"- On products with the following taxes %(taxes)s",
taxes=", ".join(base_line['tax_ids'].mapped('name')),
)
else:
if self.discount_type == 'so_discount':
so_line_description = self.env._(
"Discount %(percent)s%%",
percent=float_repr(self.discount_percentage * 100.0, discount_dp),
)
else:
so_line_description = self.env._("Discount")
so_line_values_list.append({
'name': so_line_description,
'product_id': base_line['product_id'].id,
'price_unit': base_line['price_unit'],
'technical_price_unit': 0,
'product_uom_qty': base_line['quantity'],
'tax_ids': [Command.set(base_line['tax_ids'].ids)],
'extra_tax_data': AccountTax._export_base_line_extra_tax_data(base_line),
'sequence': 999,
})
return so_line_values_list
def _get_discount_product(self):
"""Return product.product used for discount line"""
self.ensure_one()
company = self.company_id
discount_product = company.sale_discount_product_id
if not discount_product:
if (
self.env['product.product'].has_access('create')
and company.has_access('write')
and company._has_field_access(company._fields['sale_discount_product_id'], 'write')
):
company.sale_discount_product_id = self.env['product.product'].create(
self._prepare_discount_product_values()
)
else:
raise ValidationError(_(
"There does not seem to be any discount product configured for this company yet."
" You can either use a per-line discount, or ask an administrator to grant the"
" discount the first time."
))
discount_product = company.sale_discount_product_id
return discount_product
def _create_discount_lines(self):
self.ensure_one()
self = self.with_context(lang=self.sale_order_id._get_lang())
discount_product = self._get_discount_product()
if self.discount_type == 'so_discount':
amount_type = 'percent'
amount = self.discount_percentage * 100.0
else: # self.discount_type == 'amount':
amount_type = 'fixed'
amount = self.discount_amount
order = self.sale_order_id
AccountTax = self.env['account.tax']
order_lines = order.order_line.filtered(lambda x: not x.display_type)
base_lines = [line._prepare_base_line_for_taxes_computation() for line in order_lines]
AccountTax._add_tax_details_in_base_lines(base_lines, order.company_id)
AccountTax._round_base_lines_tax_details(base_lines, order.company_id)
def grouping_function(base_line):
return {'product_id': discount_product}
global_discount_base_lines = AccountTax._prepare_global_discount_lines(
base_lines=base_lines,
company=self.company_id,
amount_type=amount_type,
amount=amount,
computation_key=f'global_discount,{self.id}',
grouping_function=grouping_function,
)
order.order_line = [
Command.create(values)
for values in self._prepare_global_discount_so_lines(global_discount_base_lines)
]
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()