From 274fc3ea706a2f7ef6849fc486c7157f8f59293d Mon Sep 17 00:00:00 2001 From: "Loan (LSE)" <lse@odoo.com> Date: Fri, 9 Jun 2023 15:26:48 +0000 Subject: [PATCH] [FIX] account: fix inconsistencies in `_sync_dynamic_line` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To reproduce (V16): 0. Install Accounting (can also be reproduced with just Invoicing, but there is no "journal item" menu that will ease the process to show the issue) 1. (In debug), change the dollar currency rounding to 0.0001 (instead of 0.01) 2. Go to Accounting > Customers > Invoices 3. Create a first invoice with: - a partner P1 - set an invoice date - make sure that there is a payment term set (for example "30 days") - at least one invoiced line (the product does not matter) with the tax 15 % and the price to 12.36 DO NOT confirm the invoice 4. Create another invoice with - ANOTHER partner P2 - set an invoice date - make sure that there is a payment term set (for example "30 days") - at least one invoiced line (the product and the price does not matter) 5. Go back to the Invoices list view and select the 2 new (unposted) invoices then Action > Post entries. 6. Check Force and "Post Journal Entries" 7. Go to Accounting > Reporting > Journal Items 8. Group by "Journal Entry" and unfold the 2 last invoices (that should be the one validated in 6.) => The "Partner" set on the Debit Line is the one of the other invoice (and vice versa) Analyse: This issue is a mix of different sub-issues. Solving any of this sub-issue would solve the issue but as other sub-issues may exists we will fix each of them individually. Sub-issue 1: The account.move.line (=AML) partner is set correctly before the post (6.), this is during the "mass posting" process that the issue happens. The partner is changed because of this line: https://github.com/odoo/odoo/blob/8006e7ce618f98bd26a82cf9b64eaa9b6e9c9d2a/addons/account/models/account_move.py#L2094-L2096 The value written to AML of id `line_id` include `move_id` which overwrite the account.move that was originally set on the AML. As the `partner` is computed only in certain circumstances, if the `move_id` written differ from the one of the AML, most of the values will be updated with the write values, but the `partner_id` will keep its value (this is why the swap of partner happen). In other words, this issue happen as `line_id.move_id` order is different from `key["move_id"]`. So we write on the "wrong" AML. Sub-issue 2: In theory, this issue should not have happened in the first place. The line: https://github.com/odoo/odoo/blob/8006e7ce618f98bd26a82cf9b64eaa9b6e9c9d2a/addons/account/models/account_move.py#L2061 should have prevent the re-edition of the AML (as actually no relevant datas are written). But the equality between the dictionaries fail due to some rounding errors. Here: - needed_after[<key Invoice 1>]["balance"] --> 14.214000000000002 - needed_before[<key Invoice 1>]["balance"] -> 14.213999999999999 N.B: if we don't set an "invoice date" on the invoice, a rewrite is done before which would prevent our issue to reproduce. Sub-issue 3: If we try to reproduce the issue without setting a "payment term", the issue would not reproduce. By setting a "payment term" the `to_delete` computation change at: https://github.com/odoo/odoo/blob/8006e7ce618f98bd26a82cf9b64eaa9b6e9c9d2a/addons/account/models/account_move.py#L2067 This happens as the `existing_before` keys and `needed_after` keys are different: - existing_before[<key Invoice 1>]["discount_date"] -> False - needed_after[<key Invoice 1>]["discount_date"] ----> None As the values are different, Odoo all the AML to the `to_delete` which then trigger their overwrite. In other words, if no "payment terms" were set on the invoices, the issue would not happen either opw-3270471,3292931,3333799 closes odoo/odoo#124517 Signed-off-by: William André (wan) <wan@odoo.com> --- addons/account/models/account_move.py | 24 +++++++++++++++---- .../tests/test_account_move_out_invoice.py | 19 +++++++++++++++ 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/addons/account/models/account_move.py b/addons/account/models/account_move.py index 64ba66f6e6a6..27839d923f2d 100644 --- a/addons/account/models/account_move.py +++ b/addons/account/models/account_move.py @@ -2018,6 +2018,19 @@ class AccountMove(models.Model): ignore = False if ignore: del res[key] + + # Convert float values to their "ORM cache" one to prevent different rounding calculations + for dict_key in res: + move_id = dict_key.get('move_id') + if not move_id: + continue + record = self.env['account.move'].browse(move_id) + for fname, current_value in res[dict_key].items(): + field = self.env['account.move.line']._fields[fname] + if isinstance(current_value, float): + new_value = field.convert_to_cache(current_value, record) + res[dict_key][fname] = new_value + return res def dirty(): @@ -2061,17 +2074,18 @@ class AccountMove(models.Model): if needed_after == needed_before: return - to_delete = sorted({ + to_delete = [ line.id for key, line in existing_before.items() if key not in needed_after and key in existing_after and before2after[key] not in needed_after - } | { - line.id + ] + to_delete_set = set(to_delete) + to_delete.extend(line.id for key, line in existing_after.items() - if key not in needed_after - }) + if key not in needed_after and line.id not in to_delete_set + ) to_create = { key: values for key, values in needed_after.items() diff --git a/addons/account/tests/test_account_move_out_invoice.py b/addons/account/tests/test_account_move_out_invoice.py index 08fc96cb80fd..64efc9052117 100644 --- a/addons/account/tests/test_account_move_out_invoice.py +++ b/addons/account/tests/test_account_move_out_invoice.py @@ -3563,3 +3563,22 @@ class TestAccountMoveOutInvoiceOnchanges(AccountTestInvoicingCommon): # Date of the reversal of the exchange move should be the last day of the month/year of the payment depending on the sequence format self.assertEqual(exchange_move_reversal.date, fields.Date.to_date(expected_date)) + + def test_invoice_mass_posting(self): + """ + With some particular setup (in this case, rounding issue), partner get mixed up in the + invoice lines after mass posting. + """ + currency = self.company_data['currency'] + currency.rounding = 0.0001 + invoice1 = self.init_invoice(move_type='out_invoice', partner=self.partner_a, invoice_date='2016-01-20', products=self.product_a) + invoice1.invoice_line_ids.price_unit = 12.36 + invoice2 = self.init_invoice(move_type='out_invoice', partner=self.partner_b, invoice_date='2016-01-20', products=self.product_a) + + vam = self.env["validate.account.move"].create({"force_post": True}) + vam.with_context(active_model='account.move', active_ids=[invoice2.id, invoice1.id]).validate_move() + + for aml in invoice1.line_ids: + self.assertEqual(aml.partner_id, self.partner_a) + for aml in invoice2.line_ids: + self.assertEqual(aml.partner_id, self.partner_b) -- GitLab