mirror of
https://gitlab.com/flectra-hq/flectra.git
synced 2026-10-25 07:27:39 -05:00
[ADD] sale_discount:
New implementation of sale_discount with full support of tax reduction respecting also different taxes used on sales positions
This commit is contained in:
1 parent
b318ce618a
commit
16b82d2414
17 files changed
+695
No files matched your search
@@ -0,0 +1 @@
|
||||
from . import models
|
||||
@@ -0,0 +1,25 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
{
|
||||
'name': "Invoice Document Discount",
|
||||
'summary': """
|
||||
Use Document Discounts on Invoices
|
||||
""",
|
||||
'description': """
|
||||
Use Document Discounts on Invoices
|
||||
""",
|
||||
'author': "Jamotion GmbH, Flectra HQ",
|
||||
'website': "https://flectra-hq.com",
|
||||
'category': 'Accounting',
|
||||
'version': '0.1',
|
||||
'depends': [
|
||||
'account',
|
||||
],
|
||||
'data': [
|
||||
'views/res_config_settings_views.xml',
|
||||
'views/account_move_views.xml',
|
||||
'reports/invoice_report_views.xml',
|
||||
],
|
||||
'demo': [
|
||||
# 'demo/demo.xml',
|
||||
],
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
from . import res_company
|
||||
from . import res_config_settings
|
||||
from . import account_move
|
||||
@@ -0,0 +1,231 @@
|
||||
import logging
|
||||
from functools import partial
|
||||
from flectra import models, fields, api, _
|
||||
from flectra.tools import float_is_zero, float_round, float_compare
|
||||
from flectra.tools.misc import formatLang
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class AccountMove(models.Model):
|
||||
_inherit = 'account.move'
|
||||
|
||||
discount_type = fields.Selection(
|
||||
selection=[
|
||||
('fixed', 'Fixed'),
|
||||
('percent', 'Percent')
|
||||
],
|
||||
string="Discount Type",
|
||||
default="percent",
|
||||
)
|
||||
|
||||
discount_value = fields.Float(
|
||||
string='Discount',
|
||||
tracking=True,
|
||||
)
|
||||
|
||||
discount_value_percent = fields.Float(
|
||||
string='Discount (%)',
|
||||
digits='Discount',
|
||||
compute='_compute_document_discount',
|
||||
compute_sudo=True,
|
||||
)
|
||||
|
||||
amount_gross = fields.Monetary(
|
||||
string='Gross Amount',
|
||||
store=True,
|
||||
readonly=True,
|
||||
compute='_compute_amount',
|
||||
)
|
||||
|
||||
document_discount = fields.Monetary(
|
||||
string='Document Discount',
|
||||
store=True,
|
||||
readonly=True,
|
||||
compute='_compute_document_discount',
|
||||
)
|
||||
|
||||
document_discount_tax_amount = fields.Monetary(
|
||||
string='Document Discount Tax',
|
||||
store=True,
|
||||
readonly=True,
|
||||
compute='_compute_document_discount',
|
||||
)
|
||||
|
||||
has_document_discount = fields.Boolean(
|
||||
string='Has Document Discount',
|
||||
store=True,
|
||||
readonly=True,
|
||||
compute='_compute_document_discount',
|
||||
)
|
||||
|
||||
@api.depends(
|
||||
'line_ids.amount_currency',
|
||||
'line_ids.price_subtotal',
|
||||
'line_ids.price_total',
|
||||
'discount_type',
|
||||
'discount_value',
|
||||
)
|
||||
def _compute_document_discount(self):
|
||||
for move in self:
|
||||
document_discount = 0
|
||||
discount_used = move.discount_type and not float_is_zero(move.discount_value, precision_digits=move.currency_id.decimal_places)
|
||||
if discount_used:
|
||||
if move.discount_type == 'fixed':
|
||||
document_discount = move.discount_value * -1
|
||||
else:
|
||||
document_discount = (move.amount_gross * (move.discount_value / 100)) * -1
|
||||
|
||||
document_discount = float_round(document_discount, precision_digits=move.currency_id.decimal_places)
|
||||
|
||||
discount_lines = move.line_ids.filtered(lambda f: f.is_document_discount_line)
|
||||
if discount_lines:
|
||||
document_discount_tax_amount = float_round(
|
||||
sum(line.price_total - line.price_subtotal for line in discount_lines),
|
||||
precision_digits=move.currency_id.decimal_places
|
||||
)
|
||||
else:
|
||||
document_discount_tax_amount = 0
|
||||
move.update({
|
||||
'document_discount': document_discount,
|
||||
'document_discount_tax_amount': document_discount_tax_amount,
|
||||
'has_document_discount': discount_used,
|
||||
'discount_value_percent': move.discount_value,
|
||||
})
|
||||
|
||||
@api.depends(
|
||||
'line_ids.matched_debit_ids.debit_move_id.move_id.line_ids.amount_residual',
|
||||
'line_ids.matched_debit_ids.debit_move_id.move_id.line_ids.amount_residual_currency',
|
||||
'line_ids.matched_credit_ids.credit_move_id.move_id.line_ids.amount_residual',
|
||||
'line_ids.matched_credit_ids.credit_move_id.move_id.line_ids.amount_residual_currency',
|
||||
'line_ids.debit',
|
||||
'line_ids.credit',
|
||||
'line_ids.currency_id',
|
||||
'line_ids.amount_currency',
|
||||
'line_ids.amount_residual',
|
||||
'line_ids.amount_residual_currency',
|
||||
'line_ids.payment_id.state',
|
||||
'line_ids.full_reconcile_id',
|
||||
'line_ids.price_total',
|
||||
'document_discount',
|
||||
)
|
||||
def _compute_amount(self):
|
||||
super(AccountMove, self)._compute_amount()
|
||||
for move in self:
|
||||
if move.line_ids:
|
||||
amount_gross = sum(move.line_ids.filtered(lambda f: not f.exclude_from_invoice_tab).mapped('price_subtotal'))
|
||||
move.update({
|
||||
'amount_gross': amount_gross,
|
||||
'amount_untaxed': move.amount_gross + move.document_discount,
|
||||
})
|
||||
move._generate_document_discount_move_line()
|
||||
move.update({
|
||||
'amount_total': move.amount_untaxed + move.amount_tax,
|
||||
})
|
||||
|
||||
def _generate_document_discount_move_line(self):
|
||||
|
||||
aml = self.env['account.move.line'].with_context(check_move_validity=False)
|
||||
|
||||
update_needed = False
|
||||
|
||||
for move in self:
|
||||
|
||||
update_needed = False
|
||||
|
||||
in_draft_mode = move != move._origin
|
||||
if float_is_zero(move.document_discount, precision_digits=move.currency_id.decimal_places):
|
||||
to_del = move.line_ids.filtered(lambda f: f.is_document_discount_line)
|
||||
if in_draft_mode:
|
||||
move.update({'line_ids': [(2, line.id) for line in to_del]})
|
||||
update_needed = True
|
||||
continue
|
||||
|
||||
currency = move.currency_id or move.company_id.currency_id
|
||||
if move.discount_type == 'fixed':
|
||||
account_id = move.company_id.document_discount_fixed_account_id
|
||||
else:
|
||||
account_id = move.company_id.document_discount_percent_account_id
|
||||
|
||||
create_method = in_draft_mode and aml.new or aml.create
|
||||
distributions = move._get_document_tax_distribution()
|
||||
for distribution in distributions.values():
|
||||
existing_line = move.line_ids.filtered(lambda f: f.is_document_discount_line and f.tax_ids.ids == distribution['tax'].ids)
|
||||
balance = currency._convert(
|
||||
move.document_discount * distribution['factor'],
|
||||
move.journal_id.company_id.currency_id,
|
||||
move.journal_id.company_id,
|
||||
move.date or fields.Date.context_today(move),
|
||||
)
|
||||
price_unit = move.document_discount * distribution['factor']
|
||||
amount_currency = price_unit * -1
|
||||
if existing_line:
|
||||
line_values = {
|
||||
'amount_currency': amount_currency,
|
||||
'price_unit': price_unit,
|
||||
'debit': balance < 0.0 and -balance or 0.0,
|
||||
'credit': balance > 0.0 and balance or 0.0,
|
||||
}
|
||||
if existing_line.credit == line_values['credit'] and existing_line.debit == line_values['debit']:
|
||||
continue
|
||||
|
||||
update_needed = True
|
||||
if in_draft_mode:
|
||||
existing_line.update(line_values)
|
||||
else:
|
||||
existing_line.write(line_values)
|
||||
else:
|
||||
line_values = {
|
||||
'account_id': account_id.id,
|
||||
'amount_currency': amount_currency,
|
||||
'price_unit': price_unit,
|
||||
'currency_id': currency.id,
|
||||
'debit': balance < 0.0 and -balance or 0.0,
|
||||
'credit': balance > 0.0 and balance or 0.0,
|
||||
'name': _('Document Discount'),
|
||||
'move_id': move.id,
|
||||
'partner_id': move.partner_id.id,
|
||||
'company_id': move.company_id.id,
|
||||
'company_currency_id': currency.id,
|
||||
'exclude_from_invoice_tab': True,
|
||||
'is_document_discount_line': True,
|
||||
'tax_ids': distribution['tax'].ids,
|
||||
}
|
||||
create_method(line_values)
|
||||
update_needed = True
|
||||
|
||||
if update_needed:
|
||||
self.with_context(check_move_validity=False)._recompute_dynamic_lines(recompute_all_taxes=True)
|
||||
|
||||
def _get_document_tax_distribution(self):
|
||||
"""
|
||||
This function calculates distribution factor for right split
|
||||
of negative tax amount of document discount amount.
|
||||
@param amount_gross: the base amount (total untaxed amount) used as 100%
|
||||
@return: dict with tax.id as key and tax data includin the distribution factor
|
||||
"""
|
||||
self.ensure_one()
|
||||
res = {}
|
||||
tax_lines = self.line_ids.filtered(lambda f: f.tax_line_id)
|
||||
if not tax_lines:
|
||||
return res
|
||||
total_amount = sum(tax_lines.mapped('tax_base_amount'))
|
||||
for line in tax_lines:
|
||||
key = line.tax_line_id.id
|
||||
res.setdefault(key, {'tax': line.tax_line_id, 'factor': 0.0})
|
||||
res[key]['factor'] = line.tax_base_amount / total_amount
|
||||
return res
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
rslt = super(AccountMove, self).create(vals_list)
|
||||
rslt._compute_document_discount()
|
||||
rslt._compute_amount()
|
||||
rslt._generate_document_discount_move_line()
|
||||
return rslt
|
||||
|
||||
|
||||
class AccountMoveLine(models.Model):
|
||||
_inherit = 'account.move.line'
|
||||
|
||||
is_document_discount_line = fields.Boolean(help="Technical field used to identify document discount lines.")
|
||||
@@ -0,0 +1,18 @@
|
||||
import logging
|
||||
from flectra import models, fields, api, _
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ResCompany(models.Model):
|
||||
_inherit = 'res.company'
|
||||
|
||||
document_discount_fixed_account_id = fields.Many2one(
|
||||
comodel_name='account.account',
|
||||
string='Account for fixed Discount',
|
||||
)
|
||||
|
||||
document_discount_percent_account_id = fields.Many2one(
|
||||
comodel_name='account.account',
|
||||
string='Account for percent Discount',
|
||||
)
|
||||
@@ -0,0 +1,18 @@
|
||||
import logging
|
||||
from flectra import models, fields, api, _
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ResConfigSettings(models.TransientModel):
|
||||
_inherit = 'res.config.settings'
|
||||
|
||||
document_discount_fixed_account_id = fields.Many2one(
|
||||
related='company_id.document_discount_fixed_account_id',
|
||||
readonly=False,
|
||||
)
|
||||
|
||||
document_discount_percent_account_id = fields.Many2one(
|
||||
related='company_id.document_discount_percent_account_id',
|
||||
readonly=False,
|
||||
)
|
||||
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<flectra>
|
||||
<template id="report_invoice_document" inherit_id="account.report_invoice_document">
|
||||
<xpath expr="//div[@id='total']/div/table/tr[1]" position="before">
|
||||
<t t-if="o.has_document_discount">
|
||||
<tr class="border-black o_subtotal" style="">
|
||||
<td>
|
||||
<strong>Gross Amount</strong>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<span t-field="o.amount_gross"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr style="">
|
||||
<td t-if="o.discount_type == 'fixed'">
|
||||
Fixed Discount
|
||||
</td>
|
||||
<td t-if="o.discount_type == 'percent'">
|
||||
<span t-field="o.discount_value_percent"/>% Discount
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<span t-field="o.document_discount"/>
|
||||
</td>
|
||||
</tr>
|
||||
</t>
|
||||
</xpath>
|
||||
</template>
|
||||
</flectra>
|
||||
@@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<flectra>
|
||||
<record id="view_move_form" model="ir.ui.view">
|
||||
<field name="name">view.account.move.form.inherit</field>
|
||||
<field name="model">account.move</field>
|
||||
<field name="inherit_id" ref="account.view_move_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="amount_untaxed" position="before">
|
||||
<field name="has_document_discount" invisible="1"/>
|
||||
<field name="amount_gross" attrs="{'invisible': [('has_document_discount', '=', False)]}"/>
|
||||
<field name="document_discount" attrs="{'invisible': [('has_document_discount', '=', False)]}"/>
|
||||
</field>
|
||||
<field name="narration" position="after">
|
||||
<group>
|
||||
<group>
|
||||
<label for="discount_type" string="Document Discount"/>
|
||||
<div class="o_row">
|
||||
<field name="discount_type"/>
|
||||
<field name="discount_value" attrs="{'invisible': [('discount_type', '=', False)]}"/>
|
||||
</div>
|
||||
</group>
|
||||
</group>
|
||||
</field>
|
||||
<xpath expr="//page[@id='aml_tab']//tree/field[@name='account_id']" position="after">
|
||||
<field name="is_document_discount_line" invisible="True"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</flectra>
|
||||
@@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<flectra>
|
||||
<record id="res_config_settings_view_form" model="ir.ui.view">
|
||||
<field name="name">res.config.settings.view.form.inherit.account.discount</field>
|
||||
<field name="model">res.config.settings</field>
|
||||
<field name="inherit_id" ref="account.res_config_settings_view_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//div[@id='default_incoterm']" position="after">
|
||||
<div class="col-12 col-lg-6 o_setting_box" id="document_discount"
|
||||
title="The following accounts are used for document discount items">
|
||||
<div class="o_setting_left_pane"/>
|
||||
<div class="o_setting_right_pane">
|
||||
<span class="o_form_label">Document Discount Accounts</span>
|
||||
<span class="fa fa-lg fa-building-o" title="Values set here are company-specific."
|
||||
aria-label="Values set here are company-specific."
|
||||
groups="base.group_multi_company" role="img"/>
|
||||
<div class="text-muted">
|
||||
Accounts applied to document discount items
|
||||
</div>
|
||||
<div class="content-group">
|
||||
<div class="row mt16">
|
||||
<label string="Fixed Discount" for="document_discount_fixed_account_id" class="col-lg-3 o_light_label"/>
|
||||
<field name="document_discount_fixed_account_id"/>
|
||||
</div>
|
||||
<div class="row">
|
||||
<label string="Percent Discount" for="document_discount_percent_account_id" class="col-lg-3 o_light_label"/>
|
||||
<field name="document_discount_percent_account_id"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</flectra>
|
||||
@@ -0,0 +1 @@
|
||||
from . import models
|
||||
@@ -0,0 +1,27 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
{
|
||||
'name': "Sale Document Discount",
|
||||
'summary': """
|
||||
Use Document Discounts in Sale
|
||||
""",
|
||||
'description': """
|
||||
Use Document Discounts in Sale
|
||||
""",
|
||||
'author': "Jamotion GmbH",
|
||||
'website': "https://jamotion.ch",
|
||||
'category': 'Sales',
|
||||
'version': '0.1',
|
||||
'depends': [
|
||||
'sale',
|
||||
],
|
||||
'data': [
|
||||
# 'security/ir.model.access.csv',
|
||||
# 'views/res_config_settings_views.xml',
|
||||
# 'views/product_views.xml',
|
||||
'views/sale_views.xml',
|
||||
'reports/sale_report_templates.xml',
|
||||
],
|
||||
'demo': [
|
||||
# 'demo/demo.xml',
|
||||
],
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
from . import product
|
||||
from . import sale
|
||||
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import logging
|
||||
from flectra import models, fields, api, _
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ProductTemplate(models.Model):
|
||||
_inherit = 'product.template'
|
||||
|
||||
exclude_from_document_discount = fields.Boolean(
|
||||
string='No Document Discount',
|
||||
help='When enabled the position is excluded from Document Discount',
|
||||
)
|
||||
@@ -0,0 +1,189 @@
|
||||
import logging
|
||||
from functools import partial
|
||||
from flectra import models, fields, api, _
|
||||
from flectra.tools import float_is_zero
|
||||
from flectra.tools.misc import formatLang
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class SaleOrder(models.Model):
|
||||
_inherit = 'sale.order'
|
||||
|
||||
discount_type = fields.Selection(
|
||||
selection=[
|
||||
('fixed', 'Fixed'),
|
||||
('percent', 'Percent')
|
||||
],
|
||||
string="Discount Type",
|
||||
default="percent",
|
||||
)
|
||||
|
||||
discount_value = fields.Float(
|
||||
string='Discount',
|
||||
tracking=True,
|
||||
)
|
||||
|
||||
discount_value_percent = fields.Float(
|
||||
string='Discount (%)',
|
||||
digits='Discount',
|
||||
compute='_amount_all',
|
||||
compute_sudo=True,
|
||||
)
|
||||
|
||||
amount_gross = fields.Monetary(
|
||||
string='Gross Amount',
|
||||
store=True,
|
||||
readonly=True,
|
||||
compute='_amount_all',
|
||||
)
|
||||
|
||||
amount_discountable = fields.Monetary(
|
||||
string='Base Discount Amount',
|
||||
store=True,
|
||||
readonly=True,
|
||||
compute='_amount_all',
|
||||
)
|
||||
|
||||
document_discount = fields.Monetary(
|
||||
string='Document Discount',
|
||||
store=True,
|
||||
readonly=True,
|
||||
compute='_amount_all',
|
||||
)
|
||||
|
||||
document_discount_tax_amount = fields.Monetary(
|
||||
string='Document Discount Tax',
|
||||
store=True,
|
||||
readonly=True,
|
||||
compute='_amount_all',
|
||||
)
|
||||
|
||||
@api.depends('order_line.price_total', 'discount_type', 'discount_value')
|
||||
def _amount_all(self):
|
||||
super(SaleOrder, self)._amount_all()
|
||||
for order in self:
|
||||
amount_gross = order.amount_untaxed
|
||||
amount_discountable = 0
|
||||
document_discount = 0
|
||||
discount_tax_amount = 0
|
||||
discount_used = not float_is_zero(order.discount_value, precision_digits=order.currency_id.decimal_places)
|
||||
|
||||
if discount_used:
|
||||
amount_discountable = amount_gross
|
||||
if order.discount_type == 'fixed':
|
||||
document_discount = order.discount_value * -1
|
||||
else:
|
||||
excluded_lines = order.order_line.filtered(lambda f: f.product_id.exclude_from_document_discount)
|
||||
if excluded_lines:
|
||||
excluded_from_discount = sum(l.price_subtotal for l in excluded_lines)
|
||||
amount_discountable -= excluded_from_discount
|
||||
document_discount = (amount_discountable * (order.discount_value / 100)) * -1
|
||||
|
||||
for distribution in order._get_document_tax_distribution(amount_discountable).values():
|
||||
taxes = distribution['tax'].compute_all(
|
||||
document_discount * distribution['factor'],
|
||||
partner=order.partner_shipping_id,
|
||||
is_refund=True,
|
||||
)
|
||||
discount_tax_amount += sum(t.get('amount', 0.0) for t in taxes.get('taxes', []))
|
||||
|
||||
amount_untaxed = order.amount_untaxed + document_discount
|
||||
amount_tax = order.amount_tax + discount_tax_amount
|
||||
amount_total = amount_untaxed + amount_tax
|
||||
|
||||
order.update({
|
||||
'discount_value_percent': order.discount_value,
|
||||
'amount_discountable': amount_discountable,
|
||||
'document_discount': document_discount,
|
||||
'document_discount_tax_amount': discount_tax_amount,
|
||||
'amount_gross': amount_gross,
|
||||
'amount_untaxed': amount_untaxed,
|
||||
'amount_tax': amount_tax,
|
||||
'amount_total': amount_total,
|
||||
})
|
||||
|
||||
def _amount_by_group(self):
|
||||
for order in self:
|
||||
if float_is_zero(order.document_discount, precision_digits=order.currency_id.decimal_places):
|
||||
super(SaleOrder, order)._amount_by_group()
|
||||
continue
|
||||
|
||||
currency = order.currency_id or order.company_id.currency_id
|
||||
fmt = partial(formatLang, self.with_context(lang=order.partner_id.lang).env, currency_obj=currency)
|
||||
res = {}
|
||||
for line in order.order_line:
|
||||
price_reduce = line.price_unit * (1.0 - line.discount / 100.0)
|
||||
taxes = line.tax_id.compute_all(
|
||||
price_reduce,
|
||||
quantity=line.product_uom_qty,
|
||||
product=line.product_id,
|
||||
partner=order.partner_shipping_id,
|
||||
)['taxes']
|
||||
for tax in line.tax_id:
|
||||
group = tax.tax_group_id
|
||||
res.setdefault(group, {'amount': 0.0, 'base': 0.0})
|
||||
for t in taxes:
|
||||
if t['id'] == tax.id or t['id'] in tax.children_tax_ids.ids:
|
||||
res[group]['amount'] += t['amount']
|
||||
res[group]['base'] += t['base']
|
||||
|
||||
for distribution in order._get_document_tax_distribution(order.amount_discountable).values():
|
||||
group = distribution['tax'].tax_group_id
|
||||
taxes = distribution['tax'].compute_all(
|
||||
order.document_discount * distribution['factor'],
|
||||
partner=order.partner_shipping_id,
|
||||
is_refund=True,
|
||||
)['taxes']
|
||||
for t in taxes:
|
||||
if t['id'] == distribution['tax'].id or t['id'] in distribution['tax'].children_tax_ids.ids:
|
||||
res[group]['amount'] += t['amount']
|
||||
res[group]['base'] += t['base']
|
||||
|
||||
res = sorted(res.items(), key=lambda l: l[0].sequence)
|
||||
order.amount_by_group = [(
|
||||
l[0].name, l[1]['amount'], l[1]['base'],
|
||||
fmt(l[1]['amount']), fmt(l[1]['base']),
|
||||
len(res),
|
||||
) for l in res]
|
||||
|
||||
def _get_document_tax_distribution(self, amount_discountable):
|
||||
"""
|
||||
This function calculates distribution factor for right split
|
||||
of negative tax amount of document discount amount.
|
||||
@param amount_discountable: the base amount (total untaxed amount) used as 100%
|
||||
@return: dict with tax.id as key and tax data includin the distribution factor
|
||||
"""
|
||||
self.ensure_one()
|
||||
res = {}
|
||||
for line in self.order_line.filtered(lambda f: not f.product_id.exclude_from_document_discount):
|
||||
price_reduce = line.price_unit * (1.0 - line.discount / 100.0)
|
||||
taxes = line.tax_id.compute_all(
|
||||
price_reduce,
|
||||
quantity=line.product_uom_qty,
|
||||
product=line.product_id,
|
||||
partner=self.partner_shipping_id
|
||||
)['taxes']
|
||||
for tax in line.tax_id:
|
||||
key = tax.id
|
||||
res.setdefault(key, {'tax': tax, 'amount': 0.0, 'base': 0.0, 'factor': 0.0})
|
||||
for t in taxes:
|
||||
if t['id'] == tax.id or t['id'] in tax.children_tax_ids.ids:
|
||||
res[key]['amount'] += t['amount']
|
||||
res[key]['base'] += t['base']
|
||||
res[key]['factor'] = res[key]['base'] / amount_discountable
|
||||
return res
|
||||
|
||||
def _prepare_invoice(self):
|
||||
invoice_vals = super(SaleOrder, self)._prepare_invoice()
|
||||
|
||||
# To apply fixed discount only on first real invoice (no downpayment)
|
||||
# we need to check if any real order position was already invoiced
|
||||
real_invoice_exists = len(self.order_line.filtered(lambda f: not f.is_downpayment).invoice_lines.move_id)
|
||||
|
||||
# On fixed document discount we apply the discount only on first invoice
|
||||
# On percent document discount the discount is applied on all invoices
|
||||
if self.discount_type == 'percent' or not real_invoice_exists:
|
||||
invoice_vals['discount_type'] = self.discount_type
|
||||
invoice_vals['discount_value'] = self.discount_value
|
||||
return invoice_vals
|
||||
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<flectra>
|
||||
<template id="report_saleorder_document" inherit_id="sale.report_saleorder_document">
|
||||
<xpath expr="//div[@id='total']/div/table/tr[1]" position="before">
|
||||
<t t-if="doc.amount_gross != doc.amount_untaxed">
|
||||
<tr class="border-black o_subtotal" style="">
|
||||
<td name="td_amount_gross_label">
|
||||
<strong>Gross</strong>
|
||||
</td>
|
||||
<td name="td_amount_gross" class="text-right">
|
||||
<span t-field="doc.amount_gross"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr style="">
|
||||
<td name="td_document_discount_label">
|
||||
<t t-if="doc.discount_type == 'percent'">
|
||||
<span t-field="doc.discount_value_percent"/>
|
||||
<span>% Discount</span>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<strong>Fixed Discount</strong>
|
||||
</t>
|
||||
|
||||
</td>
|
||||
<td name="td_document_discount" class="text-right">
|
||||
<span t-field="doc.document_discount"/>
|
||||
</td>
|
||||
</tr>
|
||||
</t>
|
||||
</xpath>
|
||||
</template>
|
||||
</flectra>
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<flectra>
|
||||
<record id="product_template_sale_form_view" model="ir.ui.view">
|
||||
<field name="name">view.product.template.form.inherit</field>
|
||||
<field name="model">product.template</field>
|
||||
<field name="inherit_id" ref="sale.product_template_sale_form_view"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//page[@name='sales']/group[@name='sale']" position="after">
|
||||
<group name="document_discount" string="Document Discount">
|
||||
<div>
|
||||
<field name="exclude_from_document_discount" nolabel="1"/>
|
||||
<label for="exclude_from_document_discount" string="No Discount"/>
|
||||
</div>
|
||||
</group>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</flectra>
|
||||
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<flectra>
|
||||
<data>
|
||||
<record id="view_order_form" model="ir.ui.view">
|
||||
<field name="name">view.sale.order.form.inherit</field>
|
||||
<field name="model">sale.order</field>
|
||||
<field name="inherit_id" ref="sale.view_order_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="note" position="before">
|
||||
<label for="discount_type" string="Document Discount"/>
|
||||
<div class="o_row">
|
||||
<field name="discount_type"/>
|
||||
<field name="discount_value" attrs="{'invisible': [('discount_type', '=', False)]}"/>
|
||||
</div>
|
||||
</field>
|
||||
<field name="amount_untaxed" position="before">
|
||||
<field name="amount_gross" widget='monetary' options="{'currency_field': 'currency_id'}"/>
|
||||
<field name="document_discount" widget='monetary' options="{'currency_field': 'currency_id'}"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
</data>
|
||||
</flectra>
|
||||
Reference in new issue
Block a user