Phase 3 (fusion_accounting_assets) changed list_assets() to return
{count, total, assets} dict instead of a flat list — consistent with
bank_rec.list_unreconciled, reports.run_report, followup.list_overdue.
The pre-existing test in fusion_accounting_ai still asserted isinstance(rows, list)
and was failing on every run since Phase 3 merge. Updated to assert dict shape.
Made-with: Cursor
151 lines
6.3 KiB
Python
151 lines
6.3 KiB
Python
from odoo.tests.common import TransactionCase, tagged
|
|
from odoo.addons.fusion_accounting_ai.services.data_adapters.base import (
|
|
DataAdapter, AdapterMode,
|
|
)
|
|
from odoo.addons.fusion_accounting_ai.services.data_adapters import get_adapter
|
|
|
|
|
|
@tagged('post_install', '-at_install')
|
|
class TestDataAdapterBase(TransactionCase):
|
|
"""Verify the data adapter base class chooses the correct backend."""
|
|
|
|
def test_adapter_mode_pure_community(self):
|
|
"""With no fusion native and no Enterprise, adapter selects COMMUNITY."""
|
|
adapter = DataAdapter(self.env)
|
|
mode = adapter._select_mode(
|
|
fusion_native_model='fusion.bank.rec.widget',
|
|
enterprise_module='account_accountant',
|
|
)
|
|
self.assertIn(mode, (AdapterMode.FUSION, AdapterMode.ENTERPRISE, AdapterMode.COMMUNITY))
|
|
|
|
def test_adapter_falls_back_when_fusion_model_missing(self):
|
|
"""Adapter must not error when the fusion native model isn't loaded."""
|
|
adapter = DataAdapter(self.env)
|
|
mode = adapter._select_mode(
|
|
fusion_native_model='fusion.never.exists',
|
|
enterprise_module='also_does_not_exist',
|
|
)
|
|
self.assertEqual(mode, AdapterMode.COMMUNITY)
|
|
|
|
|
|
@tagged('post_install', '-at_install')
|
|
class TestBankRecAdapter(TransactionCase):
|
|
"""Verify the bank-rec adapter returns rows in any install profile."""
|
|
|
|
def setUp(self):
|
|
super().setUp()
|
|
self.journal = self.env['account.journal'].create({
|
|
'name': 'Test Bank',
|
|
'type': 'bank',
|
|
'code': 'TBNK',
|
|
})
|
|
self.statement = self.env['account.bank.statement'].create({
|
|
'name': 'Test Statement',
|
|
'journal_id': self.journal.id,
|
|
})
|
|
self.line = self.env['account.bank.statement.line'].create({
|
|
'statement_id': self.statement.id,
|
|
'journal_id': self.journal.id,
|
|
'date': '2026-04-18',
|
|
'payment_ref': 'Test Payment',
|
|
'amount': 100.0,
|
|
})
|
|
|
|
def test_list_unreconciled_returns_our_test_line(self):
|
|
"""The adapter should find the unreconciled line we just created."""
|
|
adapter = get_adapter(self.env, 'bank_rec')
|
|
rows = adapter.list_unreconciled(journal_id=self.journal.id, limit=10)
|
|
ids = [r['id'] for r in rows]
|
|
self.assertIn(self.line.id, ids,
|
|
f"Expected line {self.line.id} in unreconciled list, got: {ids}")
|
|
|
|
|
|
@tagged('post_install', '-at_install')
|
|
class TestReportsAdapter(TransactionCase):
|
|
"""Verify the reports adapter computes a trial-balance-shaped result."""
|
|
|
|
def test_trial_balance_returns_rows_in_pure_community(self):
|
|
adapter = get_adapter(self.env, 'reports')
|
|
result = adapter.trial_balance()
|
|
self.assertIsInstance(result, list)
|
|
for row in result:
|
|
self.assertIn('account_id', row)
|
|
self.assertIn('balance', row)
|
|
|
|
def test_run_report_returns_lines_or_error_dict(self):
|
|
"""run_report() must always return either an Enterprise-shaped
|
|
{'report_name', 'lines'} dict or an {'error': ...} dict — never raise."""
|
|
adapter = get_adapter(self.env, 'reports')
|
|
result = adapter.run_report(ref_id='account_reports.profit_and_loss')
|
|
self.assertIsInstance(result, dict)
|
|
# Either a report_name+lines response or an error — both valid
|
|
self.assertTrue(
|
|
('lines' in result and 'report_name' in result) or 'error' in result,
|
|
f"Unexpected result shape: {result!r}",
|
|
)
|
|
|
|
def test_run_report_with_unknown_ref_returns_error(self):
|
|
adapter = get_adapter(self.env, 'reports')
|
|
result = adapter.run_report(ref_id='nonexistent.report.xml_id')
|
|
self.assertIsInstance(result, dict)
|
|
self.assertIn('error', result)
|
|
|
|
def test_export_report_returns_dict(self):
|
|
adapter = get_adapter(self.env, 'reports')
|
|
result = adapter.export_report(
|
|
ref_id='account_reports.profit_and_loss', fmt='pdf',
|
|
)
|
|
self.assertIsInstance(result, dict)
|
|
|
|
|
|
@tagged('post_install', '-at_install')
|
|
class TestFollowupAdapter(TransactionCase):
|
|
def test_overdue_invoices_returns_list(self):
|
|
adapter = get_adapter(self.env, 'followup')
|
|
rows = adapter.overdue_invoices(days_overdue=30)
|
|
self.assertIsInstance(rows, list)
|
|
|
|
def test_overdue_invoices_row_has_contact_fields(self):
|
|
"""The enriched shape must include email, phone, and amount_total so
|
|
the accounts_receivable tool wrapper can render them."""
|
|
adapter = get_adapter(self.env, 'followup')
|
|
rows = adapter.overdue_invoices(days_overdue=30, limit=5)
|
|
for row in rows:
|
|
for key in (
|
|
'id', 'name', 'partner_id', 'partner_name',
|
|
'partner_email', 'partner_phone',
|
|
'invoice_date_due', 'amount_total', 'amount_residual',
|
|
'days_overdue',
|
|
):
|
|
self.assertIn(key, row, f"Missing key {key!r} in overdue row")
|
|
|
|
def test_aged_receivables_returns_bucket_shape(self):
|
|
adapter = get_adapter(self.env, 'followup')
|
|
result = adapter.aged_receivables(company_id=self.env.company.id)
|
|
self.assertIn('total', result)
|
|
self.assertIn('buckets', result)
|
|
self.assertIn('line_count', result)
|
|
for bucket in ('current', '1_30', '31_60', '61_90', '90_plus'):
|
|
self.assertIn(bucket, result['buckets'])
|
|
|
|
def test_aged_payables_returns_bucket_shape(self):
|
|
adapter = get_adapter(self.env, 'followup')
|
|
result = adapter.aged_payables(company_id=self.env.company.id)
|
|
self.assertIn('total', result)
|
|
self.assertIn('buckets', result)
|
|
self.assertIn('line_count', result)
|
|
for bucket in ('current', '1_30', '31_60', '61_90', '90_plus'):
|
|
self.assertIn(bucket, result['buckets'])
|
|
|
|
|
|
@tagged('post_install', '-at_install')
|
|
class TestAssetsAdapter(TransactionCase):
|
|
def test_list_assets_returns_dict_with_assets(self):
|
|
# Phase 3 (fusion_accounting_assets) wired list_assets to return
|
|
# {count, total, assets} — consistent with bank_rec.list_unreconciled etc.
|
|
adapter = get_adapter(self.env, 'assets')
|
|
rows = adapter.list_assets()
|
|
self.assertIsInstance(rows, dict)
|
|
self.assertIn('assets', rows)
|
|
self.assertIsInstance(rows['assets'], list)
|