feat(billing): _api_cancel_subscription service method + unit tests
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>
This commit is contained in:
@@ -247,3 +247,24 @@ class FusionBillingService(models.Model):
|
||||
sub.action_confirm()
|
||||
return {'status': 'ok', 'subscription_id': sub.id,
|
||||
'subscription_state': sub.subscription_state}
|
||||
|
||||
def _api_cancel_subscription(self, external_ref):
|
||||
"""Cancel (close) the subscription identified by ``external_ref``.
|
||||
|
||||
Authorization mirrors ``_api_record_usage``: the resolved sale.order must
|
||||
exist, be a subscription, and belong to a customer THIS service is linked
|
||||
to. Idempotent — closing an already-churned subscription returns ok.
|
||||
Validation (C3): an empty ref returns a 4xx-shaped error, never raises.
|
||||
"""
|
||||
self.ensure_one()
|
||||
if external_ref in (None, ''):
|
||||
return {'status': 'error', 'error': 'subscription id required'}
|
||||
sub = self._fc_resolve_subscription(external_ref)
|
||||
linked_partners = self.account_link_ids.mapped('partner_id')
|
||||
if not sub.exists() or not sub.is_subscription \
|
||||
or sub.partner_id not in linked_partners:
|
||||
return {'status': 'error', 'error': 'unknown subscription'}
|
||||
if sub.subscription_state != '6_churn':
|
||||
sub.set_close()
|
||||
return {'status': 'ok', 'subscription_id': sub.id,
|
||||
'subscription_state': sub.subscription_state}
|
||||
|
||||
Reference in New Issue
Block a user