From 0754d0b101ae34f532fb2c52cbfec90f63976337 Mon Sep 17 00:00:00 2001 From: gsinghpal Date: Wed, 27 May 2026 02:59:17 -0400 Subject: [PATCH] feat(billing): subscription creation handler (sale.order is_subscription) Co-Authored-By: Claude Opus 4.7 (1M context) --- fusion_centralize_billing/models/service.py | 27 +++++++++++++++++++++ fusion_centralize_billing/tests/test_api.py | 16 ++++++++++++ 2 files changed, 43 insertions(+) diff --git a/fusion_centralize_billing/models/service.py b/fusion_centralize_billing/models/service.py index 40046759..85379032 100644 --- a/fusion_centralize_billing/models/service.py +++ b/fusion_centralize_billing/models/service.py @@ -93,3 +93,30 @@ class FusionBillingService(models.Model): '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]} + + def _api_create_subscription(self, payload): + """Create and confirm a subscription sale.order for an external customer. + + The product on each line must have recurring_invoice=True so that + Odoo recognises the order as a subscription with has_recurring_line and + action_confirm() reaches subscription_state='3_progress'. + """ + self.ensure_one() + link = self.env['fusion.billing.account.link'].search([ + ('service_id', '=', self.id), + ('external_id', '=', payload.get('external_customer_id')), + ], limit=1) + if not link: + return {'status': 'error', 'error': 'unknown customer'} + order_lines = [(0, 0, { + 'product_id': line['product_id'], + 'product_uom_qty': line.get('quantity', 1), + }) for line in payload.get('lines', [])] + sub = self.env['sale.order'].sudo().create({ + 'partner_id': link.partner_id.id, + 'plan_id': payload['plan_id'], + 'order_line': order_lines, + }) + sub.action_confirm() + return {'status': 'ok', 'subscription_id': sub.id, + 'subscription_state': sub.subscription_state} diff --git a/fusion_centralize_billing/tests/test_api.py b/fusion_centralize_billing/tests/test_api.py index 5ff65b2d..c7fbe724 100644 --- a/fusion_centralize_billing/tests/test_api.py +++ b/fusion_centralize_billing/tests/test_api.py @@ -45,3 +45,19 @@ class TestApiHandlers(TransactionCase): cat = self.service._api_catalog() codes = [c['plan_code'] for c in cat['charges']] self.assertIn('maps-business', codes) + + def test_api_create_subscription(self): + self.service._api_upsert_customer({'external_id': 'client-9', 'name': 'Globex'}) + product = self.env['product.product'].sudo().create( + {'name': 'Maps Business', 'type': 'service', 'recurring_invoice': True, + 'list_price': 249.0}) + res = self.service._api_create_subscription({ + 'external_customer_id': 'client-9', + 'plan_id': self.plan.id, + 'lines': [{'product_id': product.id, 'quantity': 1}], + }) + self.assertEqual(res['status'], 'ok') + sub = self.env['sale.order'].browse(res['subscription_id']) + self.assertTrue(sub.is_subscription) + self.assertEqual(sub.plan_id, self.plan) + self.assertEqual(sub.subscription_state, '3_progress')