Files
Odoo-Modules/fusion_accounting_bank_rec/tests/test_mv_unreconciled.py
gsinghpal d953525758 fix(fusion_accounting_bank_rec): MV correctness for V19 schema + Odoo test harness
Three issues surfaced when running the MV smoke tests against westin-v19:

1. account_bank_statement_line has no `date` column in V19 — `date` is a
   related field flowing through move_id -> account_move.date. The MV
   now JOINs account_move and selects am.date.
2. is_reconciled is nullable; replace `= FALSE` with `IS NOT TRUE` so
   nulls (genuinely unreconciled lines that haven't had the compute run
   yet) are still included.
3. _refresh() now flushes the ORM cache (env.flush_all()) before the
   REFRESH so computed-stored fields like is_reconciled are written to
   the DB before the materialization snapshot reads them. Previously the
   reconcile-then-refresh path saw the pre-reconcile column value.
4. _trigger_mv_refresh() (suggestion create/write hook) now uses
   concurrently=False because Postgres forbids
   REFRESH MATERIALIZED VIEW CONCURRENTLY inside a transaction block,
   and Odoo's per-request cursor is always inside one. The cron path
   (Task 25) will open an autocommit cursor for CONCURRENTLY refreshes.
5. Tests dropped the env.cr.commit() pattern: Postgres always shows a
   transaction its own writes, so a non-CONCURRENTLY refresh in the
   same txn picks up freshly-inserted rows. Cleaner + works inside
   TransactionCase, which forbids cr.commit().

Verified: 4 new MV tests pass, 0 failures across 118 logical tests
(178 with parametrized property-based runs) of fusion_accounting_bank_rec
on westin-v19.

Made-with: Cursor
2026-04-19 11:51:02 -04:00

83 lines
3.6 KiB
Python

"""Smoke tests for the fusion_unreconciled_bank_line_mv materialized view.
Notes on transactional semantics:
- REFRESH MATERIALIZED VIEW (non-CONCURRENTLY) IS transactional and runs
inside the current transaction. Postgres always shows a transaction
its own (uncommitted) writes, so an INSERT followed by a REFRESH in
the same transaction picks up the new row — no `cr.commit()` needed.
- Odoo's TransactionCase forbids cr.commit() anyway (it would break the
per-test savepoint rollback). We rely on rollback to clean up both
the test fixtures and the MV-table mutations from the refresh.
- REFRESH MATERIALIZED VIEW CONCURRENTLY must run OUTSIDE a transaction
block; we always pass concurrently=False from tests. The production
cron path (Task 25) will open a dedicated autocommit cursor for the
concurrent refresh.
"""
from odoo.tests.common import TransactionCase, tagged
from . import _factories as f
@tagged('post_install', '-at_install')
class TestUnreconciledBankLineMV(TransactionCase):
def setUp(self):
super().setUp()
self.partner = self.env['res.partner'].create({
'name': 'MV Test Partner',
})
# Refresh once at the start so the MV reflects the current snapshot
# (including any rows inserted earlier in this savepoint chain).
self.env['fusion.unreconciled.bank.line.mv']._refresh(
concurrently=False)
def test_mv_exists_and_is_queryable(self):
# Smoke: the model can be searched without error.
rows = self.env['fusion.unreconciled.bank.line.mv'].search(
[], limit=10)
self.assertIsNotNone(rows)
def test_mv_includes_unreconciled_line(self):
bank_line = f.make_bank_line(
self.env, amount=999.99, partner=self.partner)
self.env['fusion.unreconciled.bank.line.mv']._refresh(
concurrently=False)
mv_row = self.env['fusion.unreconciled.bank.line.mv'].search([
('id', '=', bank_line.id),
])
self.assertTrue(
mv_row,
"MV should contain freshly-inserted unreconciled line")
self.assertAlmostEqual(mv_row.amount, 999.99, places=2)
# No suggestion yet -> band 'none', confidence 0.
self.assertEqual(mv_row.confidence_band, 'none')
self.assertEqual(mv_row.attachment_count, 0)
def test_mv_excludes_reconciled_line(self):
bank_line, recv_lines = f.make_reconcileable_pair(
self.env, amount=100.00, partner=self.partner)
self.env['fusion.reconcile.engine'].reconcile_one(
bank_line, against_lines=recv_lines)
self.env['fusion.unreconciled.bank.line.mv']._refresh(
concurrently=False)
mv_row = self.env['fusion.unreconciled.bank.line.mv'].search([
('id', '=', bank_line.id),
])
self.assertFalse(
mv_row, "Reconciled line should be excluded from MV")
def test_mv_confidence_band_high_for_high_conf_suggestion(self):
bank_line = f.make_bank_line(
self.env, amount=500.00, partner=self.partner)
f.make_suggestion(
self.env, statement_line=bank_line, confidence=0.92)
self.env['fusion.unreconciled.bank.line.mv']._refresh(
concurrently=False)
mv_row = self.env['fusion.unreconciled.bank.line.mv'].search([
('id', '=', bank_line.id),
])
self.assertTrue(mv_row, "MV row should exist for suggestion line")
# 0.92 falls in the 'high' band per the SQL CASE (>= 0.85).
self.assertEqual(mv_row.confidence_band, 'high')
self.assertAlmostEqual(mv_row.top_confidence, 0.92, places=2)