From 029a1ab2cc995890f99a431a4e5d043f25ce5e1c Mon Sep 17 00:00:00 2001 From: "Julien Alardot (jual)" Date: Tue, 11 Jul 2023 11:41:16 +0000 Subject: [PATCH] [FIX] hr_expense: Fix rounding mismatch When paid by the company in another currency with at least one tax, there may be a rounding difference between the move and the expense task-3390444 closes odoo/odoo#130397 X-original-commit: c889dcc83daeaccb09b7692e430e232778fc031d Signed-off-by: Laurent Smet (las) Signed-off-by: Julien Alardot (jual) --- addons/hr_expense/models/hr_expense.py | 69 +++++++++++------------- addons/hr_expense/tests/test_expenses.py | 58 ++++++++++---------- 2 files changed, 59 insertions(+), 68 deletions(-) diff --git a/addons/hr_expense/models/hr_expense.py b/addons/hr_expense/models/hr_expense.py index 969aec4caf1..1ebfe49499a 100644 --- a/addons/hr_expense/models/hr_expense.py +++ b/addons/hr_expense/models/hr_expense.py @@ -657,59 +657,51 @@ class HrExpense(models.Model): ('company_id', '=', self.company_id.id)], limit=1) if not payment_method_line: raise UserError(_("You need to add a manual payment method on the journal (%s)", journal.name)) - if self.currency_id != self.company_currency_id: - total_amount_currency = self.total_amount - rate = total_amount_currency / self.total_amount_company if self.total_amount_company else 1.0 - else: - total_amount_currency = self.total_amount_company - rate = 1 move_lines = [] # Due to rounding and conversion mismatch between vendor bills and payments, we have to force the computation into company account - amount_currency_diff = total_amount_currency - last_expense_line = None # Used for rounding in currency issues - tax_data = self.env['account.tax']._compute_taxes([self._convert_to_tax_base_line_dict(currency=self.sheet_id.currency_id)]) - for base_line_data, update in tax_data['base_lines_to_update']: # Add base lines - base_line_data.update(update) - amount_currency = self.currency_id.round(base_line_data['price_subtotal'] * rate) - expense_name = self.name.split("\n")[0][:64] - last_expense_line = base_move_line = { - 'name': f'{self.employee_id.name}: {expense_name}', - 'account_id': base_line_data['account'].id, - 'analytic_distribution': base_line_data['analytic_distribution'], - 'expense_id': self.id, - 'tax_ids': [Command.set(self.tax_ids.ids)], - 'tax_tag_ids': base_line_data['tax_tag_ids'], - 'balance': base_line_data['price_subtotal'], - 'amount_currency': amount_currency, - 'currency_id': self.currency_id.id, - 'product_id': base_line_data['product'].id, - } - amount_currency_diff -= amount_currency - move_lines.append(base_move_line) + tax_data = self.env['account.tax']._compute_taxes([ + self._convert_to_tax_base_line_dict(price_unit=self.total_amount, currency=self.currency_id) + ]) + rate = abs(self.total_amount / self.total_amount_company) + base_line_data, to_update = tax_data['base_lines_to_update'][0] # Add base line + amount_currency = to_update['price_subtotal'] + expense_name = self.name.split("\n")[0][:64] + base_move_line = { + 'name': f'{self.employee_id.name}: {expense_name}', + 'account_id': base_line_data['account'].id, + 'product_id': base_line_data['product'].id, + 'analytic_distribution': base_line_data['analytic_distribution'], + 'expense_id': self.id, + 'tax_ids': [Command.set(self.tax_ids.ids)], + 'tax_tag_ids': to_update['tax_tag_ids'], + 'amount_currency': amount_currency, + 'currency_id': self.currency_id.id, + } + move_lines.append(base_move_line) + total_tax_line_balance = 0.0 for tax_line_data in tax_data['tax_lines_to_add']: # Add tax lines - amount_currency = self.sheet_id.currency_id.round(tax_line_data['tax_amount'] * rate) - last_expense_line = tax_line = { + tax_line_balance = self.company_currency_id.round(tax_line_data['tax_amount'] / rate) + total_tax_line_balance += tax_line_balance + tax_line = { 'name': self.env['account.tax'].browse(tax_line_data['tax_id']).name, 'account_id': tax_line_data['account_id'], 'analytic_distribution': tax_line_data['analytic_distribution'], 'expense_id': self.id, 'tax_tag_ids': tax_line_data['tax_tag_ids'], - 'balance': tax_line_data['tax_amount'], - 'amount_currency': amount_currency, - 'tax_base_amount': tax_line_data['base_amount'], + 'balance': tax_line_balance, + 'amount_currency': tax_line_data['tax_amount'], + 'tax_base_amount': self.company_currency_id.round(tax_line_data['base_amount'] / rate), 'currency_id': self.currency_id.id, 'tax_repartition_line_id': tax_line_data['tax_repartition_line_id'], } move_lines.append(tax_line) - amount_currency_diff -= amount_currency - if not self.sheet_id.currency_id.is_zero(amount_currency_diff) and last_expense_line: - last_expense_line['amount_currency'] += amount_currency_diff + base_move_line['balance'] = self.total_amount_company - total_tax_line_balance expense_name = self.name.split("\n")[0][:64] move_lines.append({ # Add outstanding payment line 'name': f'{self.employee_id.name}: {expense_name}', 'account_id': self.sheet_id._get_expense_account_destination(), 'balance': -self.total_amount_company, - 'amount_currency': self.currency_id.round(-total_amount_currency), + 'amount_currency': self.currency_id.round(-self.total_amount), 'currency_id': self.currency_id.id, }) return { @@ -717,7 +709,7 @@ class HrExpense(models.Model): 'ref': self.name, 'journal_id': journal.id, 'move_type': 'entry', - 'amount': total_amount_currency, + 'amount': self.total_amount, 'payment_type': 'outbound', 'partner_type': 'supplier', 'payment_method_line_id': payment_method_line.id, @@ -1497,8 +1489,7 @@ class HrExpenseSheet(models.Model): moves = self.env['account.move'].create([sheet._prepare_bills_vals() for sheet in own_account_sheets]) payments = self.env['account.payment'].with_context(**skip_context).create([expense._prepare_payments_vals() for expense in company_account_sheets.expense_line_ids]) - payment_moves = payments.move_id - moves |= payment_moves + moves |= payments.move_id moves.action_post() self.activity_update() diff --git a/addons/hr_expense/tests/test_expenses.py b/addons/hr_expense/tests/test_expenses.py index 88a9e1bcb6b..34a64a1f968 100644 --- a/addons/hr_expense/tests/test_expenses.py +++ b/addons/hr_expense/tests/test_expenses.py @@ -469,7 +469,7 @@ class TestExpenses(TestExpenseCommon): }) self.env['res.currency.rate'].create({ 'name': '2016-01-01', - 'rate': 1/1.52890, + 'rate': 1/0.148431, 'currency_id': foreign_currency.id, 'company_id': self.company_data['company'].id, }) @@ -493,10 +493,10 @@ class TestExpenses(TestExpenseCommon): 'name': 'Own expense', 'date': '2022-11-16', 'payment_mode': 'own_account', - 'total_amount': 1000, + 'total_amount': 100, 'employee_id': self.expense_employee.id, 'product_id': self.product_c.id, - 'currency_id': foreign_currency.id, # rate is 1:1.5289 + 'currency_id': foreign_currency.id, # rate is 1:0.148431 'tax_ids': [Command.set(tax.ids)], })], }) @@ -509,10 +509,10 @@ class TestExpenses(TestExpenseCommon): 'name': 'Own expense', 'date': '2022-11-17', 'payment_mode': 'own_account', - 'total_amount': 1000, + 'total_amount': 100, 'employee_id': self.expense_employee.id, 'product_id': self.product_c.id, - 'currency_id': foreign_currency.id, # rate is 1:1.5289 + 'currency_id': foreign_currency.id, # rate is 1:0.148431 'tax_ids': [Command.set(taxes.ids)], })], }) @@ -525,10 +525,10 @@ class TestExpenses(TestExpenseCommon): 'name': 'Company expense', 'date': '2022-11-18', 'payment_mode': 'company_account', - 'total_amount': 1000, + 'total_amount': 100, 'employee_id': self.expense_employee.id, 'product_id': self.product_c.id, - 'currency_id': foreign_currency.id, # rate is 1:1.5289 + 'currency_id': foreign_currency.id, # rate is 1:0.148431 'tax_ids': [Command.set(tax.ids)], })], }) @@ -541,48 +541,48 @@ class TestExpenses(TestExpenseCommon): 'name': 'Company expense', 'date': '2022-11-19', 'payment_mode': 'company_account', - 'total_amount': 1000, + 'total_amount': 100, 'employee_id': self.expense_employee.id, 'product_id': self.product_c.id, - 'currency_id': foreign_currency.id, # rate is 1:1.5289 + 'currency_id': foreign_currency.id, # rate is 1:0.148431 'tax_ids': [Command.set(taxes.ids)], })], }) sheets = expense_sheet_own_1_tax + expense_sheet_own_2_tax + expense_sheet_company_1_tax + expense_sheet_company_2_tax self.assertRecordValues(sheets.expense_line_ids, [ - {'untaxed_amount': 869.57, 'total_amount': 1000, 'total_amount_company': 1528.90, 'amount_tax': 130.43, 'amount_tax_company': 199.42}, - {'untaxed_amount': 769.23, 'total_amount': 1000, 'total_amount_company': 1528.90, 'amount_tax': 230.77, 'amount_tax_company': 352.82}, - {'untaxed_amount': 869.57, 'total_amount': 1000, 'total_amount_company': 1528.90, 'amount_tax': 130.43, 'amount_tax_company': 199.42}, - {'untaxed_amount': 769.23, 'total_amount': 1000, 'total_amount_company': 1528.90, 'amount_tax': 230.77, 'amount_tax_company': 352.82}, + {'untaxed_amount': 86.96, 'total_amount': 100.00, 'total_amount_company': 14.84, 'amount_tax': 13.04, 'amount_tax_company': 1.94}, + {'untaxed_amount': 76.92, 'total_amount': 100.00, 'total_amount_company': 14.84, 'amount_tax': 23.08, 'amount_tax_company': 3.42}, + {'untaxed_amount': 86.96, 'total_amount': 100.00, 'total_amount_company': 14.84, 'amount_tax': 13.04, 'amount_tax_company': 1.94}, + {'untaxed_amount': 76.92, 'total_amount': 100.00, 'total_amount_company': 14.84, 'amount_tax': 23.08, 'amount_tax_company': 3.42}, ]) sheets.action_submit_sheet() sheets.action_approve_expense_sheets() sheets.action_sheet_move_create() self.assertRecordValues(expense_sheet_own_1_tax.account_move_ids.line_ids, [ - {'balance': 1329.48, 'amount_currency': 1329.48, 'currency_id': self.company_data['currency'].id}, - {'balance': 199.42, 'amount_currency': 199.42, 'currency_id': self.company_data['currency'].id}, - {'balance': -1528.90, 'amount_currency': -1528.90, 'currency_id': self.company_data['currency'].id}, + {'balance': 12.90, 'amount_currency': 12.90, 'currency_id': self.company_data['currency'].id}, + {'balance': 1.94, 'amount_currency': 1.94, 'currency_id': self.company_data['currency'].id}, + {'balance': -14.84, 'amount_currency': -14.84, 'currency_id': self.company_data['currency'].id}, ]) self.assertRecordValues(expense_sheet_own_2_tax.account_move_ids.line_ids, [ - {'balance': 1176.08, 'amount_currency': 1176.08, 'currency_id': self.company_data['currency'].id}, - {'balance': 176.41, 'amount_currency': 176.41, 'currency_id': self.company_data['currency'].id}, - {'balance': 176.41, 'amount_currency': 176.41, 'currency_id': self.company_data['currency'].id}, # == 352.82 amount_tax_company - {'balance': -1528.90, 'amount_currency': -1528.90, 'currency_id': self.company_data['currency'].id}, + {'balance': 11.42, 'amount_currency': 11.42, 'currency_id': self.company_data['currency'].id}, + {'balance': 1.71, 'amount_currency': 1.71, 'currency_id': self.company_data['currency'].id}, + {'balance': 1.71, 'amount_currency': 1.71, 'currency_id': self.company_data['currency'].id}, # == 3.42 amount_tax_company + {'balance': -14.84, 'amount_currency': -14.84, 'currency_id': self.company_data['currency'].id}, ]) self.assertRecordValues(expense_sheet_company_1_tax.account_move_ids.line_ids, [ - {'balance': 1329.48, 'amount_currency': 869.57, 'currency_id': foreign_currency.id}, - {'balance': 199.42, 'amount_currency': 130.43, 'currency_id': foreign_currency.id}, - {'balance': -1528.90, 'amount_currency': -1000.00, 'currency_id': foreign_currency.id}, + {'balance': 12.90, 'amount_currency': 86.96, 'currency_id': foreign_currency.id}, + {'balance': 1.94, 'amount_currency': 13.04, 'currency_id': foreign_currency.id}, + {'balance': -14.84, 'amount_currency': -100.00, 'currency_id': foreign_currency.id}, ]) self.assertRecordValues(expense_sheet_company_2_tax.account_move_ids.line_ids, [ - {'balance': 1176.08, 'amount_currency': 769.23, 'currency_id': foreign_currency.id}, - {'balance': 176.41, 'amount_currency': 115.38, 'currency_id': foreign_currency.id}, # == 352.82 amount_tax_company & 230.77 amount_tax - {'balance': 176.41, 'amount_currency': 115.39, 'currency_id': foreign_currency.id}, # One cent more in currency due to rounding - {'balance': -1528.90, 'amount_currency': -1000.00, 'currency_id': foreign_currency.id}, + {'balance': 11.42, 'amount_currency': 76.92, 'currency_id': foreign_currency.id}, + {'balance': 1.71, 'amount_currency': 11.54, 'currency_id': foreign_currency.id}, # == 3.42 amount_tax_company & 23.08 amount_tax + {'balance': 1.71, 'amount_currency': 11.54, 'currency_id': foreign_currency.id}, # One cent more in currency due to rounding + {'balance': -14.84, 'amount_currency': -100.00, 'currency_id': foreign_currency.id}, ]) @@ -1397,8 +1397,8 @@ class TestExpenses(TestExpenseCommon): {'balance': 130.43, 'amount_currency': 130.43, 'currency_id': self.company_data['currency'].id}, {'balance': -1000.00, 'amount_currency': -1000.00, 'currency_id': self.company_data['currency'].id}, # Second move - {'balance': 1329.48, 'amount_currency': 869.57, 'currency_id': foreign_currency.id}, - {'balance': 199.42, 'amount_currency': 130.43, 'currency_id': foreign_currency.id}, + {'balance': 1329.49, 'amount_currency': 869.57, 'currency_id': foreign_currency.id}, # Values are different due conversion issues + {'balance': 199.41, 'amount_currency': 130.43, 'currency_id': foreign_currency.id}, {'balance': -1528.90, 'amount_currency': -1000.00, 'currency_id': foreign_currency.id}, ])