Plan 01 (NexaCloud cutover) Task 1: cancel/close a subscription with the same service-scoped authorization as _api_record_usage (resolve via _fc_resolve_subscription; partner must be linked to this service). Idempotent (no-op if already 6_churn). 5 unit tests, verified green on fcb_test (fresh + l10n_ca). DELETE route + HttpCase follow in Task 2. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
55 lines
2.5 KiB
Python
55 lines
2.5 KiB
Python
# -*- coding: utf-8 -*-
|
|
from odoo.tests.common import TransactionCase, tagged
|
|
|
|
|
|
@tagged('post_install', '-at_install')
|
|
class TestSubscriptionCancel(TransactionCase):
|
|
|
|
def _service(self, code, name):
|
|
Svc = self.env['fusion.billing.service'].sudo()
|
|
return Svc.search([('code', '=', code)], limit=1) or Svc.create(
|
|
{'name': name, 'code': code})
|
|
|
|
def setUp(self):
|
|
super().setUp()
|
|
self.plan = self.env['sale.subscription.plan'].sudo().create(
|
|
{'name': 'Monthly', 'billing_period_value': 1, 'billing_period_unit': 'month'})
|
|
self.product = self.env['product.product'].sudo().create(
|
|
{'name': 'NexaCloud Plan', 'type': 'service',
|
|
'recurring_invoice': True, 'list_price': 49.0})
|
|
self.svc_a = self._service('nexacloud', 'NexaCloud')
|
|
self.svc_b = self._service('other_app', 'Other App')
|
|
self.svc_a._api_upsert_customer({'external_id': 'user-1', 'name': 'Acme'})
|
|
res = self.svc_a._api_create_subscription({
|
|
'external_customer_id': 'user-1', 'plan_id': self.plan.id,
|
|
'lines': [{'product_id': self.product.id, 'quantity': 1}]})
|
|
self.sub = self.env['sale.order'].browse(res['subscription_id'])
|
|
|
|
def test_cancel_closes_subscription(self):
|
|
self.assertEqual(self.sub.subscription_state, '3_progress')
|
|
res = self.svc_a._api_cancel_subscription(str(self.sub.id))
|
|
self.assertEqual(res['status'], 'ok')
|
|
self.assertEqual(self.sub.subscription_state, '6_churn')
|
|
|
|
def test_cancel_is_idempotent(self):
|
|
self.svc_a._api_cancel_subscription(str(self.sub.id))
|
|
res = self.svc_a._api_cancel_subscription(str(self.sub.id))
|
|
self.assertEqual(res['status'], 'ok')
|
|
self.assertEqual(self.sub.subscription_state, '6_churn')
|
|
|
|
def test_cancel_unknown_subscription_rejected(self):
|
|
res = self.svc_a._api_cancel_subscription('999999999')
|
|
self.assertEqual(res['status'], 'error')
|
|
self.assertEqual(res['error'], 'unknown subscription')
|
|
|
|
def test_cancel_cross_service_rejected(self):
|
|
# svc_b is not linked to the customer that owns self.sub
|
|
res = self.svc_b._api_cancel_subscription(str(self.sub.id))
|
|
self.assertEqual(res['status'], 'error')
|
|
self.assertEqual(res['error'], 'unknown subscription')
|
|
self.assertEqual(self.sub.subscription_state, '3_progress')
|
|
|
|
def test_cancel_missing_id_rejected(self):
|
|
res = self.svc_a._api_cancel_subscription('')
|
|
self.assertEqual(res['status'], 'error')
|