diff --git a/addons/account/models/account.py b/addons/account/models/account.py index f77eee4d956..e8323f7536e 100644 --- a/addons/account/models/account.py +++ b/addons/account/models/account.py @@ -1173,7 +1173,8 @@ class AccountTax(models.Model): str(invoice_tax_val['account_id']) + '-' + \ str(invoice_tax_val['account_analytic_id']) + '-' + \ str(invoice_tax_val.get('analytic_tag_ids', [])) + '-' + \ - str(invoice_tax_val.get('tax_ids') or []) + str(invoice_tax_val.get('tax_ids') or []) + '-' + \ + str(invoice_tax_val.get('tag_ids') or []) def _compute_amount(self, base_amount, price_unit, quantity=1.0, product=None, partner=None): """ Returns the amount of a single tax. base_amount is the actual amount on which the tax is applied, which is @@ -1391,12 +1392,22 @@ class AccountTax(models.Model): if tax.price_include and not total_included_checkpoints.get(i): cumulated_tax_included_amount += tax_amount + # If the tax affects the base of subsequent taxes, its tax move lines must + # receive the base tags of these taxes, so that the tax report computes + # the right total + additional_tags = self.env['account.account.tag'] + if tax.include_base_amount: + next_taxes_rep = taxes[i+1:].mapped(is_refund and 'refund_repartition_line_ids' or 'invoice_repartition_line_ids') + additional_tags += next_taxes_rep.filtered(lambda x: x.repartition_type == 'base').mapped('tag_ids') + + # Compute the tax lines tax_repartition_lines = is_refund and tax.refund_repartition_line_ids or tax.invoice_repartition_line_ids repartition_lines_to_treat = len(tax_repartition_lines) total_amount = 0 for repartition_line in tax_repartition_lines.filtered(lambda x: x.repartition_type == 'tax'): # In case some rounding error occurs, we compensate for it on the last line line_amount = round(sign * tax_amount * repartition_line.factor if repartition_lines_to_treat != 1 else tax_amount - total_amount, prec) + taxes_vals.append({ 'id': tax.id, 'name': partner and tax.with_context(lang=partner.lang).name or tax.name, @@ -1408,7 +1419,7 @@ class AccountTax(models.Model): 'price_include': tax.price_include, 'tax_exigibility': tax.tax_exigibility, 'tax_repartition_line_id': repartition_line.id, - 'tag_ids': [(6, False, repartition_line.tag_ids.ids)], + 'tag_ids': [(6, False, (repartition_line.tag_ids + additional_tags).ids)], }) total_amount += line_amount diff --git a/addons/account/models/account_invoice.py b/addons/account/models/account_invoice.py index 4dff98dd9ae..b76eb0c6886 100644 --- a/addons/account/models/account_invoice.py +++ b/addons/account/models/account_invoice.py @@ -1098,6 +1098,7 @@ class AccountInvoice(models.Model): 'analytic_tag_ids': tax['analytic'] and line.analytic_tag_ids.ids or False, 'tax_ids': tax_ids and [(6, None, tax_ids)] or False, 'tax_repartition_line_id': tax.get('tax_repartition_line_id'), # For base amount, we let this field empty + 'tag_ids': tax['tag_ids'], } # If the taxes generate moves on the same financial account as the invoice line, @@ -1265,7 +1266,6 @@ class AccountInvoice(models.Model): def tax_line_move_line_get(self): self.ensure_one() - res = [] # loop the invoice.tax.line in reversal sequence for tax_line in sorted(self.tax_line_ids, key=lambda x: -x.sequence): @@ -1285,7 +1285,8 @@ class AccountInvoice(models.Model): 'invoice_id': self.id, 'tax_ids': tax_line.tax_ids and [(6, 0, tax_line.tax_ids.ids)] or False, # We don't pass an empty recordset here, as it would reset the tax_exibility of the line, due to the condition in account.move.line's create 'tax_repartition_line_id': tax_line.tax_repartition_line_id.id, - 'tag_ids': [(6, 0, tax_line.tax_repartition_line_id.tag_ids.ids)], + 'tag_ids': [(6, 0, tax_line.tag_ids.ids)], + 'tax_base_amount': tax_line.base, }) return res @@ -2145,8 +2146,8 @@ class AccountInvoiceTax(models.Model): company_id = fields.Many2one('res.company', string='Company', related='account_id.company_id', store=True, readonly=True) currency_id = fields.Many2one('res.currency', related='invoice_id.currency_id', store=True, readonly=True) base = fields.Monetary(string='Base') - tax_ids = fields.Many2many('account.tax', string='Affecting Base Taxes', - help='Taxes affecting the tax base amount applied before this one.') + tax_ids = fields.Many2many('account.tax', string='Affecting Base Taxes', help='Taxes affecting the tax base amount applied before this one.') + tag_ids = fields.Many2many(string="Tags", comodel_name='account.account.tag', help="The taxes that will be applied on the move line generated for this tax entry") @api.depends('amount', 'amount_rounding') def _compute_amount_total(self): diff --git a/addons/account/models/account_move.py b/addons/account/models/account_move.py index 22c45f3feed..4c17e8f5129 100644 --- a/addons/account/models/account_move.py +++ b/addons/account/models/account_move.py @@ -289,6 +289,7 @@ class AccountMove(models.Model): 'company_currency_id': self.company_id.currency_id.id, 'tax_repartition_line_id': line_vals['tax_repartition_line_id'], 'tag_ids': line_vals['tag_ids'], + 'tax_base_amount': line_vals['base'], } # N.B. currency_id/amount_currency are not set because if we have two lines with the same tax # and different currencies, we have no idea which currency set on this line. @@ -619,15 +620,6 @@ class AccountMoveLine(models.Model): move_line.credit_cash_basis = move_line.credit move_line.balance_cash_basis = move_line.debit_cash_basis - move_line.credit_cash_basis - @api.depends('move_id.line_ids', 'move_id.line_ids.tax_line_id', 'move_id.line_ids.debit', 'move_id.line_ids.credit') - def _compute_tax_base_amount(self): - for move_line in self: - if move_line.tax_line_id: - base_lines = move_line.move_id.line_ids.filtered(lambda line: move_line.tax_line_id in line.tax_ids) - move_line.tax_base_amount = abs(sum(base_lines.mapped('balance'))) - else: - move_line.tax_base_amount = 0 - @api.depends('move_id') def _compute_parent_state(self): for record in self.filtered('move_id'): @@ -655,7 +647,7 @@ class AccountMoveLine(models.Model): help="The residual amount on a journal item expressed in the company currency.") amount_residual_currency = fields.Monetary(compute='_amount_residual', string='Residual Amount in Currency', store=True, help="The residual amount on a journal item expressed in its currency (possibly not the company currency).") - tax_base_amount = fields.Monetary(string="Base Amount", compute='_compute_tax_base_amount', currency_field='company_currency_id', store=True) + tax_base_amount = fields.Monetary(string="Base Amount", currency_field='company_currency_id') account_id = fields.Many2one('account.account', string='Account', required=True, index=True, ondelete="cascade", domain=[('deprecated', '=', False)], default=lambda self: self._context.get('account_id', False)) move_id = fields.Many2one('account.move', string='Journal Entry', ondelete="cascade", @@ -1725,6 +1717,7 @@ class AccountPartialReconcile(models.Model): 'move_id': newly_created_move.id, 'partner_id': line.partner_id.id, 'tax_repartition_line_id': line.tax_repartition_line_id.id, + 'tax_base_amount': line.tax_base_amount, 'tag_ids': [(6, 0, line.tag_ids.ids)], }) if line.account_id.reconcile: @@ -1750,6 +1743,7 @@ class AccountPartialReconcile(models.Model): 'amount_currency': self.amount_currency and line.currency_id.round(line.amount_currency * amount / line.balance) or 0.0, 'partner_id': line.partner_id.id, 'tax_repartition_line_id': line.tax_repartition_line_id.id, + 'tax_base_amount': line.tax_base_amount, 'tag_ids': [(6, 0, line.tag_ids.ids)], }) self.env['account.move.line'].with_context(check_move_validity=False).create({ diff --git a/addons/account/models/product.py b/addons/account/models/product.py index aa1c2bd8922..74122687dbe 100644 --- a/addons/account/models/product.py +++ b/addons/account/models/product.py @@ -81,4 +81,5 @@ class ProductProduct(models.Model): 'analytic_tag_ids': line.get('analytic_tag_ids', False), 'tax_repartition_line_id': line.get('tax_repartition_line_id'), 'tag_ids': line.get('tag_ids'), + 'tax_base_amount': line.get('tax_base_amount'), } diff --git a/addons/account/tests/test_reconciliation.py b/addons/account/tests/test_reconciliation.py index 67e2ae40478..01eb2c80c9c 100644 --- a/addons/account/tests/test_reconciliation.py +++ b/addons/account/tests/test_reconciliation.py @@ -1304,6 +1304,7 @@ class TestReconciliationExec(TestReconciliation): 'debit': 16.67, 'move_id': purchase_move.id, 'tax_repartition_line_id': self.tax_cash_basis.invoice_repartition_line_ids.filtered(lambda x: x.repartition_type == 'tax').id, + 'tax_base_amount': 83.33, }) purchase_move.post() @@ -1422,6 +1423,7 @@ class TestReconciliationExec(TestReconciliation): 'debit': 5, 'move_id': purchase_move.id, 'tax_repartition_line_id': tax_cash_basis10percent.invoice_repartition_line_ids.filtered(lambda x: x.repartition_type == 'tax').id, + 'tax_base_amount': 50, }) AccountMoveLine.create({ 'name': 'expenseTaxed 20%', @@ -1436,6 +1438,7 @@ class TestReconciliationExec(TestReconciliation): 'debit': 16.67, 'move_id': purchase_move.id, 'tax_repartition_line_id': self.tax_cash_basis.invoice_repartition_line_ids.filtered(lambda x: x.repartition_type == 'tax').id, + 'tax_base_amount': 83.33, }) purchase_move.post() @@ -1646,6 +1649,7 @@ class TestReconciliationExec(TestReconciliation): 'currency_id': self.currency_usd_id, 'amount_currency': 848.16, 'tax_repartition_line_id': self.tax_cash_basis.invoice_repartition_line_ids.filtered(lambda x: x.repartition_type == 'tax').id, + 'tax_base_amount': 106841.65, }) purchase_payable_line0 = aml_obj.create({ 'name': 'Payable', @@ -1816,6 +1820,7 @@ class TestReconciliationExec(TestReconciliation): 'debit': 17094.66, 'move_id': purchase_move.id, 'tax_repartition_line_id': self.tax_cash_basis.invoice_repartition_line_ids.filtered(lambda x: x.repartition_type == 'tax').id, + 'tax_base_amount': 106841.65, 'currency_id': self.currency_usd_id, 'amount_currency': 848.16, }) @@ -1975,6 +1980,7 @@ class TestReconciliationExec(TestReconciliation): 'debit': 5, 'move_id': purchase_move.id, 'tax_repartition_line_id': tax_cash_basis10percent.invoice_repartition_line_ids.filtered(lambda x: x.repartition_type == 'tax').id, + 'tax_base_amount': 50, }) AccountMoveLine.create({ 'name': 'expenseTaxed 20%', @@ -1989,6 +1995,7 @@ class TestReconciliationExec(TestReconciliation): 'debit': 20, 'move_id': purchase_move.id, 'tax_repartition_line_id': self.tax_cash_basis.invoice_repartition_line_ids.filtered(lambda x: x.repartition_type == 'tax').id, + 'tax_base_amount': 100, }) purchase_move.post() diff --git a/addons/account/views/account_invoice_view.xml b/addons/account/views/account_invoice_view.xml index c575dc42560..6dd9552541b 100644 --- a/addons/account/views/account_invoice_view.xml +++ b/addons/account/views/account_invoice_view.xml @@ -366,6 +366,7 @@ + @@ -634,6 +635,7 @@ + diff --git a/addons/account/views/account_move_views.xml b/addons/account/views/account_move_views.xml index 2f88e9ea33d..73f6dd8105c 100644 --- a/addons/account/views/account_move_views.xml +++ b/addons/account/views/account_move_views.xml @@ -360,6 +360,7 @@ + diff --git a/addons/account/wizard/wizard_tax_adjustments.py b/addons/account/wizard/wizard_tax_adjustments.py index 52d674f00fe..6afa83d9bbb 100644 --- a/addons/account/wizard/wizard_tax_adjustments.py +++ b/addons/account/wizard/wizard_tax_adjustments.py @@ -37,6 +37,7 @@ class TaxAdjustments(models.TransientModel): 'credit': not is_debit and abs(self.amount) or 0, 'account_id': is_debit and self.debit_account_id.id or self.credit_account_id.id, 'tax_repartition_line_id': repartition_line.id, + 'tax_base_amount': tax_vals['base'], 'tag_ids': [(6, False, repartition_line.tag_ids.ids)], })) diff --git a/addons/hr_expense/models/hr_expense.py b/addons/hr_expense/models/hr_expense.py index d70ab25c388..9ea4c4753b0 100644 --- a/addons/hr_expense/models/hr_expense.py +++ b/addons/hr_expense/models/hr_expense.py @@ -325,6 +325,7 @@ class HrExpense(models.Model): 'amount_currency': amount_currency if different_currency else 0.0, 'account_id': tax['account_id'] or move_line_src['account_id'], 'tax_repartition_line_id': tax['tax_repartition_line_id'], + 'tax_base_amount': tax['base'], 'expense_id': expense.id, 'partner_id': partner_id, 'currency_id': expense.currency_id.id if different_currency else False, diff --git a/addons/point_of_sale/models/pos_order.py b/addons/point_of_sale/models/pos_order.py index 52dc13a0a0f..bf13410b137 100644 --- a/addons/point_of_sale/models/pos_order.py +++ b/addons/point_of_sale/models/pos_order.py @@ -326,6 +326,7 @@ class PosOrder(models.Model): 'partner_id': partner_id, 'order_id': order.id, 'tax_repartition_line_id': tax['tax_repartition_line_id'], + 'tax_base_amount': tax['base'], 'tag_ids': tax['tag_ids'], } if currency_id != cur_company: