mirror of
https://github.com/bringout/oca-ocb-sale.git
synced 2026-04-27 04:12:05 +02:00
166 lines
6.8 KiB
Python
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()
|