"""Auto-reconcile wizard. Lets the user pick filters (journal, date range, strategy) and runs fusion.reconcile.engine.reconcile_batch on all matching unreconciled bank lines. Shows summary of results. """ from odoo import _, fields, models class FusionAutoReconcileWizard(models.TransientModel): _name = "fusion.auto.reconcile.wizard" _description = "Auto-Reconcile Bank Statement Lines Wizard" journal_id = fields.Many2one( 'account.journal', string="Bank Journal", domain=[('type', '=', 'bank')], required=True) date_from = fields.Date(string="Date From") date_to = fields.Date(string="Date To", default=fields.Date.today) strategy = fields.Selection([ ('auto', 'Auto (try amount-exact, then multi-invoice, then FIFO)'), ('amount_exact', 'Amount Exact only'), ('fifo', 'FIFO only'), ('multi_invoice', 'Multi-invoice combination only'), ], default='auto', required=True) only_with_partner = fields.Boolean( string="Only lines with a partner", default=True, help="Most safer matches require a known partner. Untick to attempt " "matching for orphan lines too (uses memo tokenization).") state = fields.Selection([ ('draft', 'Draft'), ('done', 'Done'), ], default='draft') reconciled_count = fields.Integer(readonly=True) skipped_count = fields.Integer(readonly=True) error_count = fields.Integer(readonly=True) error_summary = fields.Text(readonly=True) def _build_domain(self): self.ensure_one() domain = [ ('journal_id', '=', self.journal_id.id), ('is_reconciled', '=', False), ] if self.date_from: domain.append(('date', '>=', self.date_from)) if self.date_to: domain.append(('date', '<=', self.date_to)) if self.only_with_partner: domain.append(('partner_id', '!=', False)) return domain def action_run(self): self.ensure_one() Line = self.env['account.bank.statement.line'] lines = Line.search(self._build_domain(), limit=1000) result = self.env['fusion.reconcile.engine'].reconcile_batch( lines, strategy=self.strategy) errors = result.get('errors', []) self.write({ 'state': 'done', 'reconciled_count': result.get('reconciled_count', 0), 'skipped_count': result.get('skipped', 0), 'error_count': len(errors), 'error_summary': '\n'.join( f"Line {e['line_id']}: {e['error']}" for e in errors[:20] ) or False, }) return { 'type': 'ir.actions.act_window', 'res_model': self._name, 'res_id': self.id, 'view_mode': 'form', 'target': 'new', 'context': self.env.context, }