[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:
Wolfgang Taferner
2016-10-18 13:58:14 +02:00
committed by Nicolas Martinelli
parent dca292b268
commit 327f88dfce
3 changed files with 83 additions and 8 deletions
+4
View File
@@ -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,
+17 -8
View File
@@ -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()