diff options
| author | Olivier Colson <oco@odoo.com> | 2019-05-15 11:03:12 +0000 |
|---|---|---|
| committer | Olivier Colson <oco@odoo.com> | 2019-05-16 08:55:12 +0000 |
| commit | 57feafd4d99c0c1965c6817ceaee746eacb122ad (patch) | |
| tree | 93dc737d3767042b9a19185a3c6ea050ed4f43ac /addons | |
| parent | 69d708c07cd57b711812fe60aa434c8c8ab93623 (diff) | |
[FIX] account, hr_expense, point_of_sale: 'affect subsequent taxes' base" taxes
Consider the following use case:
- Two taxes:
-A: price included, affect base of subsequent taxes, 10%
-B: 15%, regular
- Create an invoice with a line applying A, then B, for 110€. Post it
- The following move lines are generated:
- base: 100€
- tax A: 10€
- tax B: 16.5€
Before this fix, line "tax A" only had the tax tags of tax A. It should also receive the base tags from tax B, in order to affect its grid total correctly in the tax report.
Also, tax_base_amount field used to be computed, but its computation was too simple, and most of the time wrong when doing advanced use of taxes like the one we defined above. It is now not computed anymore, and directly takes its value from the compute_all's result.
closes odoo/odoo#33420
Signed-off-by: Quentin De Paoli (qdp) <qdp@openerp.com>
Diffstat (limited to 'addons')
| -rw-r--r-- | addons/account/models/account.py | 15 | ||||
| -rw-r--r-- | addons/account/models/account_invoice.py | 9 | ||||
| -rw-r--r-- | addons/account/models/account_move.py | 14 | ||||
| -rw-r--r-- | addons/account/models/product.py | 1 | ||||
| -rw-r--r-- | addons/account/tests/test_reconciliation.py | 7 | ||||
| -rw-r--r-- | addons/account/views/account_invoice_view.xml | 2 | ||||
| -rw-r--r-- | addons/account/views/account_move_views.xml | 1 | ||||
| -rw-r--r-- | addons/account/wizard/wizard_tax_adjustments.py | 1 | ||||
| -rw-r--r-- | addons/hr_expense/models/hr_expense.py | 1 | ||||
| -rw-r--r-- | addons/point_of_sale/models/pos_order.py | 1 |
10 files changed, 36 insertions, 16 deletions
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 @@ <field name="amount_total" invisible="1"/> <field name="currency_id" invisible="1" force_save="1"/> <field name="tax_repartition_line_id" invisible="1"/> <!--Needed in order for the onchange computing the taxes to keep the repartition lines--> + <field name="tag_ids" invisible="1"/> <!--Needed in order for the onchange computing the taxes to keep the tags--> <field name="base" invisible="1"/> </tree> </field> @@ -634,6 +635,7 @@ <field name="amount_total"/> <field name="currency_id" invisible="1" force_save="1"/> <field name="tax_repartition_line_id" invisible="1"/> <!--Needed in order for the onchange computing the taxes to keep the repartition lines--> + <field name="tag_ids" invisible="1"/> <!--Needed in order for the onchange computing the taxes to keep the tags--> <field name="base" invisible="1"/> </tree> </field> 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 @@ <field name="recompute_tax_line" invisible="1" readonly="1"/> <field name="tax_line_grouping_key" invisible="1" readonly="1"/> <field name="tax_repartition_line_id" invisible="1"/> <!--Not to be removed, we need it so that the call to new() takes it into account when autocompleting the lines of a tax--> + <field name="tax_base_amount" invisible="1"/> <!--Not to be removed, we need it so that the call to new() takes it into account when autocompleting the lines of a tax--> <field name="tag_ids" invisible="1"/> </tree> </field> 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: |
