diff --git a/fusion_centralize_billing/models/service.py b/fusion_centralize_billing/models/service.py index 5dc83a1e..40046759 100644 --- a/fusion_centralize_billing/models/service.py +++ b/fusion_centralize_billing/models/service.py @@ -62,3 +62,34 @@ class FusionBillingService(models.Model): return self.browse() key_hash = hashlib.sha256(raw_key.encode()).hexdigest() return self.search([('api_key_hash', '=', key_hash), ('active', '=', True)], limit=1) + + def _api_upsert_customer(self, payload): + self.ensure_one() + ext = payload.get('external_id') + if not ext: + return {'status': 'error', 'error': 'external_id required'} + link = self.env['fusion.billing.account.link']._resolve_or_create_partner( + self, ext, name=payload.get('name'), email=payload.get('email')) + return {'status': 'ok', 'partner_id': link.partner_id.id, 'external_id': ext} + + def _api_record_usage(self, payload): + self.ensure_one() + events = payload.get('events') or [] + Usage = self.env['fusion.billing.usage'] + accepted = 0 + for ev in events: + sub = self.env['sale.order'].browse(int(ev['subscription_external_id'])) + Usage._record_usage( + sub, ev['metric_code'], float(ev['quantity']), + ev['period_start'], ev['period_end'], idem=ev.get('idempotency_key')) + accepted += 1 + return {'status': 'ok', 'accepted': accepted} + + def _api_catalog(self): + self.ensure_one() + charges = self.env['fusion.billing.charge'].search([('active', '=', True)]) + return {'status': 'ok', 'charges': [{ + 'plan_code': c.plan_code, 'metric': c.metric_id.code, + 'included_quota': c.included_quota, 'price_per_unit': c.price_per_unit, + 'unit_batch': c.unit_batch, 'charge_model': c.charge_model, + } for c in charges]} diff --git a/fusion_centralize_billing/tests/__init__.py b/fusion_centralize_billing/tests/__init__.py index 07117e0d..225b18dc 100644 --- a/fusion_centralize_billing/tests/__init__.py +++ b/fusion_centralize_billing/tests/__init__.py @@ -1,3 +1,4 @@ from . import test_identity from . import test_charge from . import test_usage +from . import test_api diff --git a/fusion_centralize_billing/tests/test_api.py b/fusion_centralize_billing/tests/test_api.py new file mode 100644 index 00000000..5ff65b2d --- /dev/null +++ b/fusion_centralize_billing/tests/test_api.py @@ -0,0 +1,47 @@ +# -*- coding: utf-8 -*- +from odoo.tests.common import TransactionCase, tagged + + +@tagged('post_install', '-at_install') +class TestApiHandlers(TransactionCase): + + def setUp(self): + super().setUp() + self.service = self.env['fusion.billing.service'].sudo().create( + {'name': 'NexaMaps', 'code': 'nexamaps'}) + self.env['fusion.billing.metric'].sudo().create( + {'name': 'API Calls', 'code': 'api_calls', 'aggregation': 'sum'}) + self.plan = self.env['sale.subscription.plan'].sudo().create( + {'name': 'Monthly', 'billing_period_value': 1, 'billing_period_unit': 'month'}) + + def test_api_upsert_customer(self): + res = self.service._api_upsert_customer( + {'external_id': 'client-9', 'name': 'Globex', 'email': 'billing@globex.test'}) + self.assertEqual(res['status'], 'ok') + link = self.env['fusion.billing.account.link'].search( + [('service_id', '=', self.service.id), ('external_id', '=', 'client-9')]) + self.assertEqual(link.partner_id.name, 'Globex') + + def test_api_record_usage_batch(self): + self.service._api_upsert_customer({'external_id': 'client-9', 'name': 'Globex'}) + partner = self.env['fusion.billing.account.link'].search( + [('external_id', '=', 'client-9')]).partner_id + sub = self.env['sale.order'].sudo().create( + {'partner_id': partner.id, 'is_subscription': True, 'plan_id': self.plan.id}) + res = self.service._api_record_usage({'events': [{ + 'subscription_external_id': str(sub.id), 'metric_code': 'api_calls', + 'quantity': 1234.0, 'period_start': '2026-05-01', 'period_end': '2026-06-01', + 'idempotency_key': 'maps:client-9:2026-05-01', + }]}) + self.assertEqual(res['accepted'], 1) + usage = self.env['fusion.billing.usage'].search([('subscription_id', '=', sub.id)]) + self.assertEqual(usage.quantity, 1234.0) + + def test_api_catalog_lists_active_charges(self): + self.env['fusion.billing.charge'].sudo().create({ + 'name': 'Maps overage', 'plan_code': 'maps-business', + 'metric_id': self.env['fusion.billing.metric'].search([('code', '=', 'api_calls')]).id, + 'included_quota': 5_000_000.0, 'price_per_unit': 0.10, 'unit_batch': 1000.0}) + cat = self.service._api_catalog() + codes = [c['plan_code'] for c in cat['charges']] + self.assertIn('maps-business', codes)