diff --git a/account_reconcile_oca/README.rst b/account_reconcile_oca/README.rst
index b0a0dd00..bd851a93 100644
--- a/account_reconcile_oca/README.rst
+++ b/account_reconcile_oca/README.rst
@@ -52,12 +52,20 @@ Access Invoicing / Accounting / Actions / Reconcile All the possible
reconcile options will show and you will be able to reconcile properly.
You can access the same widget from accounts and Partners.
+Applying a reconcilation model to multiple lines
+------------------------------------------------
+
+1. Select multiple transactions (account.bank.statement.line)
+2. Actions -> Reconcile with model
+3. In the wizard, select the model
+4. Run
+
Known issues / Roadmap
======================
The following bugs are already detected:
-- Creation of activities on the chatter do show automatically
+- Creation of activities on the chatter do show automatically
Bug Tracker
===========
@@ -81,7 +89,8 @@ Authors
Contributors
------------
-- Enric Tobella
+- Enric Tobella
+- Simone Rubino
Maintainers
-----------
diff --git a/account_reconcile_oca/__init__.py b/account_reconcile_oca/__init__.py
index cc6b6354..a0f65393 100644
--- a/account_reconcile_oca/__init__.py
+++ b/account_reconcile_oca/__init__.py
@@ -1,2 +1,3 @@
from . import models
+from . import wizards
from .hooks import post_init_hook
diff --git a/account_reconcile_oca/__manifest__.py b/account_reconcile_oca/__manifest__.py
index 90bf3ebc..67635d24 100644
--- a/account_reconcile_oca/__manifest__.py
+++ b/account_reconcile_oca/__manifest__.py
@@ -25,6 +25,7 @@
"views/account_move.xml",
"views/account_account.xml",
"views/account_bank_statement.xml",
+ "wizards/reconcile_multiple_lines views.xml",
],
"demo": ["demo/demo.xml"],
"post_init_hook": "post_init_hook",
diff --git a/account_reconcile_oca/readme/CONTRIBUTORS.md b/account_reconcile_oca/readme/CONTRIBUTORS.md
index c84bf49d..2a3cbdd7 100644
--- a/account_reconcile_oca/readme/CONTRIBUTORS.md
+++ b/account_reconcile_oca/readme/CONTRIBUTORS.md
@@ -1 +1,2 @@
- Enric Tobella
+- Simone Rubino
diff --git a/account_reconcile_oca/readme/USAGE.md b/account_reconcile_oca/readme/USAGE.md
index 2a053a39..6c9ae7c4 100644
--- a/account_reconcile_oca/readme/USAGE.md
+++ b/account_reconcile_oca/readme/USAGE.md
@@ -8,3 +8,10 @@ capabilities. Select reconcile on the journal of your choice.
Access Invoicing / Accounting / Actions / Reconcile All the possible
reconcile options will show and you will be able to reconcile properly.
You can access the same widget from accounts and Partners.
+
+## Applying a reconcilation model to multiple lines
+
+1. Select multiple transactions (account.bank.statement.line)
+2. Actions -> Reconcile with model
+3. In the wizard, select the model
+4. Run
diff --git a/account_reconcile_oca/security/ir.model.access.csv b/account_reconcile_oca/security/ir.model.access.csv
index d73fa145..66529426 100644
--- a/account_reconcile_oca/security/ir.model.access.csv
+++ b/account_reconcile_oca/security/ir.model.access.csv
@@ -1,3 +1,4 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_account_account_reconcile,account.account.reconcile,model_account_account_reconcile,account.group_account_user,1,1,0,0
access_account_account_reconcile_data,account.account.reconcile,model_account_account_reconcile_data,account.group_account_user,1,1,1,1
+account_reconcile_oca.access_account_reconcile_oca_reconcile_multiple_lines,Allow account user to reconcile multiple lines with a reconciliation model,account_reconcile_oca.model_account_reconcile_oca_reconcile_multiple_lines,account.group_account_user,1,1,1,1
diff --git a/account_reconcile_oca/static/description/index.html b/account_reconcile_oca/static/description/index.html
index 73472519..314cf1c0 100644
--- a/account_reconcile_oca/static/description/index.html
+++ b/account_reconcile_oca/static/description/index.html
@@ -378,14 +378,15 @@ reconcile.
Usage
-Known issues / Roadmap
-Bug Tracker
-Credits
@@ -403,16 +404,25 @@ capabilities. Select reconcile on the journal of your choice.
reconcile options will show and you will be able to reconcile properly.
You can access the same widget from accounts and Partners.
+
+
+
+- Select multiple transactions (account.bank.statement.line)
+- Actions -> Reconcile with model
+- In the wizard, select the model
+- Run
+
+
-
+
The following bugs are already detected:
- Creation of activities on the chatter do show automatically
-
+
Bugs are tracked on GitHub Issues.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
@@ -420,22 +430,23 @@ If you spotted it first, help us to smash it by providing a detailed and welcome
Do not contact contributors directly about support or help with technical issues.
-
+
-
+
- Enric Tobella
+- Simone Rubino
-
+
This module is maintained by the OCA.
diff --git a/account_reconcile_oca/tests/__init__.py b/account_reconcile_oca/tests/__init__.py
index 17f193ed..d3a4dc27 100644
--- a/account_reconcile_oca/tests/__init__.py
+++ b/account_reconcile_oca/tests/__init__.py
@@ -1,2 +1,3 @@
from . import test_bank_account_reconcile
from . import test_account_reconcile
+from . import test_reconcile_multiple_lines
diff --git a/account_reconcile_oca/tests/test_reconcile_multiple_lines.py b/account_reconcile_oca/tests/test_reconcile_multiple_lines.py
new file mode 100644
index 00000000..5b2aced6
--- /dev/null
+++ b/account_reconcile_oca/tests/test_reconcile_multiple_lines.py
@@ -0,0 +1,88 @@
+# Copyright 2025 Simone Rubino
+# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
+
+from odoo import Command
+from odoo.tests import Form, tagged
+
+from odoo.addons.account_reconcile_model_oca.tests.common import (
+ TestAccountReconciliationCommon,
+)
+
+
+@tagged("post_install", "-at_install")
+class TestReconcileMultipleLines(TestAccountReconciliationCommon):
+ @classmethod
+ def setUpClass(cls, chart_template_ref=None):
+ super().setUpClass(chart_template_ref=chart_template_ref)
+ (cls.reconcile_account,) = cls.env["account.account"].create(
+ [
+ {
+ "name": "Test account for reconciliation",
+ "code": "TSTREC",
+ "account_type": "liability_payable",
+ }
+ ]
+ )
+
+ cls.bank_journal = cls.company_data["default_journal_bank"]
+ (
+ cls.bank_line_1,
+ cls.bank_line_2,
+ ) = cls.env["account.bank.statement.line"].create(
+ [
+ {
+ "journal_id": cls.bank_journal.id,
+ "date": "2020-01-01",
+ "amount": 100,
+ },
+ {
+ "journal_id": cls.bank_journal.id,
+ "date": "2020-01-01",
+ "amount": 600,
+ },
+ ],
+ )
+ (cls.reconcile_model,) = cls.env["account.reconcile.model"].create(
+ [
+ {
+ "name": "Test Writeoff",
+ "rule_type": "writeoff_button",
+ "line_ids": [
+ Command.create(
+ {
+ "account_id": cls.reconcile_account.id,
+ }
+ ),
+ ],
+ },
+ ]
+ )
+
+ def _get_wizard(self, statement_lines, reconcile_model):
+ selection_context = {
+ "active_model": statement_lines._name,
+ "active_ids": statement_lines.ids,
+ }
+ wizard_model = self.env[
+ "account_reconcile_oca.reconcile_multiple_lines"
+ ].with_context(**selection_context)
+ wizard_form = Form(wizard_model)
+ wizard_form.manual_model_id = reconcile_model
+ return wizard_form.save()
+
+ def test_writeoff_2_lines(self):
+ """The wizard can writeoff 2 statement lines."""
+ # Arrange
+ reconcile_account = self.reconcile_account
+ statement_lines = self.bank_line_1 | self.bank_line_2
+ writeoff_reconcile_model = self.reconcile_model
+ wizard = self._get_wizard(statement_lines, writeoff_reconcile_model)
+ # pre-condition
+ self.assertEqual(writeoff_reconcile_model.rule_type, "writeoff_button")
+ self.assertNotIn(reconcile_account, statement_lines.line_ids.account_id)
+
+ # Act
+ wizard.run()
+
+ # Assert
+ self.assertIn(reconcile_account, statement_lines.line_ids.account_id)
diff --git a/account_reconcile_oca/wizards/__init__.py b/account_reconcile_oca/wizards/__init__.py
new file mode 100644
index 00000000..bc02c4a8
--- /dev/null
+++ b/account_reconcile_oca/wizards/__init__.py
@@ -0,0 +1,3 @@
+# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
+
+from . import reconcile_multiple_lines
diff --git a/account_reconcile_oca/wizards/reconcile_multiple_lines views.xml b/account_reconcile_oca/wizards/reconcile_multiple_lines views.xml
new file mode 100644
index 00000000..edaefe50
--- /dev/null
+++ b/account_reconcile_oca/wizards/reconcile_multiple_lines views.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+ Form view to reconcile multiple lines with a reconciliation model
+ account_reconcile_oca.reconcile_multiple_lines
+
+
+
+
+
+
+ Reconcile with model
+ account_reconcile_oca.reconcile_multiple_lines
+ form
+ new
+
+
+
diff --git a/account_reconcile_oca/wizards/reconcile_multiple_lines.py b/account_reconcile_oca/wizards/reconcile_multiple_lines.py
new file mode 100644
index 00000000..a37b6fc3
--- /dev/null
+++ b/account_reconcile_oca/wizards/reconcile_multiple_lines.py
@@ -0,0 +1,46 @@
+# Copyright 2025 Simone Rubino
+# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
+
+from odoo import _, fields, models
+from odoo.exceptions import UserError
+
+
+class ReconcileMultipleLines(models.TransientModel):
+ _name = "account_reconcile_oca.reconcile_multiple_lines"
+ _description = "Reconcile multiple lines with a reconciliation model"
+
+ manual_model_id = fields.Many2one(
+ comodel_name="account.reconcile.model",
+ required=True,
+ )
+
+ def _get_statement_lines(self):
+ model = self.env.context.get("active_model")
+ ids = self.env.context.get("active_ids")
+ statement_lines = self.env[model].browse(ids)
+ return statement_lines
+
+ def _apply_model_to_line(self, reconciliation_model, statement_line):
+ reconciliation_model.ensure_one()
+ statement_line.ensure_one()
+ partner = reconciliation_model._get_partner_from_mapping(statement_line)
+ if not reconciliation_model._is_applicable_for(statement_line, partner):
+ raise UserError(
+ _(
+ "Reconcilation model %(model)s "
+ "cannot be applied to line %(line)s.\n"
+ "Please select a compatible reconciliation model "
+ "or deselect the line.",
+ model=reconciliation_model.display_name,
+ line=statement_line.display_name,
+ )
+ )
+ statement_line.manual_model_id = reconciliation_model
+ statement_line._onchange_manual_model_id()
+ statement_line.reconcile_bank_line()
+
+ def run(self):
+ statement_lines = self._get_statement_lines()
+ reconciliation_model = self.manual_model_id
+ for line in statement_lines:
+ self._apply_model_to_line(reconciliation_model, line)