[IMP] account: Allow tax to not be affected by a previous one
In India, we need to configure taxes like this: - 6% applied on base amount - 6% applied on base amount - 1% applied on 1.12 * base amount If both 6% taxes are affected the base of subsequent taxes, it gives the following result: - 6% applied on base amount - 6% applied on 1.06 * base amount - 1% applied on 1.06 * 1.06 * base amount Thanks to the new 'is_base_affected' field set on the second tax, it gives the expected results. closes odoo/odoo#66134 Task: 2343958 Related: odoo/upgrade#2254 Signed-off-by: Quentin De Paoli (qdp) <qdp@openerp.com>
This commit is contained in:
@@ -73,7 +73,11 @@ class AccountTax(models.Model):
|
||||
price_include = fields.Boolean(string='Included in Price', default=False,
|
||||
help="Check this if the price you use on the product and invoices includes this tax.")
|
||||
include_base_amount = fields.Boolean(string='Affect Base of Subsequent Taxes', default=False,
|
||||
help="If set, taxes which are computed after this one will be computed based on the price tax included.")
|
||||
help="If set, taxes with a higher sequence than this one will be affected by it, provided they accept it.")
|
||||
is_base_affected = fields.Boolean(
|
||||
string="Base Affected by Previous Taxes",
|
||||
default=True,
|
||||
help="If set, taxes with a lower sequence might affect this one, provided they try to do it.")
|
||||
analytic = fields.Boolean(string="Include in Analytic Cost", help="If set, the amount computed by this tax will be assigned to the same analytic account as the invoice line (if any)")
|
||||
tax_group_id = fields.Many2one('account.tax.group', string="Tax Group", default=_default_tax_group, required=True)
|
||||
# Technical field to make the 'tax_exigibility' field invisible if the same named field is set to false in 'res.company' model
|
||||
@@ -373,6 +377,7 @@ class AccountTax(models.Model):
|
||||
# 3) Deal with the rounding methods
|
||||
if not currency:
|
||||
currency = company.currency_id
|
||||
|
||||
# By default, for each tax, tax amount will first be computed
|
||||
# and rounded at the 'Account' decimal precision for each
|
||||
# PO/SO/invoice line and then these rounded amounts will be
|
||||
@@ -510,11 +515,16 @@ class AccountTax(models.Model):
|
||||
i = 0
|
||||
cumulated_tax_included_amount = 0
|
||||
for tax in taxes:
|
||||
price_include = self._context.get('force_price_include', tax.price_include)
|
||||
|
||||
if price_include or tax.is_base_affected:
|
||||
tax_base_amount = base
|
||||
else:
|
||||
tax_base_amount = total_excluded
|
||||
|
||||
tax_repartition_lines = (is_refund and tax.refund_repartition_line_ids or tax.invoice_repartition_line_ids).filtered(lambda x: x.repartition_type == 'tax')
|
||||
sum_repartition_factor = sum(tax_repartition_lines.mapped('factor'))
|
||||
|
||||
price_include = self._context.get('force_price_include', tax.price_include)
|
||||
|
||||
#compute the tax_amount
|
||||
if price_include and total_included_checkpoints.get(i):
|
||||
# We know the total to reach for that tax, so we make a substraction to avoid any rounding issues
|
||||
@@ -522,7 +532,7 @@ class AccountTax(models.Model):
|
||||
cumulated_tax_included_amount = 0
|
||||
else:
|
||||
tax_amount = tax.with_context(force_price_include=False)._compute_amount(
|
||||
base, sign * price_unit, quantity, product, partner)
|
||||
tax_base_amount, sign * price_unit, quantity, product, partner)
|
||||
|
||||
# Round the tax_amount multiplied by the computed repartition lines factor.
|
||||
tax_amount = round(tax_amount, precision_rounding=prec)
|
||||
@@ -563,7 +573,7 @@ class AccountTax(models.Model):
|
||||
'id': tax.id,
|
||||
'name': partner and tax.with_context(lang=partner.lang).name or tax.name,
|
||||
'amount': sign * line_amount,
|
||||
'base': round(sign * base, precision_rounding=prec),
|
||||
'base': round(sign * tax_base_amount, precision_rounding=prec),
|
||||
'sequence': tax.sequence,
|
||||
'account_id': tax.cash_basis_transition_account_id.id if tax.tax_exigibility == 'on_payment' else repartition_line.account_id.id,
|
||||
'analytic': tax.analytic,
|
||||
|
||||
@@ -914,7 +914,11 @@ class AccountTaxTemplate(models.Model):
|
||||
price_include = fields.Boolean(string='Included in Price', default=False,
|
||||
help="Check this if the price you use on the product and invoices includes this tax.")
|
||||
include_base_amount = fields.Boolean(string='Affect Subsequent Taxes', default=False,
|
||||
help="If set, taxes which are computed after this one will be computed based on the price tax included.")
|
||||
help="If set, taxes with a higher sequence than this one will be affected by it, provided they accept it.")
|
||||
is_base_affected = fields.Boolean(
|
||||
string="Base Affected by Previous Taxes",
|
||||
default=True,
|
||||
help="If set, taxes with a lower sequence might affect this one, provided they try to do it.")
|
||||
analytic = fields.Boolean(string="Analytic Cost", help="If set, the amount computed by this tax will be assigned to the same analytic account as the invoice line (if any)")
|
||||
invoice_repartition_line_ids = fields.One2many(string="Repartition for Invoices", comodel_name="account.tax.repartition.line.template", inverse_name="invoice_tax_id", copy=True, help="Repartition when the tax is used on an invoice")
|
||||
refund_repartition_line_ids = fields.One2many(string="Repartition for Refund Invoices", comodel_name="account.tax.repartition.line.template", inverse_name="refund_tax_id", copy=True, help="Repartition when the tax is used on a refund")
|
||||
@@ -964,6 +968,7 @@ class AccountTaxTemplate(models.Model):
|
||||
'description': self.description,
|
||||
'price_include': self.price_include,
|
||||
'include_base_amount': self.include_base_amount,
|
||||
'is_base_affected': self.is_base_affected,
|
||||
'analytic': self.analytic,
|
||||
'children_tax_ids': [(6, 0, children_ids)],
|
||||
'tax_exigibility': self.tax_exigibility,
|
||||
|
||||
@@ -1045,3 +1045,31 @@ class TestTax(TestTaxCommon):
|
||||
],
|
||||
res3
|
||||
)
|
||||
|
||||
def test_is_base_affected(self):
|
||||
taxes = self.env['account.tax'].create([{
|
||||
'name': 'test_is_base_affected%s' % i,
|
||||
'amount_type': 'percent',
|
||||
'amount': amount,
|
||||
'include_base_amount': include_base_amount,
|
||||
'is_base_affected': is_base_affected,
|
||||
'sequence': i,
|
||||
} for i, amount, include_base_amount, is_base_affected in [
|
||||
(0, 6, True, True),
|
||||
(1, 6, True, False),
|
||||
(2, 10, False, True),
|
||||
]])
|
||||
|
||||
self._check_compute_all_results(
|
||||
123.2, # 'total_included'
|
||||
100.0, # 'total_excluded'
|
||||
[
|
||||
# base, amount
|
||||
# -------------------------
|
||||
(100.0, 6.0),
|
||||
(100.0, 6.0),
|
||||
(112.0, 11.2),
|
||||
# -------------------------
|
||||
],
|
||||
taxes.compute_all(100.0),
|
||||
)
|
||||
|
||||
@@ -172,6 +172,8 @@
|
||||
<group name="price_definitions">
|
||||
<field name="price_include" attrs="{'invisible':[('amount_type','=', 'group')]}" />
|
||||
<field name="include_base_amount" attrs="{'invisible':[('amount_type','=', 'group')]}" />
|
||||
<field name="is_base_affected"
|
||||
attrs="{'invisible': ['|', ('amount_type','=', 'group'), ('price_include', '=', True)]}"/>
|
||||
</group>
|
||||
<group name="tax_configuration">
|
||||
<field name="active" groups="base.group_no_one"/>
|
||||
|
||||
@@ -164,6 +164,8 @@
|
||||
<group name="advanced_booleans">
|
||||
<field name="price_include" attrs="{'invisible':[('amount_type','=', 'group')]}" />
|
||||
<field name="include_base_amount" attrs="{'invisible':[('amount_type','=', 'group')]}" />
|
||||
<field name="is_base_affected"
|
||||
attrs="{'invisible': ['|', ('amount_type','=', 'group'), ('price_include', '=', True)]}"/>
|
||||
<field name="hide_tax_exigibility" invisible="1"/>
|
||||
<field name="tax_exigibility" widget="radio" attrs="{'invisible':['|', ('amount_type','=', 'group'), ('hide_tax_exigibility', '=', False)]}" groups="account.group_account_readonly"/>
|
||||
<field name="cash_basis_transition_account_id" options="{'no_create': True}" attrs="{'invisible': [('tax_exigibility', '=', 'on_invoice')], 'required': [('tax_exigibility', '=', 'on_payment')]}" groups="account.group_account_readonly"/>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<field name="model">account.tax</field>
|
||||
<field name="inherit_id" ref="account.view_tax_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="include_base_amount" position="after">
|
||||
<field name="is_base_affected" position="after">
|
||||
<field name="l10n_in_reverse_charge" attrs="{'invisible':[('amount_type','=', 'group')]}"/>
|
||||
</field>
|
||||
</field>
|
||||
|
||||
@@ -10,7 +10,7 @@ class AccountTax(models.Model):
|
||||
def write(self, vals):
|
||||
forbidden_fields = set([
|
||||
'amount_type', 'amount', 'type_tax_use', 'tax_group_id', 'price_include',
|
||||
'include_base_amount'
|
||||
'include_base_amount', 'is_base_affected',
|
||||
])
|
||||
if forbidden_fields & set(vals.keys()):
|
||||
tax_ids = self.env['pos.order.line'].sudo().search([
|
||||
|
||||
@@ -245,7 +245,7 @@ exports.PosModel = Backbone.Model.extend({
|
||||
},
|
||||
},{
|
||||
model: 'account.tax',
|
||||
fields: ['name','amount', 'price_include', 'include_base_amount', 'amount_type', 'children_tax_ids'],
|
||||
fields: ['name','amount', 'price_include', 'include_base_amount', 'is_base_affected', 'amount_type', 'children_tax_ids'],
|
||||
domain: function(self) {return [['company_id', '=', self.company && self.company.id || false]]},
|
||||
loaded: function(self, taxes){
|
||||
self.taxes = taxes;
|
||||
@@ -2218,11 +2218,16 @@ exports.Orderline = Backbone.Model.extend({
|
||||
i = 0;
|
||||
var cumulated_tax_included_amount = 0;
|
||||
_(taxes.reverse()).each(function(tax){
|
||||
if(tax.price_include || tax.is_base_affected)
|
||||
var tax_base_amount = base;
|
||||
else
|
||||
var tax_base_amount = total_excluded;
|
||||
|
||||
if(tax.price_include && total_included_checkpoints[i] !== undefined){
|
||||
var tax_amount = total_included_checkpoints[i] - (base + cumulated_tax_included_amount);
|
||||
cumulated_tax_included_amount = 0;
|
||||
}else
|
||||
var tax_amount = self._compute_all(tax, base, quantity, true);
|
||||
var tax_amount = self._compute_all(tax, tax_base_amount, quantity, true);
|
||||
|
||||
tax_amount = round_pr(tax_amount, currency_rounding);
|
||||
|
||||
@@ -2233,7 +2238,7 @@ exports.Orderline = Backbone.Model.extend({
|
||||
'id': tax.id,
|
||||
'name': tax.name,
|
||||
'amount': sign * tax_amount,
|
||||
'base': sign * round_pr(base, currency_rounding),
|
||||
'base': sign * round_pr(tax_base_amount, currency_rounding),
|
||||
});
|
||||
|
||||
if(tax.include_base_amount)
|
||||
|
||||
Reference in New Issue
Block a user