[FIX] account: tax base computation
Before the fix, the tax base amount is computed only for a complete invoice line. However, depending on the tax sequence and their inclusion in the base amount, the tax base amount might be different for the various taxes of an invoice line. We keep track of the base amount for every single tax, and refactor the `_compute_base_amount` to take these values into account. opw-691177 Closes #13819
This commit is contained in:
committed by
Nicolas Martinelli
parent
dca292b268
commit
327f88dfce
@@ -717,6 +717,9 @@ class AccountTax(models.Model):
|
||||
else:
|
||||
total_included += tax_amount
|
||||
|
||||
# Keep base amount used for the current tax
|
||||
tax_base = base
|
||||
|
||||
if tax.include_base_amount:
|
||||
base += tax_amount
|
||||
|
||||
@@ -724,6 +727,7 @@ class AccountTax(models.Model):
|
||||
'id': tax.id,
|
||||
'name': tax.with_context(**{'lang': partner.lang} if partner else {}).name,
|
||||
'amount': tax_amount,
|
||||
'base': tax_base,
|
||||
'sequence': tax.sequence,
|
||||
'account_id': tax.account_id.id,
|
||||
'refund_account_id': tax.refund_account_id.id,
|
||||
|
||||
@@ -12,6 +12,9 @@ from openerp.tools.misc import formatLang
|
||||
from openerp.exceptions import UserError, RedirectWarning, ValidationError
|
||||
|
||||
import openerp.addons.decimal_precision as dp
|
||||
import logging
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
# mapping invoice type to journal type
|
||||
TYPE2JOURNAL = {
|
||||
@@ -551,6 +554,7 @@ class AccountInvoice(models.Model):
|
||||
'name': tax['name'],
|
||||
'tax_id': tax['id'],
|
||||
'amount': tax['amount'],
|
||||
'base': tax['base'],
|
||||
'manual': False,
|
||||
'sequence': tax['sequence'],
|
||||
'account_analytic_id': tax['analytic'] and line.account_analytic_id.id or False,
|
||||
@@ -580,6 +584,7 @@ class AccountInvoice(models.Model):
|
||||
tax_grouped[key] = val
|
||||
else:
|
||||
tax_grouped[key]['amount'] += val['amount']
|
||||
tax_grouped[key]['base'] += val['base']
|
||||
return tax_grouped
|
||||
|
||||
@api.multi
|
||||
@@ -1293,15 +1298,19 @@ class AccountInvoiceTax(models.Model):
|
||||
_order = 'sequence'
|
||||
|
||||
def _compute_base_amount(self):
|
||||
tax_grouped = {}
|
||||
for invoice in self.mapped('invoice_id'):
|
||||
tax_grouped[invoice.id] = invoice.get_taxes_values()
|
||||
for tax in self:
|
||||
base = 0.0
|
||||
for line in tax.invoice_id.invoice_line_ids:
|
||||
if tax.tax_id in line.invoice_line_tax_ids:
|
||||
price_unit = line.price_unit * (1 - (line.discount or 0.0) / 100.0)
|
||||
base += (line.invoice_line_tax_ids - tax.tax_id).compute_all(
|
||||
price_unit, line.invoice_id.currency_id, line.quantity, line.product_id, line.invoice_id.partner_id
|
||||
)['base']
|
||||
tax.base = base
|
||||
key = self.env['account.tax'].browse(tax.tax_id.id).get_grouping_key({
|
||||
'tax_id': tax.tax_id.id,
|
||||
'account_id': tax.account_id.id,
|
||||
'account_analytic_id': tax.account_analytic_id.id,
|
||||
})
|
||||
if tax.invoice_id and key in tax_grouped[tax.invoice_id.id]:
|
||||
tax.base = tax_grouped[tax.invoice_id.id][key]['base']
|
||||
else:
|
||||
_logger.warning('Tax Base Amount not computable probably due to a change in an underlying tax (%s).', tax.tax_id.name)
|
||||
|
||||
invoice_id = fields.Many2one('account.invoice', string='Invoice', ondelete='cascade', index=True)
|
||||
name = fields.Char(string='Tax Description', required=True)
|
||||
|
||||
@@ -41,3 +41,65 @@ class TestAccountSupplierInvoice(AccountingTestCase):
|
||||
#I cancel the account move which is in posted state and verifies that it gives warning message
|
||||
with self.assertRaises(Warning):
|
||||
invoice.move_id.button_cancel()
|
||||
|
||||
def test_supplier_invoice2(self):
|
||||
tax_fixed = self.env['account.tax'].create({
|
||||
'sequence': 10,
|
||||
'name': 'Tax 10.0 (Fixed)',
|
||||
'amount': 10.0,
|
||||
'amount_type': 'fixed',
|
||||
'include_base_amount': True,
|
||||
})
|
||||
tax_percent_included_base_incl = self.env['account.tax'].create({
|
||||
'sequence': 20,
|
||||
'name': 'Tax 50.0% (Percentage of Price Tax Included)',
|
||||
'amount': 50.0,
|
||||
'amount_type': 'division',
|
||||
'include_base_amount': True,
|
||||
})
|
||||
tax_percentage = self.env['account.tax'].create({
|
||||
'sequence': 30,
|
||||
'name': 'Tax 20.0% (Percentage of Price)',
|
||||
'amount': 20.0,
|
||||
'amount_type': 'percent',
|
||||
'include_base_amount': False,
|
||||
})
|
||||
analytic_account = self.env['account.analytic.account'].create({
|
||||
'name': 'test account',
|
||||
})
|
||||
|
||||
# Should be changed by automatic on_change later
|
||||
invoice_account = self.env['account.account'].search([('user_type_id', '=', self.env.ref('account.data_account_type_receivable').id)], limit=1).id
|
||||
invoice_line_account = self.env['account.account'].search([('user_type_id', '=', self.env.ref('account.data_account_type_expenses').id)], limit=1).id
|
||||
|
||||
invoice = self.env['account.invoice'].create({'partner_id': self.env.ref('base.res_partner_2').id,
|
||||
'account_id': invoice_account,
|
||||
'type': 'in_invoice',
|
||||
})
|
||||
|
||||
invoice_line = self.env['account.invoice.line'].create({'product_id': self.env.ref('product.product_product_4').id,
|
||||
'quantity': 5.0,
|
||||
'price_unit': 100.0,
|
||||
'invoice_id': invoice.id,
|
||||
'name': 'product that cost 100',
|
||||
'account_id': invoice_line_account,
|
||||
'invoice_line_tax_ids': [(6, 0, [tax_fixed.id, tax_percent_included_base_incl.id, tax_percentage.id])],
|
||||
'account_analytic_id': analytic_account.id,
|
||||
})
|
||||
invoice.compute_taxes()
|
||||
|
||||
# check that Initially supplier bill state is "Draft"
|
||||
self.assertTrue((invoice.state == 'draft'), "Initially vendor bill state is Draft")
|
||||
|
||||
#change the state of invoice to open by clicking Validate button
|
||||
invoice.signal_workflow('invoice_open')
|
||||
|
||||
# Check if amount and corresponded base is correct for all tax scenarios given on a computational base
|
||||
# Keep in mind that tax amount can be changed by the user at any time before validating (based on the invoice and tax laws applicable)
|
||||
invoice_tax = invoice.tax_line_ids.sorted(key=lambda r: r.sequence)
|
||||
self.assertEquals(invoice_tax.mapped('amount'), [50.0, 550.0, 220.0])
|
||||
self.assertEquals(invoice_tax.mapped('base'), [500.0, 550.0, 1100.0])
|
||||
|
||||
#I cancel the account move which is in posted state and verifies that it gives warning message
|
||||
with self.assertRaises(Warning):
|
||||
invoice.move_id.button_cancel()
|
||||
|
||||
Reference in New Issue
Block a user