Files
Odoo-Modules/fusion_accounting_bank_rec/models/fusion_reconcile_precedent.py
gsinghpal b6aedc9bbe feat(fusion_accounting_bank_rec): migration wizard bootstrap step
Adds bank_rec_bootstrap step that backfills fusion.reconcile.precedent
from existing account.partial.reconcile rows during migration. This
gives the AI memory from past Enterprise reconciles. Also triggers
pattern refresh + MV refresh for immediate UI readiness.

- New service services/precedent_backfill.py walks
  account.partial.reconcile rows, identifies the bank-statement-line
  side, and creates a precedent per qualifying partial. Idempotent via
  (statement_line, account, amount, source='backfill') signature.
- New model models/fusion_migration_wizard.py inherits
  fusion.migration.wizard, exposes _bank_rec_bootstrap_step() (callable
  from tests/audit), and overrides action_run_migration() to call
  super() + the bootstrap.
- Adds 'backfill' to fusion.reconcile.precedent.source selection.
- Adds fusion_accounting_migration to depends.

Made-with: Cursor
2026-04-19 13:24:17 -04:00

51 lines
2.0 KiB
Python

"""Per-historical-decision reconciliation memory.
One row per past reconciliation. Holds the full feature vector + outcome,
used by precedent_lookup for K-nearest-neighbour search when scoring a
new bank line.
"""
from odoo import fields, models
class FusionReconcilePrecedent(models.Model):
_name = "fusion.reconcile.precedent"
_description = "Historical bank reconciliation decision (memory)"
_order = "reconciled_at desc, id desc"
company_id = fields.Many2one('res.company', required=True, index=True,
default=lambda self: self.env.company)
partner_id = fields.Many2one('res.partner', index=True)
# Bank line features (the "input")
amount = fields.Monetary(currency_field='currency_id')
currency_id = fields.Many2one('res.currency')
date = fields.Date()
memo_tokens = fields.Char(
help="Comma-separated normalized memo tokens (output of memo_tokenizer)")
journal_id = fields.Many2one('account.journal')
# Outcome (the "decision made")
matched_move_line_count = fields.Integer(
help="1 = exact, 2-3 = consolidation, etc.")
matched_account_ids = fields.Char(
help="Comma-separated account.account IDs that were matched against")
matched_invoice_ages_days = fields.Char(
help="Comma-separated days-old at reconcile time, e.g. '12, 45, 78'")
write_off_amount = fields.Float()
write_off_account_id = fields.Many2one('account.account')
exchange_diff = fields.Boolean()
# Provenance
reconciler_user_id = fields.Many2one('res.users')
reconciled_at = fields.Datetime()
source = fields.Selection([
('historical_bootstrap', 'Imported from history'),
('backfill', 'Backfilled from account.partial.reconcile (migration)'),
('manual', 'Manual reconcile via fusion'),
('ai_accepted', 'AI suggestion accepted'),
('auto_rule', 'account.reconcile.model auto-fired'),
], required=True)
# No uniqueness constraint — multiple reconciles can share features