From 608ab7dc55d772b50f524401e267238f9e318ca7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Duy=20=28=C4=90=E1=BB=97=20Anh=29?= Date: Tue, 12 Nov 2024 10:29:55 +0700 Subject: [PATCH 01/22] [IMP] account_reconcile_model_oca: excludes account_accountant module --- account_reconcile_model_oca/__manifest__.py | 1 + .../static/description/index.html | 11 +++++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/account_reconcile_model_oca/__manifest__.py b/account_reconcile_model_oca/__manifest__.py index 306e1ace..1a3478de 100644 --- a/account_reconcile_model_oca/__manifest__.py +++ b/account_reconcile_model_oca/__manifest__.py @@ -10,6 +10,7 @@ "author": "Dixmit,Odoo,Odoo Community Association (OCA)", "website": "https://github.com/OCA/account-reconcile", "depends": ["account"], + "excludes": ["account_accountant"], "data": [], "demo": [], } diff --git a/account_reconcile_model_oca/static/description/index.html b/account_reconcile_model_oca/static/description/index.html index 846d951a..c6920a8e 100644 --- a/account_reconcile_model_oca/static/description/index.html +++ b/account_reconcile_model_oca/static/description/index.html @@ -8,10 +8,11 @@ /* :Author: David Goodger (goodger@python.org) -:Id: $Id: html4css1.css 8954 2022-01-20 10:10:25Z milde $ +:Id: $Id: html4css1.css 9511 2024-01-13 09:50:07Z milde $ :Copyright: This stylesheet has been placed in the public domain. Default cascading style sheet for the HTML output of Docutils. +Despite the name, some widely supported CSS2 features are used. See https://docutils.sourceforge.io/docs/howto/html-stylesheets.html for how to customize this style sheet. @@ -274,7 +275,7 @@ pre.literal-block, pre.doctest-block, pre.math, pre.code { margin-left: 2em ; margin-right: 2em } -pre.code .ln { color: grey; } /* line numbers */ +pre.code .ln { color: gray; } /* line numbers */ pre.code, code { background-color: #eeeeee } pre.code .comment, code .comment { color: #5C6576 } pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold } @@ -300,7 +301,7 @@ span.option { span.pre { white-space: pre } -span.problematic { +span.problematic, pre.problematic { color: red } span.section-subtitle { @@ -412,7 +413,9 @@ If you spotted it first, help us to smash it by providing a detailed and welcome

Maintainers

This module is maintained by the OCA.

-Odoo Community Association + +Odoo Community Association +

OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use.

From f33ba2c50d660ef665698c5165067230ad073bce Mon Sep 17 00:00:00 2001 From: "Pedro M. Baeza" Date: Thu, 13 Feb 2025 20:34:45 +0000 Subject: [PATCH 02/22] Translated using Weblate (Spanish) Currently translated at 60.0% (12 of 20 strings) Translation: account-reconcile-17.0/account-reconcile-17.0-account_statement_base Translate-URL: https://translation.odoo-community.org/projects/account-reconcile-17-0/account-reconcile-17-0-account_statement_base/es/ --- account_statement_base/i18n/es.po | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/account_statement_base/i18n/es.po b/account_statement_base/i18n/es.po index 28052d01..888bbafb 100644 --- a/account_statement_base/i18n/es.po +++ b/account_statement_base/i18n/es.po @@ -6,15 +6,15 @@ msgid "" msgstr "" "Project-Id-Version: Odoo Server 16.0\n" "Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: 2023-07-29 12:10+0000\n" -"Last-Translator: Ivorra78 \n" +"PO-Revision-Date: 2025-02-13 20:35+0000\n" +"Last-Translator: \"Pedro M. Baeza\" \n" "Language-Team: none\n" "Language: es\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: \n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 4.17\n" +"X-Generator: Weblate 5.6.2\n" #. module: account_statement_base #: model_terms:ir.ui.view,arch_db:account_statement_base.view_bank_statement_form @@ -63,7 +63,7 @@ msgstr "Etiqueta, referencia o notas" #. module: account_statement_base #: model_terms:ir.ui.view,arch_db:account_statement_base.account_bank_statement_line_search msgid "Not Reconciled" -msgstr "No reconciliado" +msgstr "No conciliado" #. module: account_statement_base #: model_terms:ir.ui.view,arch_db:account_statement_base.account_bank_statement_line_form @@ -89,7 +89,7 @@ msgstr "Socio" #. module: account_statement_base #: model_terms:ir.ui.view,arch_db:account_statement_base.account_bank_statement_line_search msgid "Reconciled" -msgstr "Reconciliado" +msgstr "Conciliado" #. module: account_statement_base #: model_terms:ir.ui.view,arch_db:account_statement_base.account_bank_statement_line_tree From dcd9a75d5d98074f82085768c4595b7ee07642bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Mart=C3=ADnez?= Date: Thu, 20 Feb 2025 09:39:53 +0100 Subject: [PATCH 03/22] [FIX] account_reconcile_oca: Replace name_get() to delete the warning log WARNING prod py.warnings: /opt/odoo/auto/addons/account_reconcile_oca/models/account_bank_statement_line.py:616: DeprecationWarning: Since 17.0, deprecated method, read display_name instead --- account_reconcile_oca/models/account_bank_statement_line.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/account_reconcile_oca/models/account_bank_statement_line.py b/account_reconcile_oca/models/account_bank_statement_line.py index ffb78029..c8285a0a 100644 --- a/account_reconcile_oca/models/account_bank_statement_line.py +++ b/account_reconcile_oca/models/account_bank_statement_line.py @@ -613,7 +613,10 @@ class AccountBankStatementLine(models.Model): self.env["res.partner"].browse(line["partner_id"]).display_name, ) elif self.partner_id: - new_line["partner_id"] = self.partner_id.name_get()[0] + new_line["partner_id"] = ( + self.partner_id.id, + self.partner_id.display_name, + ) new_data.append(new_line) return new_data, reconcile_auxiliary_id From 2c5e7accb6179ae30c190f4e262da73ecbd006b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Mart=C3=ADnez?= Date: Thu, 20 Feb 2025 09:54:58 +0100 Subject: [PATCH 04/22] [FIX] account_reconcile_oca: Set the appropriate value for partner_id to avoid warning log WARNING prod py.warnings: /opt/odoo/auto/addons/account/models/account_move.py:4651: UserWarning: unsupported operand type(s) for "==": 'res.partner()' == '403' --- account_reconcile_oca/models/account_bank_statement_line.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/account_reconcile_oca/models/account_bank_statement_line.py b/account_reconcile_oca/models/account_bank_statement_line.py index c8285a0a..bf1fb530 100644 --- a/account_reconcile_oca/models/account_bank_statement_line.py +++ b/account_reconcile_oca/models/account_bank_statement_line.py @@ -1020,7 +1020,7 @@ class AccountBankStatementLine(models.Model): suspense_lines, _other_lines, ) = st_line._seek_for_lines() - line_vals = {"partner_id": st_line.partner_id} + line_vals = {"partner_id": st_line.partner_id.id} line_ids_commands = [(1, liquidity_lines.id, line_vals)] if suspense_lines: line_ids_commands.append((1, suspense_lines.id, line_vals)) From 8203ea2020cfd1d2733e300ed314754d4c5973fc Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Thu, 20 Feb 2025 09:10:19 +0000 Subject: [PATCH 05/22] [BOT] post-merge updates --- README.md | 2 +- account_reconcile_oca/README.rst | 2 +- account_reconcile_oca/__manifest__.py | 2 +- account_reconcile_oca/static/description/index.html | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 120ac789..e6d83b4d 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ addon | version | maintainers | summary [account_in_payment](account_in_payment/) | 17.0.1.0.0 | | This module enables in-payment mode for your accounting [account_mass_reconcile](account_mass_reconcile/) | 17.0.1.0.1 | | Account Mass Reconcile [account_reconcile_model_oca](account_reconcile_model_oca/) | 17.0.1.0.2 | | This includes the logic moved from Odoo Community to Odoo Enterprise -[account_reconcile_oca](account_reconcile_oca/) | 17.0.1.5.5 | [![etobella](https://github.com/etobella.png?size=30px)](https://github.com/etobella) | Reconcile addons for Odoo CE accounting +[account_reconcile_oca](account_reconcile_oca/) | 17.0.1.5.6 | [![etobella](https://github.com/etobella.png?size=30px)](https://github.com/etobella) | Reconcile addons for Odoo CE accounting [account_statement_base](account_statement_base/) | 17.0.1.5.0 | [![alexis-via](https://github.com/alexis-via.png?size=30px)](https://github.com/alexis-via) | Base module for Bank Statements [//]: # (end addons) diff --git a/account_reconcile_oca/README.rst b/account_reconcile_oca/README.rst index ac588432..ceaf846b 100644 --- a/account_reconcile_oca/README.rst +++ b/account_reconcile_oca/README.rst @@ -7,7 +7,7 @@ Account Reconcile Oca !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! source digest: sha256:cf0d778067ac722c5a6d7f65f8fa4b0766076829ba9c9c147cc3718782cc85b0 + !! source digest: sha256:fa8f7f5af2e7a631cb39b699ebbc41f1bc6cca067be28665f5d4590c752f3054 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png diff --git a/account_reconcile_oca/__manifest__.py b/account_reconcile_oca/__manifest__.py index 06df5d9b..b34888a2 100644 --- a/account_reconcile_oca/__manifest__.py +++ b/account_reconcile_oca/__manifest__.py @@ -5,7 +5,7 @@ "name": "Account Reconcile Oca", "summary": """ Reconcile addons for Odoo CE accounting""", - "version": "17.0.1.5.5", + "version": "17.0.1.5.6", "license": "AGPL-3", "author": "CreuBlanca,Dixmit,Odoo Community Association (OCA)", "maintainers": ["etobella"], diff --git a/account_reconcile_oca/static/description/index.html b/account_reconcile_oca/static/description/index.html index 0e3f271c..280f37db 100644 --- a/account_reconcile_oca/static/description/index.html +++ b/account_reconcile_oca/static/description/index.html @@ -367,7 +367,7 @@ ul.auto-toc { !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -!! source digest: sha256:cf0d778067ac722c5a6d7f65f8fa4b0766076829ba9c9c147cc3718782cc85b0 +!! source digest: sha256:fa8f7f5af2e7a631cb39b699ebbc41f1bc6cca067be28665f5d4590c752f3054 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->

Beta License: AGPL-3 OCA/account-reconcile Translate me on Weblate Try me on Runboat

This addon allows to reconcile bank statements and account marked as From d2623dd7cc4222f4e01da22a1d26021b3b9daab6 Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Tue, 25 Feb 2025 20:56:08 +0000 Subject: [PATCH 06/22] [BOT] post-merge updates --- README.md | 2 +- account_reconcile_model_oca/README.rst | 2 +- account_reconcile_model_oca/__manifest__.py | 2 +- account_reconcile_model_oca/static/description/index.html | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index e6d83b4d..431cd75a 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ addon | version | maintainers | summary --- | --- | --- | --- [account_in_payment](account_in_payment/) | 17.0.1.0.0 | | This module enables in-payment mode for your accounting [account_mass_reconcile](account_mass_reconcile/) | 17.0.1.0.1 | | Account Mass Reconcile -[account_reconcile_model_oca](account_reconcile_model_oca/) | 17.0.1.0.2 | | This includes the logic moved from Odoo Community to Odoo Enterprise +[account_reconcile_model_oca](account_reconcile_model_oca/) | 17.0.1.0.3 | | This includes the logic moved from Odoo Community to Odoo Enterprise [account_reconcile_oca](account_reconcile_oca/) | 17.0.1.5.6 | [![etobella](https://github.com/etobella.png?size=30px)](https://github.com/etobella) | Reconcile addons for Odoo CE accounting [account_statement_base](account_statement_base/) | 17.0.1.5.0 | [![alexis-via](https://github.com/alexis-via.png?size=30px)](https://github.com/alexis-via) | Base module for Bank Statements diff --git a/account_reconcile_model_oca/README.rst b/account_reconcile_model_oca/README.rst index d76f5538..b622f4c1 100644 --- a/account_reconcile_model_oca/README.rst +++ b/account_reconcile_model_oca/README.rst @@ -7,7 +7,7 @@ Account Reconcile Model Oca !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! source digest: sha256:80bb08dc3058116c364563a7014c16787db3ac0b12afadbde03716f7277fa298 + !! source digest: sha256:47f1e5b39eb79210fe77d7228768cfedf31f124a30046f15ca7bb2bc33c34a9d !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png diff --git a/account_reconcile_model_oca/__manifest__.py b/account_reconcile_model_oca/__manifest__.py index 242ad6cc..bc263a62 100644 --- a/account_reconcile_model_oca/__manifest__.py +++ b/account_reconcile_model_oca/__manifest__.py @@ -5,7 +5,7 @@ "name": "Account Reconcile Model Oca", "summary": """ This includes the logic moved from Odoo Community to Odoo Enterprise""", - "version": "17.0.1.0.2", + "version": "17.0.1.0.3", "license": "LGPL-3", "author": "Dixmit,Odoo,Odoo Community Association (OCA)", "website": "https://github.com/OCA/account-reconcile", diff --git a/account_reconcile_model_oca/static/description/index.html b/account_reconcile_model_oca/static/description/index.html index a7e54853..981036d7 100644 --- a/account_reconcile_model_oca/static/description/index.html +++ b/account_reconcile_model_oca/static/description/index.html @@ -367,7 +367,7 @@ ul.auto-toc { !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -!! source digest: sha256:80bb08dc3058116c364563a7014c16787db3ac0b12afadbde03716f7277fa298 +!! source digest: sha256:47f1e5b39eb79210fe77d7228768cfedf31f124a30046f15ca7bb2bc33c34a9d !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->

Beta License: LGPL-3 OCA/account-reconcile Translate me on Weblate Try me on Runboat

This module restores account reconciliation models functions moved from From f9341aeda74b8abda9b2d9f8a203a56ab1e89cc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Mart=C3=ADnez?= Date: Mon, 24 Feb 2025 13:03:02 +0100 Subject: [PATCH 07/22] [FIX] account_reconcile_oca: Avoiding the singleton error related to _get_reconcile_line() Example use case: File "/opt/odoo/auto/addons/account_reconcile_oca/models/account_bank_statement_line.py", line 541, in _compute_reconcile_data_info record.reconcile_data_info = record._default_reconcile_data( File "/opt/odoo/auto/addons/account_reconcile_oca/models/account_bank_statement_line.py", line 698, in _default_reconcile_data reconcile_auxiliary_id, lines = self._get_reconcile_line( File "/opt/odoo/auto/addons/account_reconcile_oca/models/account_bank_statement_line.py", line 1164, in _get_reconcile_line new_vals = super()._get_reconcile_line( File "/opt/odoo/auto/addons/account_reconcile_oca/models/account_reconcile_abstract.py", line 49, in _get_reconcile_line original_amount = amount = net_amount = line.debit - line.credit File "/opt/odoo/custom/src/odoo/odoo/fields.py", line 1154, in __get__ record.ensure_one() File "/opt/odoo/custom/src/odoo/odoo/models.py", line 5204, in ensure_one raise ValueError("Expected singleton: %s" % self) ValueError: Expected singleton: account.move.line(208, 211, 212) TT55221 --- .../models/account_bank_statement_line.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/account_reconcile_oca/models/account_bank_statement_line.py b/account_reconcile_oca/models/account_bank_statement_line.py index bf1fb530..65817970 100644 --- a/account_reconcile_oca/models/account_bank_statement_line.py +++ b/account_reconcile_oca/models/account_bank_statement_line.py @@ -684,13 +684,16 @@ class AccountBankStatementLine(models.Model): reconciled_line.move_id.journal_id == self.company_id.currency_exchange_journal_id ): - reconcile_auxiliary_id, lines = self._get_reconcile_line( - reconciled_line.move_id.line_ids - reconciled_line, - "other", - from_unreconcile=False, - move=True, - ) - data += lines + for rl_item in ( + reconciled_line.move_id.line_ids - reconciled_line + ): + reconcile_auxiliary_id, lines = self._get_reconcile_line( + rl_item, + "other", + from_unreconcile=False, + move=True, + ) + data += lines continue partial = partial_lines.filtered( lambda r, line=reconciled_line: r.debit_move_id == line From 836417b15062f1c1df2388ae463c08ade8b51f57 Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Wed, 26 Feb 2025 16:48:44 +0000 Subject: [PATCH 08/22] [BOT] post-merge updates --- README.md | 2 +- account_reconcile_oca/README.rst | 2 +- account_reconcile_oca/__manifest__.py | 2 +- account_reconcile_oca/static/description/index.html | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 431cd75a..cbf91245 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ addon | version | maintainers | summary [account_in_payment](account_in_payment/) | 17.0.1.0.0 | | This module enables in-payment mode for your accounting [account_mass_reconcile](account_mass_reconcile/) | 17.0.1.0.1 | | Account Mass Reconcile [account_reconcile_model_oca](account_reconcile_model_oca/) | 17.0.1.0.3 | | This includes the logic moved from Odoo Community to Odoo Enterprise -[account_reconcile_oca](account_reconcile_oca/) | 17.0.1.5.6 | [![etobella](https://github.com/etobella.png?size=30px)](https://github.com/etobella) | Reconcile addons for Odoo CE accounting +[account_reconcile_oca](account_reconcile_oca/) | 17.0.1.5.7 | [![etobella](https://github.com/etobella.png?size=30px)](https://github.com/etobella) | Reconcile addons for Odoo CE accounting [account_statement_base](account_statement_base/) | 17.0.1.5.0 | [![alexis-via](https://github.com/alexis-via.png?size=30px)](https://github.com/alexis-via) | Base module for Bank Statements [//]: # (end addons) diff --git a/account_reconcile_oca/README.rst b/account_reconcile_oca/README.rst index ceaf846b..b93b73a0 100644 --- a/account_reconcile_oca/README.rst +++ b/account_reconcile_oca/README.rst @@ -7,7 +7,7 @@ Account Reconcile Oca !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! source digest: sha256:fa8f7f5af2e7a631cb39b699ebbc41f1bc6cca067be28665f5d4590c752f3054 + !! source digest: sha256:b17130b1fa64185780d85cb252c1af8cf33bbc9745ce2a82e13263c86619e417 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png diff --git a/account_reconcile_oca/__manifest__.py b/account_reconcile_oca/__manifest__.py index b34888a2..d1129a26 100644 --- a/account_reconcile_oca/__manifest__.py +++ b/account_reconcile_oca/__manifest__.py @@ -5,7 +5,7 @@ "name": "Account Reconcile Oca", "summary": """ Reconcile addons for Odoo CE accounting""", - "version": "17.0.1.5.6", + "version": "17.0.1.5.7", "license": "AGPL-3", "author": "CreuBlanca,Dixmit,Odoo Community Association (OCA)", "maintainers": ["etobella"], diff --git a/account_reconcile_oca/static/description/index.html b/account_reconcile_oca/static/description/index.html index 280f37db..1dcc91c3 100644 --- a/account_reconcile_oca/static/description/index.html +++ b/account_reconcile_oca/static/description/index.html @@ -367,7 +367,7 @@ ul.auto-toc { !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -!! source digest: sha256:fa8f7f5af2e7a631cb39b699ebbc41f1bc6cca067be28665f5d4590c752f3054 +!! source digest: sha256:b17130b1fa64185780d85cb252c1af8cf33bbc9745ce2a82e13263c86619e417 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->

Beta License: AGPL-3 OCA/account-reconcile Translate me on Weblate Try me on Runboat

This addon allows to reconcile bank statements and account marked as From 48b4502a2c5c11b51ccd89360b52180034476a05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Mart=C3=ADnez?= Date: Mon, 31 Mar 2025 08:27:50 +0200 Subject: [PATCH 09/22] [FIX] account_reconcile_model_oca: fix partner category reconciliation when partner has multiple categories * Create a reconciliation model that matches partners that have a category `C` * Create a bank statement and a partner that has *multiple* categories, including C. * Validate and reconcile The reconciliation model you created should be used, but it is not. Related to https://github.com/odoo/odoo/commit/a0016fc6c98a0978a3a1f57fe99497c54c333bbc --- .../models/account_reconcile_model.py | 2 +- .../tests/test_reconciliation_match.py | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/account_reconcile_model_oca/models/account_reconcile_model.py b/account_reconcile_model_oca/models/account_reconcile_model.py index a3ec70e5..340ddd25 100644 --- a/account_reconcile_model_oca/models/account_reconcile_model.py +++ b/account_reconcile_model_oca/models/account_reconcile_model.py @@ -258,7 +258,7 @@ class AccountReconcileModel(models.Model): or ( self.match_partner and self.match_partner_category_ids - and partner.category_id not in self.match_partner_category_ids + and not (partner.category_id & self.match_partner_category_ids) ) ): return False diff --git a/account_reconcile_model_oca/tests/test_reconciliation_match.py b/account_reconcile_model_oca/tests/test_reconciliation_match.py index 9c99f101..6c9f6b27 100644 --- a/account_reconcile_model_oca/tests/test_reconciliation_match.py +++ b/account_reconcile_model_oca/tests/test_reconciliation_match.py @@ -778,7 +778,10 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon): test_category = self.env["res.partner.category"].create( {"name": "Consulting Services"} ) - self.partner_2.category_id = test_category + test_category2 = self.env["res.partner.category"].create( + {"name": "Consulting Services2"} + ) + self.partner_2.category_id = test_category + test_category2 self.rule_1.match_partner_category_ids |= test_category self._check_statement_matching( self.rule_1, From 9156ba4bac2a890f775417006b65bbe97de8691a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Mart=C3=ADnez?= Date: Mon, 31 Mar 2025 08:41:12 +0200 Subject: [PATCH 10/22] [FIX] account_reconcile_model_oca: reconciliation models Some behavior were incorrect with reconciliation rules: 1) Whit invoice_matching rule, no match_text_location and no match partner set. Nothing was matching, even by filling the right fields. 2) We were taking into account only the digits of the payment reference of the invoice, by removing all non digits characters. It has been decided to also taking non digit strings from 'payment_ref' and 'ref' into account, as long as they don't exceed one word. Steps for point 1: - Reco model with invoice_atching rule, payment_tolerance 0%, no match_text_location and no match_partner - An invoice for 100$ with name of the invoice as payment reference (eg 'INV/2023/00001') - A statement line of 100$ with either 'payment_ref, 'ref' or 'narration' set as 'INV/2023/00001', and no partner -> No match Steps for point 2: - Same reco model but with match_text_location_label set to True - An invoice for 100$ with eg 'abcdef' as payment reference - A statement line of 100$ with payment_reference (label) set as 'abcdef' and no partner -> No match Related to https://github.com/odoo/odoo/commit/cddea149547ded315a39b5df0ead53d8a3523995 --- .../models/account_bank_statement_line.py | 24 +- .../models/account_reconcile_model.py | 80 ++++-- .../tests/test_reconciliation_match.py | 261 ++++++++++-------- 3 files changed, 217 insertions(+), 148 deletions(-) diff --git a/account_reconcile_model_oca/models/account_bank_statement_line.py b/account_reconcile_model_oca/models/account_bank_statement_line.py index 652a3aa6..9dd16137 100644 --- a/account_reconcile_model_oca/models/account_bank_statement_line.py +++ b/account_reconcile_model_oca/models/account_bank_statement_line.py @@ -109,23 +109,15 @@ class AccountBankStatementLine(models.Model): """ self.ensure_one() - def _get_text_value(field_name): - if self._fields[field_name].type == "html": - return self[field_name] and html2plaintext(self[field_name]) - else: - return self[field_name] - st_line_text_values = [] - if allowed_fields is None or "payment_ref" in allowed_fields: - value = _get_text_value("payment_ref") - if value: - st_line_text_values.append(value) - if allowed_fields is None or "narration" in allowed_fields: - value = _get_text_value("narration") - if value: - st_line_text_values.append(value) - if allowed_fields is None or "ref" in allowed_fields: - value = _get_text_value("ref") + if not allowed_fields or "payment_ref" in allowed_fields: + if self.payment_ref: + st_line_text_values.append(self.payment_ref) + if not allowed_fields or "narration" in allowed_fields: + value = html2plaintext(self.narration or "") if value: st_line_text_values.append(value) + if not allowed_fields or "ref" in allowed_fields: + if self.ref: + st_line_text_values.append(self.ref) return st_line_text_values diff --git a/account_reconcile_model_oca/models/account_reconcile_model.py b/account_reconcile_model_oca/models/account_reconcile_model.py index 340ddd25..b70fe2c7 100644 --- a/account_reconcile_model_oca/models/account_reconcile_model.py +++ b/account_reconcile_model_oca/models/account_reconcile_model.py @@ -314,24 +314,42 @@ class AccountReconcileModel(models.Model): return aml_domain + def _get_st_line_text_values_for_matching(self, st_line): + """Collect the strings that could be used on the statement line to perform + some matching. + + :param st_line: The current statement line. + :return: A list of strings. + """ + self.ensure_one() + allowed_fields = [] + if self.match_text_location_label: + allowed_fields.append("payment_ref") + if self.match_text_location_note: + allowed_fields.append("narration") + if self.match_text_location_reference: + allowed_fields.append("ref") + return st_line._get_st_line_strings_for_matching(allowed_fields=allowed_fields) + def _get_invoice_matching_st_line_tokens(self, st_line): """Parse the textual information from the statement line passed as parameter in order to extract from it the meaningful information in order to perform the matching. :param st_line: A statement line. - :return: A list of tokens, each one being a string. + :return: A tuple of list of tokens, each one being a string. + The first element is a list of tokens you may match on + numerical information. + The second element is a list of tokens you may match exactly. """ - st_line_text_values = st_line._get_st_line_strings_for_matching( - allowed_fields=( - "payment_ref" if self.match_text_location_label else None, - "narration" if self.match_text_location_note else None, - "ref" if self.match_text_location_reference else None, - ) - ) + st_line_text_values = self._get_st_line_text_values_for_matching(st_line) significant_token_size = 4 - tokens = [] + numerical_tokens = [] + exact_tokens = [] for text_value in st_line_text_values: - for token in (text_value or "").split(): + tokens = (text_value or "").split() + + # Numerical tokens + for token in tokens: # The token is too short to be significant. if len(token) < significant_token_size: continue @@ -342,8 +360,12 @@ class AccountReconcileModel(models.Model): if len(formatted_token) < significant_token_size: continue - tokens.append(formatted_token) - return tokens + numerical_tokens.append(formatted_token) + + # Exact tokens. + if len(tokens) == 1: + exact_tokens.append(tokens[0]) + return numerical_tokens, exact_tokens def _get_invoice_matching_amls_candidates(self, st_line, partner): """Returns the match candidates for the 'invoice_matching' rule, with respect to @@ -364,9 +386,12 @@ class AccountReconcileModel(models.Model): query = self.env["account.move.line"]._where_calc(aml_domain) tables, where_clause, where_params = query.get_sql() - tokens = self._get_invoice_matching_st_line_tokens(st_line) - if tokens: - sub_queries = [] + sub_queries = [] + all_params = [] + numerical_tokens, exact_tokens = self._get_invoice_matching_st_line_tokens( + st_line + ) + if numerical_tokens: for table_alias, field in ( ("account_move_line", "name"), ("account_move_line__move_id", "name"), @@ -395,7 +420,30 @@ class AccountReconcileModel(models.Model): WHERE {where_clause} AND {table_alias}.{field} IS NOT NULL """ ) + all_params += where_params + if exact_tokens: + for table_alias, field in ( + ("account_move_line", "name"), + ("account_move_line__move_id", "name"), + ("account_move_line__move_id", "ref"), + ): + sub_queries.append( + rf""" + SELECT + account_move_line.id, + account_move_line.date, + account_move_line.date_maturity, + {table_alias}.{field} AS token + FROM {tables} + JOIN account_move account_move_line__move_id + ON account_move_line__move_id.id = account_move_line.move_id + WHERE {where_clause} AND {table_alias}.{field} IS NOT NULL + """ + ) + all_params += where_params + + if sub_queries: self._cr.execute( """ SELECT @@ -411,7 +459,7 @@ class AccountReconcileModel(models.Model): + order_by + """ """, - (where_params * 3) + [tuple(tokens)], + all_params + [tuple(numerical_tokens + exact_tokens)], ) candidate_ids = [r[0] for r in self._cr.fetchall()] if candidate_ids: diff --git a/account_reconcile_model_oca/tests/test_reconciliation_match.py b/account_reconcile_model_oca/tests/test_reconciliation_match.py index 6c9f6b27..bb46c290 100644 --- a/account_reconcile_model_oca/tests/test_reconciliation_match.py +++ b/account_reconcile_model_oca/tests/test_reconciliation_match.py @@ -1,3 +1,5 @@ +from contextlib import contextmanager + from freezegun import freeze_time from odoo import Command @@ -301,122 +303,6 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon): }, ) - @freeze_time("2020-01-01") - def test_matching_fields_match_text_location(self): - st_line = self._create_st_line( - payment_ref="1111", ref="2222 3333", narration="4444 5555 6666" - ) - - inv1 = self._create_invoice_line( - 1000, self.partner_a, "out_invoice", pay_reference="bernard 1111 gagnant" - ) - inv2 = self._create_invoice_line( - 1000, self.partner_a, "out_invoice", pay_reference="2222 turlututu 3333" - ) - inv3 = self._create_invoice_line( - 1000, - self.partner_a, - "out_invoice", - pay_reference="4444 tsoin 5555 tsoin 6666", - ) - - rule = self._create_reconcile_model( - allow_payment_tolerance=False, - match_text_location_label=True, - match_text_location_reference=False, - match_text_location_note=False, - ) - self.assertDictEqual( - rule._apply_rules(st_line, st_line._retrieve_partner()), - {"amls": inv1, "model": rule}, - ) - - rule.match_text_location_reference = True - self.assertDictEqual( - rule._apply_rules(st_line, st_line._retrieve_partner()), - {"amls": inv2, "model": rule}, - ) - - rule.match_text_location_note = True - self.assertDictEqual( - rule._apply_rules(st_line, st_line._retrieve_partner()), - {"amls": inv3, "model": rule}, - ) - - def test_matching_fields_match_text_location_no_partner(self): - self.bank_line_2.unlink() # One line is enough for this test - self.bank_line_1.partner_id = None - - self.partner_1.name = "Bernard Gagnant" - - self.rule_1.write( - { - "match_partner": False, - "match_partner_ids": [(5, 0, 0)], - "line_ids": [(5, 0, 0)], - } - ) - - st_line_initial_vals = { - "ref": None, - "payment_ref": "nothing", - "narration": None, - } - recmod_initial_vals = { - "match_text_location_label": False, - "match_text_location_note": False, - "match_text_location_reference": False, - } - - rec_mod_options_to_fields = { - "match_text_location_label": "payment_ref", - "match_text_location_note": "narration", - "match_text_location_reference": "ref", - } - - for rec_mod_field, st_line_field in rec_mod_options_to_fields.items(): - self.rule_1.write({**recmod_initial_vals, rec_mod_field: True}) - # Fully reinitialize the statement line - self.bank_line_1.write(st_line_initial_vals) - - # Nothing should match - self._check_statement_matching( - self.rule_1, - { - self.bank_line_1: {}, - }, - ) - - # Test matching with the invoice ref - self.bank_line_1.write( - {st_line_field: self.invoice_line_1.move_id.payment_reference} - ) - - self._check_statement_matching( - self.rule_1, - { - self.bank_line_1: { - "amls": self.invoice_line_1, - "model": self.rule_1, - }, - }, - ) - - # Test matching with the partner name (resetting the statement line first) - self.bank_line_1.write( - {**st_line_initial_vals, st_line_field: self.partner_1.name} - ) - - self._check_statement_matching( - self.rule_1, - { - self.bank_line_1: { - "amls": self.invoice_line_1, - "model": self.rule_1, - }, - }, - ) - def test_matching_fields_match_journal_ids(self): self.rule_1.match_journal_ids |= self.cash_line_1.journal_id self._check_statement_matching( @@ -1493,3 +1379,146 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon): }, }, ) + + @freeze_time("2019-01-01") + def test_invoice_matching_using_match_text_location(self): + @contextmanager + def rollback(): + savepoint = self.cr.savepoint() + yield + savepoint.rollback() + + rule = self._create_reconcile_model( + match_partner=False, + allow_payment_tolerance=False, + match_text_location_label=False, + match_text_location_reference=False, + match_text_location_note=False, + ) + st_line = self._create_st_line(amount=1000, partner_id=False) + invoice = self.env["account.move"].create( + { + "move_type": "out_invoice", + "partner_id": self.partner_a.id, + "invoice_date": "2019-01-01", + "invoice_line_ids": [ + Command.create( + { + "product_id": self.product_a.id, + "price_unit": 100, + } + ) + ], + } + ) + invoice.action_post() + term_line = invoice.line_ids.filtered( + lambda x: x.display_type == "payment_term" + ) + + # No match at all. + self.assertDictEqual( + rule._apply_rules(st_line, None), + {}, + ) + + with rollback(): + term_line.name = "1234" + st_line.payment_ref = "1234" + + # Matching if no checkbox checked. + self.assertDictEqual( + rule._apply_rules(st_line, None), + {"amls": term_line, "model": rule}, + ) + + # No matching if other checkbox is checked. + rule.match_text_location_note = True + self.assertDictEqual( + rule._apply_rules(st_line, None), + {}, + ) + + for rule_field, st_line_field in ( + ("match_text_location_label", "payment_ref"), + ("match_text_location_reference", "ref"), + ("match_text_location_note", "narration"), + ): + with self.subTest(rule_field=rule_field, st_line_field=st_line_field): + with rollback(): + rule[rule_field] = True + st_line[st_line_field] = "123456" + term_line.name = "123456" + + # Matching if the corresponding flag is enabled. + self.assertDictEqual( + rule._apply_rules(st_line, None), + {"amls": term_line, "model": rule}, + ) + + # It works also if the statement line contains the word. + st_line[st_line_field] = "payment for 123456 urgent!" + self.assertDictEqual( + rule._apply_rules(st_line, None), + {"amls": term_line, "model": rule}, + ) + + # Not if the invoice has nothing in common even if numerical. + term_line.name = "78910" + self.assertDictEqual( + rule._apply_rules(st_line, None), + {}, + ) + + # Exact matching on a single word. + st_line[st_line_field] = "TURLUTUTU21" + term_line.name = "TURLUTUTU21" + self.assertDictEqual( + rule._apply_rules(st_line, None), + {"amls": term_line, "model": rule}, + ) + + # No matching if not enough numerical values. + st_line[st_line_field] = "12" + term_line.name = "selling 3 apples, 2 tomatoes and 12kg of potatoes" + self.assertDictEqual( + rule._apply_rules(st_line, None), + {}, + ) + + invoice2 = self.env["account.move"].create( + { + "move_type": "out_invoice", + "partner_id": self.partner_a.id, + "invoice_date": "2019-01-01", + "invoice_line_ids": [ + Command.create( + { + "product_id": self.product_a.id, + "price_unit": 100, + } + ) + ], + } + ) + invoice2.action_post() + term_lines = (invoice + invoice2).line_ids.filtered( + lambda x: x.display_type == "payment_term" + ) + + # Matching multiple invoices. + rule.match_text_location_label = True + st_line.payment_ref = "paying invoices 1234 & 5678" + term_lines[0].name = "INV/1234" + term_lines[1].name = "INV/5678" + self.assertDictEqual( + rule._apply_rules(st_line, None), + {"amls": term_lines, "model": rule}, + ) + + # Matching multiple invoices sharing the same reference. + term_lines[1].name = "INV/1234" + self.assertDictEqual( + rule._apply_rules(st_line, None), + {"amls": term_lines, "model": rule}, + ) From 35908dcdbf9975ab138e85ee0cdfb5d4ceac833a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Mart=C3=ADnez?= Date: Mon, 31 Mar 2025 08:45:06 +0200 Subject: [PATCH 11/22] [FIX] account_reconcile_model_oca: Add textual tokens to improve matching rules Making `SUBSTRING(REGEXP_REPLACE(LOWER(...), '[^0-9a-z\s]', '', 'g'), '\S(?:.*\S)*')` is costly in sql. To avoid that, the tokens are computed python-side to avoid this extra processing. Also, we want to avoid extra conditions on the tokens before executing the queries (and avoid it if necessary). For example, we want to execute the query matching the sale orders only if at least one token is prefixed correctly by 'SO'. Related to https://github.com/odoo/odoo/commit/76da4b055fc0ec2501a8ea1afbea5598bb6a7309 --- .../models/account_reconcile_model.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/account_reconcile_model_oca/models/account_reconcile_model.py b/account_reconcile_model_oca/models/account_reconcile_model.py index b70fe2c7..766e5923 100644 --- a/account_reconcile_model_oca/models/account_reconcile_model.py +++ b/account_reconcile_model_oca/models/account_reconcile_model.py @@ -345,8 +345,12 @@ class AccountReconcileModel(models.Model): significant_token_size = 4 numerical_tokens = [] exact_tokens = [] + text_tokens = [] for text_value in st_line_text_values: - tokens = (text_value or "").split() + tokens = [ + "".join(x for x in token if re.match(r"[0-9a-zA-Z\s]", x)) + for token in (text_value or "").split() + ] # Numerical tokens for token in tokens: @@ -354,6 +358,7 @@ class AccountReconcileModel(models.Model): if len(token) < significant_token_size: continue + text_tokens.append(token) formatted_token = "".join(x for x in token if x.isdecimal()) # The token is too short after formatting to be significant. @@ -365,7 +370,7 @@ class AccountReconcileModel(models.Model): # Exact tokens. if len(tokens) == 1: exact_tokens.append(tokens[0]) - return numerical_tokens, exact_tokens + return numerical_tokens, exact_tokens, text_tokens def _get_invoice_matching_amls_candidates(self, st_line, partner): """Returns the match candidates for the 'invoice_matching' rule, with respect to @@ -388,9 +393,11 @@ class AccountReconcileModel(models.Model): sub_queries = [] all_params = [] - numerical_tokens, exact_tokens = self._get_invoice_matching_st_line_tokens( - st_line - ) + ( + numerical_tokens, + exact_tokens, + _text_tokens, + ) = self._get_invoice_matching_st_line_tokens(st_line) if numerical_tokens: for table_alias, field in ( ("account_move_line", "name"), From 7e5d7e7b33df00b6febf68333d4c27a81c789003 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Mart=C3=ADnez?= Date: Mon, 31 Mar 2025 08:49:42 +0200 Subject: [PATCH 12/22] [FIX] account_reconcile_model_oca: bank rec match with empty name It can be that the name field on the account_move_line is an empty string (as opposed to NULL). This can happen, for instance, when generating the payment term line for Switzerland without the appropriate fields on the company. Since there are instances when the payment term's name is an empty string, we can retrieve these lines and automatically match them with payments from the bank reconciliation model. This can happen automatically, with exact matches between a payment with a near-empty reference (when the payment ref is just "/" or "?", this is sanitized and ends up as an empty string for the purposes of comparison), and these empty string payment term invoice lines. The query that fetches the candidate lines for exact matches already excludes those lines with a NULL move_line name, move ref, move name. The natural extension of this behav Related to https://github.com/odoo/odoo/commit/ee22c02b1f0348e9dcedab1280de9ec8db8fae0c --- .../models/account_reconcile_model.py | 2 +- .../tests/test_reconciliation_match.py | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/account_reconcile_model_oca/models/account_reconcile_model.py b/account_reconcile_model_oca/models/account_reconcile_model.py index 766e5923..53eb028e 100644 --- a/account_reconcile_model_oca/models/account_reconcile_model.py +++ b/account_reconcile_model_oca/models/account_reconcile_model.py @@ -445,7 +445,7 @@ class AccountReconcileModel(models.Model): FROM {tables} JOIN account_move account_move_line__move_id ON account_move_line__move_id.id = account_move_line.move_id - WHERE {where_clause} AND {table_alias}.{field} IS NOT NULL + WHERE {where_clause} AND COALESCE({table_alias}.{field}, '') != '' """ ) all_params += where_params diff --git a/account_reconcile_model_oca/tests/test_reconciliation_match.py b/account_reconcile_model_oca/tests/test_reconciliation_match.py index bb46c290..8ee89599 100644 --- a/account_reconcile_model_oca/tests/test_reconciliation_match.py +++ b/account_reconcile_model_oca/tests/test_reconciliation_match.py @@ -1439,6 +1439,19 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon): {}, ) + with self.subTest( + rule_field="match_text_location_label", st_line_field="payment_ref" + ): + with rollback(): + term_line.name = "" + st_line.payment_ref = "/?" + + # No exact matching when the term line name is an empty string + self.assertDictEqual( + rule._apply_rules(st_line, None), + {}, + ) + for rule_field, st_line_field in ( ("match_text_location_label", "payment_ref"), ("match_text_location_reference", "ref"), From a25137c3d4d4d1e9d9ad13335d31a9390cd697fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Mart=C3=ADnez?= Date: Mon, 31 Mar 2025 08:52:55 +0200 Subject: [PATCH 13/22] [FIX] account_reconcile_model_oca: Fix reco model matching on exact_token When the payment label is a single word, we try to match the full value and not only the alphanumeric values. However, the matching was made on the token that was already sanitized using the [0-9a-zA-Z\s] regex. Instead, let's match the full value. Related to https://github.com/odoo/odoo/commit/bccc0ae0e896d58b54debae9483b8744d2b59ddc --- .../models/account_reconcile_model.py | 2 +- .../tests/test_reconciliation_match.py | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/account_reconcile_model_oca/models/account_reconcile_model.py b/account_reconcile_model_oca/models/account_reconcile_model.py index 53eb028e..4963abe1 100644 --- a/account_reconcile_model_oca/models/account_reconcile_model.py +++ b/account_reconcile_model_oca/models/account_reconcile_model.py @@ -369,7 +369,7 @@ class AccountReconcileModel(models.Model): # Exact tokens. if len(tokens) == 1: - exact_tokens.append(tokens[0]) + exact_tokens.append(text_value) return numerical_tokens, exact_tokens, text_tokens def _get_invoice_matching_amls_candidates(self, st_line, partner): diff --git a/account_reconcile_model_oca/tests/test_reconciliation_match.py b/account_reconcile_model_oca/tests/test_reconciliation_match.py index 8ee89599..a51cc29f 100644 --- a/account_reconcile_model_oca/tests/test_reconciliation_match.py +++ b/account_reconcile_model_oca/tests/test_reconciliation_match.py @@ -1439,6 +1439,17 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon): {}, ) + with rollback(): + # Test Matching on exact_token. + term_line.name = "PAY-123" + st_line.payment_ref = "PAY-123" + + # Matching if no checkbox checked. + self.assertDictEqual( + rule._apply_rules(st_line, None), + {"amls": term_line, "model": rule}, + ) + with self.subTest( rule_field="match_text_location_label", st_line_field="payment_ref" ): From 3a71f30cf6fa6d035a6b4da80bd64ef36a44fb10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Mart=C3=ADnez?= Date: Mon, 31 Mar 2025 08:57:16 +0200 Subject: [PATCH 14/22] [FIX] account_reconcile_model_oca: rounding issue reconciliation Fix a decimal issue on reconciliation. Steps: - Set a reconciliation model with payment tolerance of 2% - Create an invoice for $1210 - Create a bank stmt with a line for $1185.80 -> Reconciliation model is not apply This is because of a decimal issue when calculating the residual balance after reconciliation, leading to the difference being 2.000000000000004% instead of 2%. Related to https://github.com/odoo/odoo/commit/d33e98b1417fc6c1dc08aabe7f6e973cdca836af --- .../models/account_reconcile_model.py | 14 +++++++++++--- .../tests/test_reconciliation_match.py | 8 ++++---- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/account_reconcile_model_oca/models/account_reconcile_model.py b/account_reconcile_model_oca/models/account_reconcile_model.py index 4963abe1..9671e766 100644 --- a/account_reconcile_model_oca/models/account_reconcile_model.py +++ b/account_reconcile_model_oca/models/account_reconcile_model.py @@ -698,7 +698,9 @@ class AccountReconcileModel(models.Model): for aml_values in amls_values_list ) sign = 1 if st_line_amount_curr > 0.0 else -1 - amount_curr_after_rec = sign * (amls_amount_curr + st_line_amount_curr) + amount_curr_after_rec = st_line_currency.round( + sign * (amls_amount_curr + st_line_amount_curr) + ) # The statement line will be fully reconciled. if st_line_currency.is_zero(amount_curr_after_rec): @@ -717,7 +719,10 @@ class AccountReconcileModel(models.Model): # amount doesn't exceed the tolerance. if ( self.payment_tolerance_type == "fixed_amount" - and -amount_curr_after_rec <= self.payment_tolerance_param + and st_line_currency.compare_amounts( + -amount_curr_after_rec, self.payment_tolerance_param + ) + <= 0 ): return {"allow_write_off", "allow_auto_reconcile"} @@ -727,7 +732,10 @@ class AccountReconcileModel(models.Model): ) * 100.0 if ( self.payment_tolerance_type == "percentage" - and reconciled_percentage_left <= self.payment_tolerance_param + and st_line_currency.compare_amounts( + reconciled_percentage_left, self.payment_tolerance_param + ) + <= 0 ): return {"allow_write_off", "allow_auto_reconcile"} diff --git a/account_reconcile_model_oca/tests/test_reconciliation_match.py b/account_reconcile_model_oca/tests/test_reconciliation_match.py index a51cc29f..c051dd5b 100644 --- a/account_reconcile_model_oca/tests/test_reconciliation_match.py +++ b/account_reconcile_model_oca/tests/test_reconciliation_match.py @@ -513,17 +513,17 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon): @freeze_time("2019-01-01") def test_enough_payment_tolerance(self): rule = self._create_reconcile_model( - payment_tolerance_param=1.0, + payment_tolerance_param=2.0, line_ids=[{}], ) for inv_type, bsl_sign in (("out_invoice", 1), ("in_invoice", -1)): invl = self._create_invoice_line( - 1000.0, self.partner_a, inv_type, inv_date="2019-01-01" + 1210.0, self.partner_a, inv_type, inv_date="2019-01-01" ) # Enough tolerance to match the invoice line. - st_line = self._create_st_line(amount=bsl_sign * 990.0) + st_line = self._create_st_line(amount=bsl_sign * 1185.80) self._check_statement_matching( rule, {st_line: {"amls": invl, "model": rule, "status": "write_off"}}, @@ -532,7 +532,7 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon): # The payment amount is higher than the invoice one. # However, since the invoice amount is lower than the payment amount, # the tolerance is not checked and the invoice line is matched. - st_line = self._create_st_line(amount=bsl_sign * 1010.0) + st_line = self._create_st_line(amount=bsl_sign * 1234.20) self._check_statement_matching( rule, {st_line: {"amls": invl, "model": rule}}, From cccc82f3df11be2ac02ff7b2e2e7f52fd7f0f7f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Mart=C3=ADnez?= Date: Mon, 31 Mar 2025 08:58:12 +0200 Subject: [PATCH 15/22] [FIX] account_reconcile_model_oca: Fix unassigned variable 'balance' It seems some people are getting an "unassigned variable 'balance'". This shouldn't happen with the standard code because _get_write_off_move_lines_dict is only called from the manual reconciliation widget in which, you only have access to models having strictly lines being percentage or fixed (see get_reconcile_modelds_for_manual_reconciliation). This method is not called from the bank reco widget pointed by "RECONCILE x ITEMS" on the accounting dashboard. It probably comes from some customization but I don't know which ones. Related to https://github.com/odoo/odoo/commit/99525f8dac17170a59ed10e471a8ea364258884c --- account_reconcile_model_oca/models/account_reconcile_model.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/account_reconcile_model_oca/models/account_reconcile_model.py b/account_reconcile_model_oca/models/account_reconcile_model.py index 9671e766..c4620013 100644 --- a/account_reconcile_model_oca/models/account_reconcile_model.py +++ b/account_reconcile_model_oca/models/account_reconcile_model.py @@ -131,6 +131,8 @@ class AccountReconcileModel(models.Model): balance = currency.round( line.amount * (1 if residual_balance > 0.0 else -1) ) + else: + balance = 0.0 if currency.is_zero(balance): continue From 7e45e2c123279f1e8ec3d7065005deea7afa1e58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Mart=C3=ADnez?= Date: Mon, 31 Mar 2025 09:23:10 +0200 Subject: [PATCH 16/22] [FIX] account_reconcile_model_oca: Fix matching rules with no partner The orm doesn't match monetary amounts using the related currency. It means, -208.73 != 208.730000000000002. Let's match the amount using an sql query instead. Related to https://github.com/odoo/odoo/commit/ae848e9981a7f359995f48dbd909c5764abd6a9c --- .../models/account_reconcile_model.py | 63 ++++++++--- .../tests/test_reconciliation_match.py | 101 ++++++++++++------ 2 files changed, 114 insertions(+), 50 deletions(-) diff --git a/account_reconcile_model_oca/models/account_reconcile_model.py b/account_reconcile_model_oca/models/account_reconcile_model.py index c4620013..1e0a20e7 100644 --- a/account_reconcile_model_oca/models/account_reconcile_model.py +++ b/account_reconcile_model_oca/models/account_reconcile_model.py @@ -380,15 +380,16 @@ class AccountReconcileModel(models.Model): :param st_line: A statement line. :param partner: The partner associated to the statement line. """ + + def get_order_by_clause(alias=None): + direction = "DESC" if self.matching_order == "new_first" else "ASC" + dotted_alias = f"{alias}." if alias else "" + return f"{dotted_alias}date_maturity {direction}, {dotted_alias}date {direction}, {dotted_alias}id {direction}" # noqa: E501 + assert self.rule_type == "invoice_matching" self.env["account.move"].flush_model() self.env["account.move.line"].flush_model() - if self.matching_order == "new_first": - order_by = "sub.date_maturity DESC, sub.date DESC, sub.id DESC" - else: - order_by = "sub.date_maturity ASC, sub.date ASC, sub.id ASC" - aml_domain = self._get_invoice_matching_amls_domain(st_line, partner) query = self.env["account.move.line"]._where_calc(aml_domain) tables, where_clause, where_params = query.get_sql() @@ -453,6 +454,7 @@ class AccountReconcileModel(models.Model): all_params += where_params if sub_queries: + order_by = get_order_by_clause(alias="sub") self._cr.execute( """ SELECT @@ -477,19 +479,48 @@ class AccountReconcileModel(models.Model): "amls": self.env["account.move.line"].browse(candidate_ids), } - # Search without any matching based on textual information. - if partner: - if self.matching_order == "new_first": - order = "date_maturity DESC, date DESC, id DESC" + if not partner: + st_line_currency = ( + st_line.foreign_currency_id + or st_line.journal_id.currency_id + or st_line.company_currency_id + ) + if st_line_currency == self.company_id.currency_id: + aml_amount_field = "amount_residual" else: - order = "date_maturity ASC, date ASC, id ASC" + aml_amount_field = "amount_residual_currency" - amls = self.env["account.move.line"].search(aml_domain, order=order) - if amls: - return { - "allow_auto_reconcile": False, - "amls": amls, - } + order_by = get_order_by_clause(alias="account_move_line") + self._cr.execute( + f""" + SELECT account_move_line.id + FROM {tables} + WHERE + {where_clause} + AND account_move_line.currency_id = %s + AND ROUND(account_move_line.{aml_amount_field}, %s) = ROUND(%s, %s) + ORDER BY {order_by} + """, # noqa: E501 + where_params + + [ + st_line_currency.id, + st_line_currency.decimal_places, + -st_line.amount_residual, + st_line_currency.decimal_places, + ], + ) + amls = self.env["account.move.line"].browse( + [row[0] for row in self._cr.fetchall()] + ) + else: + amls = self.env["account.move.line"].search( + aml_domain, order=get_order_by_clause() + ) + if amls: + return { + "allow_auto_reconcile": False, + "amls": amls, + } def _get_invoice_matching_rules_map(self): """Get a mapping that could be overridden in others diff --git a/account_reconcile_model_oca/tests/test_reconciliation_match.py b/account_reconcile_model_oca/tests/test_reconciliation_match.py index c051dd5b..de9fed3e 100644 --- a/account_reconcile_model_oca/tests/test_reconciliation_match.py +++ b/account_reconcile_model_oca/tests/test_reconciliation_match.py @@ -1101,40 +1101,6 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon): # Matching is back thanks to "coincoin". self.assertEqual(st_line._retrieve_partner(), self.partner_1) - def test_partner_name_in_communication(self): - self.invoice_line_1.partner_id.write({"name": "Archibald Haddock"}) - self.bank_line_1.write( - {"partner_id": None, "payment_ref": "1234//HADDOCK-Archibald"} - ) - self.bank_line_2.write({"partner_id": None}) - self.rule_1.write({"match_partner": False}) - - # bank_line_1 should match, as its communic. contains the invoice's partner name - self._check_statement_matching( - self.rule_1, - { - self.bank_line_1: {"amls": self.invoice_line_1, "model": self.rule_1}, - self.bank_line_2: {}, - }, - ) - - def test_partner_name_with_regexp_chars(self): - self.invoice_line_1.partner_id.write({"name": "Archibald + Haddock"}) - self.bank_line_1.write( - {"partner_id": None, "payment_ref": "1234//HADDOCK+Archibald"} - ) - self.bank_line_2.write({"partner_id": None}) - self.rule_1.write({"match_partner": False}) - - # The query should still work - self._check_statement_matching( - self.rule_1, - { - self.bank_line_1: {"amls": self.invoice_line_1, "model": self.rule_1}, - self.bank_line_2: {}, - }, - ) - def test_match_multi_currencies(self): """Ensure the matching of candidates is made using the right statement line currency. In this test, the value of the statement line is 100 USD = 300 @@ -1546,3 +1512,70 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon): rule._apply_rules(st_line, None), {"amls": term_lines, "model": rule}, ) + + @freeze_time("2019-01-01") + def test_matching_exact_amount_no_partner(self): + """In case the reconciliation model can't match via text or partner matching + we do a last check to find amls with the exact amount. + """ + self.rule_1.write( + { + "match_text_location_label": False, + "match_partner": False, + "match_partner_ids": [Command.clear()], + } + ) + self.bank_line_1.partner_id = None + self.bank_line_1.payment_ref = False + + with self.subTest(test="single_currency"): + st_line = self._create_st_line( + amount=100, payment_ref=None, partner_id=None + ) + invl = self._create_invoice_line(100, self.partner_1, "out_invoice") + self._check_statement_matching( + self.rule_1, + { + st_line: { + "amls": invl, + "model": self.rule_1, + }, + }, + ) + + with self.subTest(test="rounding"): + st_line = self._create_st_line( + amount=-208.73, payment_ref=None, partner_id=None + ) + invl = self._create_invoice_line(208.73, self.partner_1, "in_invoice") + self._check_statement_matching( + self.rule_1, + { + st_line: { + "amls": invl, + "model": self.rule_1, + }, + }, + ) + + with self.subTest(test="multi_currencies"): + foreign_curr = self.currency_data_2["currency"] + invl = self._create_invoice_line( + 300, self.partner_1, "out_invoice", currency=foreign_curr + ) + st_line = self._create_st_line( + amount=15.0, + foreign_currency_id=foreign_curr.id, + amount_currency=300.0, + payment_ref=None, + partner_id=None, + ) + self._check_statement_matching( + self.rule_1, + { + st_line: { + "amls": invl, + "model": self.rule_1, + }, + }, + ) From 2dc838a43758285fce6a644e75f3fc17ef63dd0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Mart=C3=ADnez?= Date: Mon, 31 Mar 2025 09:34:13 +0200 Subject: [PATCH 17/22] [FIX] account_reconcile_model_oca: only pre-mount true matches with reconcile models Before, when using reconcile models that wanted to match invoice/bills to statement based on the Label and/or Note and/or Reference, if there is no real match with the statement label, the bank reco model would mount/suggest another entry from the same partner. This is misleading as we expect to be suggested with lines that actually match with the statement, as stated in the reconciliation model. Now, if any of the Invoices/bills matching rule is toggled, it will suggest *only* matching amls. If no rule is toggled, it will behave like before (mounting a match if any, or an aml from the same partner). Related to https://github.com/odoo/odoo/commit/8948b314073f258121a0f34c1848181181fd52b5 --- .../models/account_reconcile_model.py | 9 ++++ .../tests/test_reconciliation_match.py | 51 +++++++++++++++---- 2 files changed, 50 insertions(+), 10 deletions(-) diff --git a/account_reconcile_model_oca/models/account_reconcile_model.py b/account_reconcile_model_oca/models/account_reconcile_model.py index 1e0a20e7..baa4bf24 100644 --- a/account_reconcile_model_oca/models/account_reconcile_model.py +++ b/account_reconcile_model_oca/models/account_reconcile_model.py @@ -478,6 +478,15 @@ class AccountReconcileModel(models.Model): "allow_auto_reconcile": True, "amls": self.env["account.move.line"].browse(candidate_ids), } + elif ( + self.match_text_location_label + or self.match_text_location_note + or self.match_text_location_reference + ): + # In the case any of the Label, Note or Reference matching rule has been + # toggled, and the query didn't return + # any candidates, the model should not try to mount another aml instead. + return if not partner: st_line_currency = ( diff --git a/account_reconcile_model_oca/tests/test_reconciliation_match.py b/account_reconcile_model_oca/tests/test_reconciliation_match.py index de9fed3e..163973a6 100644 --- a/account_reconcile_model_oca/tests/test_reconciliation_match.py +++ b/account_reconcile_model_oca/tests/test_reconciliation_match.py @@ -289,6 +289,7 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon): def test_matching_fields(self): # Check without restriction. + self.rule_1.match_text_location_label = False self._check_statement_matching( self.rule_1, { @@ -304,6 +305,7 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon): ) def test_matching_fields_match_journal_ids(self): + self.rule_1.match_text_location_label = False self.rule_1.match_journal_ids |= self.cash_line_1.journal_id self._check_statement_matching( self.rule_1, @@ -315,6 +317,7 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon): ) def test_matching_fields_match_nature(self): + self.rule_1.match_text_location_label = False self.rule_1.match_nature = "amount_received" self._check_statement_matching( self.rule_1, @@ -340,6 +343,7 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon): ) def test_matching_fields_match_amount(self): + self.rule_1.match_text_location_label = False self.rule_1.match_amount = "lower" self.rule_1.match_amount_max = 150 self._check_statement_matching( @@ -383,6 +387,7 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon): ) def test_matching_fields_match_label(self): + self.rule_1.match_text_location_label = False self.rule_1.match_label = "contains" self.rule_1.match_label_param = "yyyyy" self._check_statement_matching( @@ -429,21 +434,27 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon): ) # Exact matching. - st_line = self._create_st_line(amount=bsl_sign * 1000.0) + st_line = self._create_st_line( + amount=bsl_sign * 1000.0, payment_ref=invl.name + ) self._check_statement_matching( rule, {st_line: {"amls": invl, "model": rule}}, ) # No matching because there is no tolerance. - st_line = self._create_st_line(amount=bsl_sign * 990.0) + st_line = self._create_st_line( + amount=bsl_sign * 990.0, payment_ref=invl.name + ) self._check_statement_matching( rule, {st_line: {}}, ) # The payment amount is higher than the invoice one. - st_line = self._create_st_line(amount=bsl_sign * 1010.0) + st_line = self._create_st_line( + amount=bsl_sign * 1010.0, payment_ref=invl.name + ) self._check_statement_matching( rule, {st_line: {"amls": invl, "model": rule}}, @@ -466,7 +477,9 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon): ) # No matching because there is no tolerance. - st_line = self._create_st_line(amount=bsl_sign * 990.0) + st_line = self._create_st_line( + amount=bsl_sign * 990.0, payment_ref="123456" + ) self._check_statement_matching( rule, {st_line: {}}, @@ -495,7 +508,9 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon): ) # No matching because there is no enough tolerance. - st_line = self._create_st_line(amount=bsl_sign * 990.0) + st_line = self._create_st_line( + amount=bsl_sign * 990.0, payment_ref=invl.name + ) self._check_statement_matching( rule, {st_line: {}}, @@ -504,7 +519,9 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon): # The payment amount is higher than the invoice one. # However, since the invoice amount is lower than the payment amount, # the tolerance is not checked and the invoice line is matched. - st_line = self._create_st_line(amount=bsl_sign * 1010.0) + st_line = self._create_st_line( + amount=bsl_sign * 1010.0, payment_ref=invl.name + ) self._check_statement_matching( rule, {st_line: {"amls": invl, "model": rule}}, @@ -523,7 +540,9 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon): ) # Enough tolerance to match the invoice line. - st_line = self._create_st_line(amount=bsl_sign * 1185.80) + st_line = self._create_st_line( + amount=bsl_sign * 1185.80, payment_ref=invl.name + ) self._check_statement_matching( rule, {st_line: {"amls": invl, "model": rule, "status": "write_off"}}, @@ -532,7 +551,9 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon): # The payment amount is higher than the invoice one. # However, since the invoice amount is lower than the payment amount, # the tolerance is not checked and the invoice line is matched. - st_line = self._create_st_line(amount=bsl_sign * 1234.20) + st_line = self._create_st_line( + amount=bsl_sign * 1234.20, payment_ref=invl.name + ) self._check_statement_matching( rule, {st_line: {"amls": invl, "model": rule}}, @@ -581,7 +602,9 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon): invl = self._create_invoice_line( 990.0, self.partner_a, inv_type, inv_date="2019-01-01" ) - st_line = self._create_st_line(amount=bsl_sign * 1000) + st_line = self._create_st_line( + amount=bsl_sign * 1000, payment_ref=invl.name + ) # Partial reconciliation. self._check_statement_matching( @@ -661,6 +684,7 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon): ) def test_matching_fields_match_partner_category_ids(self): + self.rule_1.match_text_location_label = False test_category = self.env["res.partner.category"].create( {"name": "Consulting Services"} ) @@ -681,6 +705,7 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon): def test_mixin_rules(self): """Test usage of rules together.""" + self.rule_1.match_text_location_label = False # rule_1 is used before rule_2. self.rule_1.sequence = 1 self.rule_2.sequence = 2 @@ -754,6 +779,7 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon): self.rule_1.sequence = 2 self.rule_1.auto_reconcile = True self.rule_1.payment_tolerance_param = 10.0 + self.rule_1.match_text_location_label = False self.rule_2.sequence = 1 self.rule_2.match_partner_ids |= self.partner_2 self.rule_2.auto_reconcile = True @@ -790,6 +816,7 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon): self.rule_1.allow_payment_tolerance = False self.rule_1.auto_reconcile = True self.rule_1.line_ids = [(5, 0, 0)] + self.rule_1.match_text_location_label = False self._check_statement_matching( self.rule_1, @@ -958,6 +985,7 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon): move_reversed = move._reverse_moves() self.assertTrue(move_reversed.exists()) + self.rule_1.match_text_location_label = False self.bank_line_1.write( { "payment_ref": "8", @@ -999,7 +1027,7 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon): "partner_id": partner.id, "foreign_currency_id": currency_statement.id, "amount_currency": 100, - "payment_ref": "test", + "payment_ref": invoice_line.name, } ) self._check_statement_matching( @@ -1133,6 +1161,7 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon): "match_same_currency": False, "company_id": self.company_data["company"].id, "past_months_limit": False, + "match_text_location_label": False, } ) @@ -1309,6 +1338,7 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon): 200 should be proposed. """ self.rule_1.allow_payment_tolerance = False + self.rule_1.match_text_location_label = False self.bank_line_2.amount = 250 self.bank_line_1.partner_id = None @@ -1331,6 +1361,7 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon): other ones are disregarded. """ self.rule_1.allow_payment_tolerance = False + self.rule_1.match_text_location_label = False self.bank_line_2.amount = 300 self.bank_line_1.partner_id = None From e8a7c8d6dd5a53d6e02e6b3f124d690aed1f81aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Mart=C3=ADnez?= Date: Mon, 31 Mar 2025 09:42:16 +0200 Subject: [PATCH 18/22] [PERF] account_reconcile_model_oca: add CTE to invoice amls candidates query Backport of odoo/enterprise#64754. This commit adds a CTE at the beginning of the invoice matching amls candidate to speedup the whole query. More info and speedup in PR above Related to https://github.com/odoo/odoo/commit/f396bc2f1f423e0c13a553a252953b99913f1b78 --- .../models/account_reconcile_model.py | 51 ++++++++++++------- 1 file changed, 32 insertions(+), 19 deletions(-) diff --git a/account_reconcile_model_oca/models/account_reconcile_model.py b/account_reconcile_model_oca/models/account_reconcile_model.py index baa4bf24..53d4b178 100644 --- a/account_reconcile_model_oca/models/account_reconcile_model.py +++ b/account_reconcile_model_oca/models/account_reconcile_model.py @@ -396,11 +396,29 @@ class AccountReconcileModel(models.Model): sub_queries = [] all_params = [] + aml_cte = "" ( numerical_tokens, exact_tokens, _text_tokens, ) = self._get_invoice_matching_st_line_tokens(st_line) + if numerical_tokens or exact_tokens: + aml_cte = rf""" + WITH aml_cte AS ( + SELECT + account_move_line.id as account_move_line_id, + account_move_line.date as account_move_line_date, + account_move_line.date_maturity as account_move_line_date_maturity, + account_move_line.name as account_move_line_name, + account_move_line__move_id.name as account_move_line__move_id_name, + account_move_line__move_id.ref as account_move_line__move_id_ref + FROM {tables} + JOIN account_move account_move_line__move_id + ON account_move_line__move_id.id = account_move_line.move_id + WHERE {where_clause} + ) + """ # noqa: E501 + all_params += where_params if numerical_tokens: for table_alias, field in ( ("account_move_line", "name"), @@ -410,27 +428,24 @@ class AccountReconcileModel(models.Model): sub_queries.append( rf""" SELECT - account_move_line.id, - account_move_line.date, - account_move_line.date_maturity, + account_move_line_id as id, + account_move_line_date as date, + account_move_line_date_maturity as date_maturity, UNNEST( REGEXP_SPLIT_TO_ARRAY( SUBSTRING( REGEXP_REPLACE( - {table_alias}.{field}, '[^0-9\s]', '', 'g' + {table_alias}_{field}, '[^0-9\s]', '', 'g' ), '\S(?:.*\S)*' ), '\s+' ) ) AS token - FROM {tables} - JOIN account_move account_move_line__move_id - ON account_move_line__move_id.id = account_move_line.move_id - WHERE {where_clause} AND {table_alias}.{field} IS NOT NULL + FROM aml_cte + WHERE {table_alias}_{field} IS NOT NULL """ ) - all_params += where_params if exact_tokens: for table_alias, field in ( @@ -441,22 +456,20 @@ class AccountReconcileModel(models.Model): sub_queries.append( rf""" SELECT - account_move_line.id, - account_move_line.date, - account_move_line.date_maturity, - {table_alias}.{field} AS token - FROM {tables} - JOIN account_move account_move_line__move_id - ON account_move_line__move_id.id = account_move_line.move_id - WHERE {where_clause} AND COALESCE({table_alias}.{field}, '') != '' + account_move_line_id as id, + account_move_line_date as date, + account_move_line_date_maturity as date_maturity, + {table_alias}_{field} AS token + FROM aml_cte + WHERE COALESCE({table_alias}_{field}, '') != '' """ ) - all_params += where_params if sub_queries: order_by = get_order_by_clause(alias="sub") self._cr.execute( - """ + aml_cte + + """ SELECT sub.id, COUNT(*) AS nb_match From efaf301163cf9bd7751422a206fc3772307317dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Mart=C3=ADnez?= Date: Mon, 31 Mar 2025 09:52:19 +0200 Subject: [PATCH 19/22] [FIX] account_reconcile_model_oca: Suggest amls candidates correctly filtering as defined in the reconciliation model Reconciliation model use case: - No auto-validate - No Match Invoice/bill fields set. Expected result: - The amls suggested should be as expected, i.e., do not filter for data that is not defined in the reconciliation model. TT52146 Related to https://github.com/odoo/odoo/commit/1db826b3c4acd6476b436eb2bbd3a5052be86672 --- .../models/account_reconcile_model.py | 21 +++++----- .../tests/test_reconciliation_match.py | 39 +++++++++++++------ 2 files changed, 39 insertions(+), 21 deletions(-) diff --git a/account_reconcile_model_oca/models/account_reconcile_model.py b/account_reconcile_model_oca/models/account_reconcile_model.py index 53d4b178..033418ec 100644 --- a/account_reconcile_model_oca/models/account_reconcile_model.py +++ b/account_reconcile_model_oca/models/account_reconcile_model.py @@ -419,12 +419,17 @@ class AccountReconcileModel(models.Model): ) """ # noqa: E501 all_params += where_params + + enabled_matches = [] + if self.match_text_location_label: + enabled_matches.append(("account_move_line", "name")) + if self.match_text_location_note: + enabled_matches.append(("account_move_line__move_id", "name")) + if self.match_text_location_reference: + enabled_matches.append(("account_move_line__move_id", "ref")) + if numerical_tokens: - for table_alias, field in ( - ("account_move_line", "name"), - ("account_move_line__move_id", "name"), - ("account_move_line__move_id", "ref"), - ): + for table_alias, field in enabled_matches: sub_queries.append( rf""" SELECT @@ -448,11 +453,7 @@ class AccountReconcileModel(models.Model): ) if exact_tokens: - for table_alias, field in ( - ("account_move_line", "name"), - ("account_move_line__move_id", "name"), - ("account_move_line__move_id", "ref"), - ): + for table_alias, field in enabled_matches: sub_queries.append( rf""" SELECT diff --git a/account_reconcile_model_oca/tests/test_reconciliation_match.py b/account_reconcile_model_oca/tests/test_reconciliation_match.py index 163973a6..fc8f5324 100644 --- a/account_reconcile_model_oca/tests/test_reconciliation_match.py +++ b/account_reconcile_model_oca/tests/test_reconciliation_match.py @@ -85,6 +85,8 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon): "match_nature": "both", "match_same_currency": True, "allow_payment_tolerance": True, + "match_text_location_note": True, + "match_text_location_reference": True, "payment_tolerance_type": "percentage", "payment_tolerance_param": 0.0, "match_partner": True, @@ -426,7 +428,11 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon): @freeze_time("2019-01-01") def test_zero_payment_tolerance(self): - rule = self._create_reconcile_model(line_ids=[{}]) + rule = self._create_reconcile_model( + line_ids=[{}], + match_text_location_reference=True, + match_text_location_note=True, + ) for inv_type, bsl_sign in (("out_invoice", 1), ("in_invoice", -1)): invl = self._create_invoice_line( @@ -779,24 +785,29 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon): self.rule_1.sequence = 2 self.rule_1.auto_reconcile = True self.rule_1.payment_tolerance_param = 10.0 - self.rule_1.match_text_location_label = False self.rule_2.sequence = 1 self.rule_2.match_partner_ids |= self.partner_2 self.rule_2.auto_reconcile = True self._check_statement_matching( - self.rule_1 + self.rule_2, + self.rule_1, { self.bank_line_1: { "amls": self.invoice_line_1, "model": self.rule_1, "auto_reconcile": True, }, + }, + ) + rule_3 = self.rule_1.copy({"match_text_location_label": False}) + self._check_statement_matching( + self.rule_2 + rule_3, + { self.bank_line_2: { "amls": self.invoice_line_1 + self.invoice_line_2 + self.invoice_line_3, - "model": self.rule_1, + "model": rule_3, }, self.cash_line_1: { "model": self.rule_2, @@ -816,7 +827,6 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon): self.rule_1.allow_payment_tolerance = False self.rule_1.auto_reconcile = True self.rule_1.line_ids = [(5, 0, 0)] - self.rule_1.match_text_location_label = False self._check_statement_matching( self.rule_1, @@ -826,11 +836,17 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon): "model": self.rule_1, "auto_reconcile": True, }, + }, + ) + rule_3 = self.rule_1.copy({"match_text_location_label": False}) + self._check_statement_matching( + rule_3, + { self.bank_line_2: { "amls": self.invoice_line_1 + self.invoice_line_2 + self.invoice_line_3, - "model": self.rule_1, + "model": rule_3, }, }, ) @@ -1388,9 +1404,8 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon): rule = self._create_reconcile_model( match_partner=False, allow_payment_tolerance=False, - match_text_location_label=False, - match_text_location_reference=False, - match_text_location_note=False, + match_text_location_reference=True, + match_text_location_note=True, ) st_line = self._create_st_line(amount=1000, partner_id=False) invoice = self.env["account.move"].create( @@ -1429,8 +1444,10 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon): {"amls": term_line, "model": rule}, ) - # No matching if other checkbox is checked. - rule.match_text_location_note = True + # No matching if checkbox is unchecked. + rule.match_text_location_label = False + rule.match_text_location_reference = False + rule.match_text_location_note = False self.assertDictEqual( rule._apply_rules(st_line, None), {}, From f0d3959c0ff87971f0dcc7cc67ef52ef3454dc40 Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Mon, 31 Mar 2025 18:49:49 +0000 Subject: [PATCH 20/22] [BOT] post-merge updates --- README.md | 2 +- account_reconcile_model_oca/README.rst | 2 +- account_reconcile_model_oca/__manifest__.py | 2 +- account_reconcile_model_oca/static/description/index.html | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index cbf91245..54da3f3d 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ addon | version | maintainers | summary --- | --- | --- | --- [account_in_payment](account_in_payment/) | 17.0.1.0.0 | | This module enables in-payment mode for your accounting [account_mass_reconcile](account_mass_reconcile/) | 17.0.1.0.1 | | Account Mass Reconcile -[account_reconcile_model_oca](account_reconcile_model_oca/) | 17.0.1.0.3 | | This includes the logic moved from Odoo Community to Odoo Enterprise +[account_reconcile_model_oca](account_reconcile_model_oca/) | 17.0.1.0.4 | | This includes the logic moved from Odoo Community to Odoo Enterprise [account_reconcile_oca](account_reconcile_oca/) | 17.0.1.5.7 | [![etobella](https://github.com/etobella.png?size=30px)](https://github.com/etobella) | Reconcile addons for Odoo CE accounting [account_statement_base](account_statement_base/) | 17.0.1.5.0 | [![alexis-via](https://github.com/alexis-via.png?size=30px)](https://github.com/alexis-via) | Base module for Bank Statements diff --git a/account_reconcile_model_oca/README.rst b/account_reconcile_model_oca/README.rst index b622f4c1..c266798b 100644 --- a/account_reconcile_model_oca/README.rst +++ b/account_reconcile_model_oca/README.rst @@ -7,7 +7,7 @@ Account Reconcile Model Oca !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! source digest: sha256:47f1e5b39eb79210fe77d7228768cfedf31f124a30046f15ca7bb2bc33c34a9d + !! source digest: sha256:f0554ce70e9ac90badf0a4082aecd5af7011cf1461fe0cc6678577d2b7f87e21 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png diff --git a/account_reconcile_model_oca/__manifest__.py b/account_reconcile_model_oca/__manifest__.py index bc263a62..997b00f6 100644 --- a/account_reconcile_model_oca/__manifest__.py +++ b/account_reconcile_model_oca/__manifest__.py @@ -5,7 +5,7 @@ "name": "Account Reconcile Model Oca", "summary": """ This includes the logic moved from Odoo Community to Odoo Enterprise""", - "version": "17.0.1.0.3", + "version": "17.0.1.0.4", "license": "LGPL-3", "author": "Dixmit,Odoo,Odoo Community Association (OCA)", "website": "https://github.com/OCA/account-reconcile", diff --git a/account_reconcile_model_oca/static/description/index.html b/account_reconcile_model_oca/static/description/index.html index 981036d7..ef60142c 100644 --- a/account_reconcile_model_oca/static/description/index.html +++ b/account_reconcile_model_oca/static/description/index.html @@ -367,7 +367,7 @@ ul.auto-toc { !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -!! source digest: sha256:47f1e5b39eb79210fe77d7228768cfedf31f124a30046f15ca7bb2bc33c34a9d +!! source digest: sha256:f0554ce70e9ac90badf0a4082aecd5af7011cf1461fe0cc6678577d2b7f87e21 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->

Beta License: LGPL-3 OCA/account-reconcile Translate me on Weblate Try me on Runboat

This module restores account reconciliation models functions moved from From 64b048cd9f869f11b4673b10607bb22762b1e7a3 Mon Sep 17 00:00:00 2001 From: Enric Tobella Date: Tue, 1 Apr 2025 11:48:57 +0200 Subject: [PATCH 21/22] [IMP] account_reconcile_oca: Allow an option to avoid a function --- .../models/account_bank_statement_line.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/account_reconcile_oca/models/account_bank_statement_line.py b/account_reconcile_oca/models/account_bank_statement_line.py index 65817970..5a046b33 100644 --- a/account_reconcile_oca/models/account_bank_statement_line.py +++ b/account_reconcile_oca/models/account_bank_statement_line.py @@ -1276,3 +1276,11 @@ class AccountBankStatementLine(models.Model): for line in lines: self._add_account_move_line(line, keep_current=True) return res + + def _retrieve_partner(self): + if self.env.context.get("skip_retrieve_partner"): + # This hook can be used, for example, when importing files. + # With large databases, we already have the information, moreover, + # the data might be preloaded, so it has no sense to import it again + return self.partner_id + return super()._retrieve_partner() From 99b8269256c9ff349a03db9c1b563cc0f0dde330 Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Thu, 3 Apr 2025 06:17:34 +0000 Subject: [PATCH 22/22] [BOT] post-merge updates --- README.md | 2 +- account_reconcile_oca/README.rst | 2 +- account_reconcile_oca/__manifest__.py | 2 +- account_reconcile_oca/static/description/index.html | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 54da3f3d..cc787d91 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ addon | version | maintainers | summary [account_in_payment](account_in_payment/) | 17.0.1.0.0 | | This module enables in-payment mode for your accounting [account_mass_reconcile](account_mass_reconcile/) | 17.0.1.0.1 | | Account Mass Reconcile [account_reconcile_model_oca](account_reconcile_model_oca/) | 17.0.1.0.4 | | This includes the logic moved from Odoo Community to Odoo Enterprise -[account_reconcile_oca](account_reconcile_oca/) | 17.0.1.5.7 | [![etobella](https://github.com/etobella.png?size=30px)](https://github.com/etobella) | Reconcile addons for Odoo CE accounting +[account_reconcile_oca](account_reconcile_oca/) | 17.0.1.5.8 | [![etobella](https://github.com/etobella.png?size=30px)](https://github.com/etobella) | Reconcile addons for Odoo CE accounting [account_statement_base](account_statement_base/) | 17.0.1.5.0 | [![alexis-via](https://github.com/alexis-via.png?size=30px)](https://github.com/alexis-via) | Base module for Bank Statements [//]: # (end addons) diff --git a/account_reconcile_oca/README.rst b/account_reconcile_oca/README.rst index b93b73a0..b0a0dd00 100644 --- a/account_reconcile_oca/README.rst +++ b/account_reconcile_oca/README.rst @@ -7,7 +7,7 @@ Account Reconcile Oca !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! source digest: sha256:b17130b1fa64185780d85cb252c1af8cf33bbc9745ce2a82e13263c86619e417 + !! source digest: sha256:a1d19f7cb36b1b53954b505f964b766f7237615fc66c01368256cc9c5f390fd2 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png diff --git a/account_reconcile_oca/__manifest__.py b/account_reconcile_oca/__manifest__.py index d1129a26..90bf3ebc 100644 --- a/account_reconcile_oca/__manifest__.py +++ b/account_reconcile_oca/__manifest__.py @@ -5,7 +5,7 @@ "name": "Account Reconcile Oca", "summary": """ Reconcile addons for Odoo CE accounting""", - "version": "17.0.1.5.7", + "version": "17.0.1.5.8", "license": "AGPL-3", "author": "CreuBlanca,Dixmit,Odoo Community Association (OCA)", "maintainers": ["etobella"], diff --git a/account_reconcile_oca/static/description/index.html b/account_reconcile_oca/static/description/index.html index 1dcc91c3..73472519 100644 --- a/account_reconcile_oca/static/description/index.html +++ b/account_reconcile_oca/static/description/index.html @@ -367,7 +367,7 @@ ul.auto-toc { !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -!! source digest: sha256:b17130b1fa64185780d85cb252c1af8cf33bbc9745ce2a82e13263c86619e417 +!! source digest: sha256:a1d19f7cb36b1b53954b505f964b766f7237615fc66c01368256cc9c5f390fd2 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->

Beta License: AGPL-3 OCA/account-reconcile Translate me on Weblate Try me on Runboat

This addon allows to reconcile bank statements and account marked as