From 8f6e85f75275f8b8ffe7c0c7aa6ef7dd609dee8f Mon Sep 17 00:00:00 2001 From: "Touati Djamel (otd)" Date: Wed, 28 Jul 2021 15:08:49 +0000 Subject: [PATCH] [FIX] account, hr_expense: fix reconciliation function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Steps to reproduce the bug: - Install Accounting and Expense app - Go to accounting settings > enable cash basis option - Go to accounting > configuration > accounting > taxes - Create a new tax or choose an existing one e.g “Tax abc”: Tax Computation = `”Percentage of Price”` Tax Type = `”Purchases”` Choose any value for amount Select "Based on Payment" in tax due choose an account with the "Allow Reconciliation" option activated in the Cash Basis Transition Account field Save - Go to expenses > Expense Reports > All reports > Create a new one : - Add 2 expense and for both set up the taxes to "Tax abc" and unit price > 0 - Submit to manager - Approve - Post journal entries - Register a payment Problem: A user error with the following message is triggered: “You are trying to reconcile some entries that are already reconciled” In the use case we have two expenses and for each, an `" account.partial.reconcile "` will be created, so we will loop twice and therefore add twice all the `”account.move.line”` linked to the taxes in the dict `" to_reconcile_after "`: https://github.com/odoo/odoo/blob/beccf82e09d536255d9d9cb9bfe58ebae2559843/addons/account/models/account_partial_reconcile.py#L585 Then we loop to reconcile all the `" account.move.line "` which are in the dict `" to_reconcile_after "`, but since we added them all twice, at each turn of the loop we give them all as a parameter of the “reconcile” function without checking whether they are already reconciled or not. opw-2565934 closes odoo/odoo#74432 X-original-commit: fb2b1482f5d3dec924b08e08dc915d6608c7d6b3 Signed-off-by: Laurent Smet Signed-off-by: Djamel Touati --- .../models/account_partial_reconcile.py | 11 +++- addons/hr_expense/tests/test_expenses.py | 56 ++++++++++++++++++- 2 files changed, 64 insertions(+), 3 deletions(-) diff --git a/addons/account/models/account_partial_reconcile.py b/addons/account/models/account_partial_reconcile.py index c3b43ccc714..e32c9727f96 100644 --- a/addons/account/models/account_partial_reconcile.py +++ b/addons/account/models/account_partial_reconcile.py @@ -503,13 +503,20 @@ class AccountPartialReconcile(models.Model): moves._post(soft=False) # Reconcile the tax lines being on a reconcile tax basis transfer account. - for line, move_index, sequence in to_reconcile_after: + for lines, move_index, sequence in to_reconcile_after: + + # In expenses, all move lines are created manually without any grouping on tax lines. + # In that case, 'lines' could be already reconciled. + lines = lines.filtered(lambda x: not x.reconciled) + if not lines: + continue + counterpart_line = moves[move_index].line_ids.filtered(lambda line: line.sequence == sequence) # When dealing with tiny amounts, the line could have a zero amount and then, be already reconciled. if counterpart_line.reconciled: continue - (line + counterpart_line).reconcile() + (lines + counterpart_line).reconcile() return moves diff --git a/addons/hr_expense/tests/test_expenses.py b/addons/hr_expense/tests/test_expenses.py index 9ba6e66fd33..77f8250271e 100644 --- a/addons/hr_expense/tests/test_expenses.py +++ b/addons/hr_expense/tests/test_expenses.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. from odoo.addons.hr_expense.tests.common import TestExpenseCommon -from odoo.tests import tagged +from odoo.tests import tagged, Form from odoo import fields @@ -223,3 +223,57 @@ class TestExpenses(TestExpenseCommon): # Assert not "Cannot create unbalanced journal entry" error. expense.action_sheet_move_create() + + def test_reconcile_payment(self): + tax = self.env['account.tax'].create({ + 'name': 'tax abc', + 'type_tax_use': 'purchase', + 'amount_type': 'percent', + 'amount': 15, + 'price_include': False, + 'include_base_amount': False, + 'tax_exigibility': 'on_payment' + }) + current_assets_type = self.env.ref('account.data_account_type_current_assets') + company = self.env.company.id + tax.cash_basis_transition_account_id = self.env['account.account'].create({ + 'name': "test", + 'code': 999991, + 'reconcile': True, + 'user_type_id': current_assets_type.id, + 'company_id': company, + }).id + + sheet = self.env['hr.expense.sheet'].create({ + 'company_id': company, + 'employee_id': self.expense_employee.id, + 'name': 'test sheet', + 'expense_line_ids': [ + (0, 0, { + 'name': 'expense_1', + 'date': '2016-01-01', + 'product_id': self.product_a.id, + 'unit_amount': 10.0, + 'employee_id': self.expense_employee.id, + 'tax_ids': tax + }), + (0, 0, { + 'name': 'expense_2', + 'date': '2016-01-01', + 'product_id': self.product_a.id, + 'unit_amount': 1.0, + 'employee_id': self.expense_employee.id, + 'tax_ids': tax + }), + ], + }) + + + #actions + sheet.action_submit_sheet() + sheet.approve_expense_sheets() + sheet.action_sheet_move_create() + action_data = sheet.action_register_payment() + wizard = Form(self.env['account.payment.register'].with_context(action_data['context'])).save() + wizard.action_create_payments() + self.assertEqual(sheet.state, 'done', 'all account.move.line linked to expenses must be reconciled after payment')