diff --git a/fusion_accounting_ai/services/data_adapters/__init__.py b/fusion_accounting_ai/services/data_adapters/__init__.py index df70bf1d..1f69704c 100644 --- a/fusion_accounting_ai/services/data_adapters/__init__.py +++ b/fusion_accounting_ai/services/data_adapters/__init__.py @@ -3,5 +3,7 @@ from ._registry import get_adapter, register_adapter from . import bank_rec # noqa: F401 from . import reports # noqa: F401 +from . import followup # noqa: F401 +from . import assets # noqa: F401 __all__ = ['DataAdapter', 'AdapterMode', 'get_adapter', 'register_adapter'] diff --git a/fusion_accounting_ai/services/data_adapters/assets.py b/fusion_accounting_ai/services/data_adapters/assets.py new file mode 100644 index 00000000..df7eaca6 --- /dev/null +++ b/fusion_accounting_ai/services/data_adapters/assets.py @@ -0,0 +1,42 @@ +"""Assets data adapter.""" + +from .base import DataAdapter +from ._registry import register_adapter + + +class AssetsAdapter(DataAdapter): + FUSION_MODEL = 'fusion.asset' + ENTERPRISE_MODULE = 'account_asset' + + def list_assets(self, state=None): + return self._dispatch('list_assets', state=state) + + def list_assets_via_fusion(self, state=None): + return self._read_fusion('fusion.asset', state=state) + + def list_assets_via_enterprise(self, state=None): + return self._read_fusion('account.asset', state=state) + + def list_assets_via_community(self, state=None): + # No assets feature in pure Community — return empty list with a hint. + return [] + + def _read_fusion(self, model_name, state=None): + """Shared shape between fusion and enterprise (both use account.asset-like API).""" + Model = self.env[model_name].sudo() + domain = [] + if state: + domain.append(('state', '=', state)) + records = Model.search(domain, limit=200) + out = [] + for r in records: + out.append({ + 'id': r.id, + 'name': getattr(r, 'name', None), + 'state': getattr(r, 'state', None), + 'value': getattr(r, 'original_value', None) or getattr(r, 'acquisition_cost', None), + }) + return out + + +register_adapter('assets', AssetsAdapter) diff --git a/fusion_accounting_ai/services/data_adapters/followup.py b/fusion_accounting_ai/services/data_adapters/followup.py new file mode 100644 index 00000000..c6d98a76 --- /dev/null +++ b/fusion_accounting_ai/services/data_adapters/followup.py @@ -0,0 +1,47 @@ +"""Follow-up data adapter.""" + +from datetime import date, timedelta +from .base import DataAdapter +from ._registry import register_adapter + + +class FollowupAdapter(DataAdapter): + FUSION_MODEL = 'fusion.followup.line' + ENTERPRISE_MODULE = 'account_followup' + + def overdue_invoices(self, days_overdue=30, partner_id=None): + return self._dispatch('overdue_invoices', days_overdue=days_overdue, partner_id=partner_id) + + def overdue_invoices_via_fusion(self, days_overdue=30, partner_id=None): + return self.overdue_invoices_via_community(days_overdue=days_overdue, partner_id=partner_id) + + def overdue_invoices_via_enterprise(self, days_overdue=30, partner_id=None): + return self.overdue_invoices_via_community(days_overdue=days_overdue, partner_id=partner_id) + + def overdue_invoices_via_community(self, days_overdue=30, partner_id=None): + cutoff = date.today() - timedelta(days=days_overdue) + domain = [ + ('move_type', 'in', ('out_invoice', 'out_refund')), + ('state', '=', 'posted'), + ('payment_state', 'in', ('not_paid', 'partial')), + ('invoice_date_due', '<=', cutoff), + ] + if partner_id: + domain.append(('partner_id', '=', partner_id)) + moves = self.env['account.move'].sudo().search(domain, limit=200, order='invoice_date_due asc') + return [ + { + 'id': m.id, + 'name': m.name, + 'partner_id': m.partner_id.id, + 'partner_name': m.partner_id.name, + 'invoice_date_due': m.invoice_date_due, + 'amount_residual': m.amount_residual, + 'currency_id': m.currency_id.id, + 'days_overdue': (date.today() - m.invoice_date_due).days, + } + for m in moves + ] + + +register_adapter('followup', FollowupAdapter) diff --git a/fusion_accounting_ai/tests/test_data_adapters.py b/fusion_accounting_ai/tests/test_data_adapters.py index 4bff52aa..0d6de1a8 100644 --- a/fusion_accounting_ai/tests/test_data_adapters.py +++ b/fusion_accounting_ai/tests/test_data_adapters.py @@ -74,3 +74,19 @@ class TestReportsAdapter(TransactionCase): for row in result: self.assertIn('account_id', row) self.assertIn('balance', row) + + +@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) + + +@tagged('post_install', '-at_install') +class TestAssetsAdapter(TransactionCase): + def test_list_assets_returns_list(self): + adapter = get_adapter(self.env, 'assets') + rows = adapter.list_assets() + self.assertIsInstance(rows, list)