This commit is contained in:
gsinghpal
2026-05-16 13:18:52 -04:00
parent 191a9c82be
commit 9ebf89bde2
1080 changed files with 0 additions and 1197 deletions

View File

@@ -0,0 +1,2 @@
from . import auto_reconcile_wizard
from . import bulk_reconcile_wizard

View File

@@ -0,0 +1,78 @@
"""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,
}

View File

@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="view_fusion_auto_reconcile_wizard_form" model="ir.ui.view">
<field name="name">fusion.auto.reconcile.wizard.form</field>
<field name="model">fusion.auto.reconcile.wizard</field>
<field name="arch" type="xml">
<form string="Auto-Reconcile Bank Lines">
<group invisible="state == 'done'">
<field name="journal_id" options="{'no_create': True}"/>
<field name="date_from"/>
<field name="date_to"/>
<field name="strategy"/>
<field name="only_with_partner"/>
</group>
<group invisible="state != 'done'" string="Results">
<field name="reconciled_count"/>
<field name="skipped_count"/>
<field name="error_count"/>
<field name="error_summary"/>
</group>
<field name="state" invisible="1"/>
<footer>
<button name="action_run" type="object" string="Run"
class="btn-primary" invisible="state == 'done'"/>
<button special="cancel" string="Close"/>
</footer>
</form>
</field>
</record>
<record id="action_fusion_auto_reconcile_wizard" model="ir.actions.act_window">
<field name="name">Auto-Reconcile</field>
<field name="res_model">fusion.auto.reconcile.wizard</field>
<field name="view_mode">form</field>
<field name="target">new</field>
</record>
</odoo>

View File

@@ -0,0 +1,93 @@
"""Bulk reconcile wizard — operates on user-selected records.
Reads active_ids from context (selected bank lines). Two modes:
1. Auto (run engine on all selected with chosen strategy)
2. Apply reconcile model (apply a chosen account.reconcile.model to all)
"""
from odoo import _, api, fields, models
from odoo.exceptions import UserError
class FusionBulkReconcileWizard(models.TransientModel):
_name = "fusion.bulk.reconcile.wizard"
_description = "Bulk Reconcile Selected Bank Lines Wizard"
statement_line_ids = fields.Many2many(
'account.bank.statement.line',
string="Selected Bank Lines",
default=lambda self: [(6, 0, self._default_line_ids())])
selected_count = fields.Integer(
compute='_compute_selected_count', string="# Selected")
mode = fields.Selection([
('auto', 'Auto (engine reconcile_batch)'),
('reconcile_model', 'Apply Reconcile Model'),
], default='auto', required=True)
strategy = fields.Selection([
('auto', 'Auto'),
('amount_exact', 'Amount Exact only'),
('fifo', 'FIFO only'),
('multi_invoice', 'Multi-invoice'),
], default='auto')
reconcile_model_id = fields.Many2one(
'account.reconcile.model', string="Reconcile Model",
domain=[('rule_type', '=', 'writeoff_button')])
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)
@api.model
def _default_line_ids(self):
ctx = self.env.context
if ctx.get('active_model') == 'account.bank.statement.line':
return ctx.get('active_ids', [])
return []
@api.depends('statement_line_ids')
def _compute_selected_count(self):
for w in self:
w.selected_count = len(w.statement_line_ids)
def action_run(self):
self.ensure_one()
if self.mode == 'auto':
result = self.env['fusion.reconcile.engine'].reconcile_batch(
self.statement_line_ids, strategy=self.strategy)
elif self.mode == 'reconcile_model':
if not self.reconcile_model_id:
raise UserError(_("Pick a reconcile model first."))
# Phase 1 fallback: apply the model line-by-line via the engine's
# write_off path (simplified — real reconcile-model semantics are
# more nuanced; full integration in Task 38 follow-up).
result = {'reconciled_count': 0, 'skipped': 0, 'errors': []}
for line in self.statement_line_ids:
try:
self.reconcile_model_id._apply_lines_for_bank_statement_line(line)
result['reconciled_count'] += 1
except Exception as e: # noqa: BLE001
result['errors'].append({'line_id': line.id, 'error': str(e)})
else:
result = {'reconciled_count': 0, 'skipped': 0, 'errors': []}
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,
}

View File

@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="view_fusion_bulk_reconcile_wizard_form" model="ir.ui.view">
<field name="name">fusion.bulk.reconcile.wizard.form</field>
<field name="model">fusion.bulk.reconcile.wizard</field>
<field name="arch" type="xml">
<form string="Bulk Reconcile Selected">
<group invisible="state == 'done'">
<field name="selected_count" readonly="1"/>
<field name="mode" widget="radio"/>
<field name="strategy" invisible="mode != 'auto'"/>
<field name="reconcile_model_id"
invisible="mode != 'reconcile_model'"
required="mode == 'reconcile_model'"/>
</group>
<group invisible="state != 'done'" string="Results">
<field name="reconciled_count"/>
<field name="skipped_count"/>
<field name="error_count"/>
<field name="error_summary"/>
</group>
<field name="state" invisible="1"/>
<field name="statement_line_ids" invisible="1"/>
<footer>
<button name="action_run" type="object" string="Run"
class="btn-primary" invisible="state == 'done'"/>
<button special="cancel" string="Close"/>
</footer>
</form>
</field>
</record>
<record id="action_fusion_bulk_reconcile_wizard" model="ir.actions.act_window">
<field name="name">Bulk Reconcile Selected</field>
<field name="res_model">fusion.bulk.reconcile.wizard</field>
<field name="view_mode">form</field>
<field name="target">new</field>
<field name="binding_model_id" ref="account.model_account_bank_statement_line"/>
<field name="binding_view_types">list</field>
</record>
</odoo>