Fix plan permissions using correct billing feature codes

The /api/business/current/ endpoint was using legacy permission key names
instead of the actual feature codes from the billing catalog. This caused
tenants on paid plans to be incorrectly locked out of features.

- Updated current_business_view to use correct feature codes (e.g.,
  'can_use_plugins' instead of 'plugins', 'sms_enabled' instead of
  'sms_reminders')
- Updated test to mock billing subscription and has_feature correctly

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
poduck
2025-12-12 21:48:09 -05:00
parent b384d9912a
commit aa9d920612
2 changed files with 49 additions and 32 deletions

View File

@@ -163,23 +163,24 @@ def current_business_view(request):
subdomain = domain_parts[0]
# Get plan permissions from billing system entitlements
# Use the actual feature codes from the billing catalog
permissions = {
'sms_reminders': tenant.has_feature('sms_reminders'),
'webhooks': tenant.has_feature('webhooks'),
'sms_reminders': tenant.has_feature('sms_enabled'),
'webhooks': tenant.has_feature('integrations_enabled'),
'api_access': tenant.has_feature('api_access'),
'custom_domain': tenant.has_feature('custom_domain'),
'white_label': tenant.has_feature('white_label'),
'custom_oauth': tenant.has_feature('custom_oauth'),
'plugins': tenant.has_feature('plugins'),
'custom_oauth': tenant.has_feature('can_manage_oauth'),
'plugins': tenant.has_feature('can_use_plugins'),
'can_create_plugins': tenant.has_feature('can_create_plugins'),
'tasks': tenant.has_feature('tasks'),
'export_data': tenant.has_feature('export_data'),
'video_conferencing': tenant.has_feature('video_conferencing'),
'two_factor_auth': tenant.has_feature('two_factor_auth'),
'masked_calling': tenant.has_feature('masked_calling'),
'pos_system': tenant.has_feature('pos_system'),
'mobile_app': tenant.has_feature('mobile_app'),
'contracts': tenant.has_feature('contracts'),
'tasks': tenant.has_feature('can_use_tasks'),
'export_data': tenant.has_feature('can_export_data'),
'video_conferencing': tenant.has_feature('can_add_video_conferencing'),
'two_factor_auth': tenant.has_feature('team_permissions'),
'masked_calling': tenant.has_feature('masked_calling_enabled'),
'pos_system': tenant.has_feature('can_use_pos'),
'mobile_app': tenant.has_feature('mobile_app_access'),
'contracts': tenant.has_feature('can_use_contracts'),
'multi_location': tenant.has_feature('multi_location'),
}
@@ -219,7 +220,7 @@ def current_business_view(request):
'website_pages': {},
'customer_dashboard_content': [],
# Platform permissions
'can_manage_oauth_credentials': tenant.has_feature('custom_oauth'),
'can_manage_oauth_credentials': tenant.has_feature('can_manage_oauth'),
'payments_enabled': tenant.payment_mode != 'none',
# Plan permissions (what features are available based on subscription)
'plan_permissions': permissions,

View File

@@ -263,7 +263,6 @@ class TestCurrentBusinessView:
mock_tenant.id = 1
mock_tenant.name = 'Demo Business'
mock_tenant.schema_name = 'demo'
mock_tenant.subscription_tier = 'PROFESSIONAL'
mock_tenant.is_active = True
mock_tenant.created_on = Mock()
mock_tenant.created_on.isoformat.return_value = '2024-01-01T00:00:00'
@@ -277,26 +276,43 @@ class TestCurrentBusinessView:
mock_tenant.booking_return_url = ''
mock_tenant.service_selection_heading = 'Choose'
mock_tenant.service_selection_subheading = 'Select'
mock_tenant.can_use_sms_reminders = True
mock_tenant.can_use_webhooks = False
mock_tenant.can_api_access = False
mock_tenant.can_use_custom_domain = False
mock_tenant.can_white_label = False
mock_tenant.can_manage_oauth_credentials = False
mock_tenant.can_use_plugins = True
mock_tenant.can_create_plugins = False
mock_tenant.can_use_tasks = True
mock_tenant.can_export_data = False
mock_tenant.can_add_video_conferencing = False
mock_tenant.can_require_2fa = False
mock_tenant.can_use_masked_phone_numbers = False
mock_tenant.can_use_pos = False
mock_tenant.can_use_mobile_app = True
mock_tenant.can_use_contracts = False
mock_tenant.subscription_plan = None
mock_tenant.payment_mode = 'stripe'
mock_tenant.domains.filter.return_value.first.return_value = mock_domain
# Mock billing subscription (plan is "pro")
mock_plan = Mock()
mock_plan.code = 'pro'
mock_plan_version = Mock()
mock_plan_version.plan = mock_plan
mock_subscription = Mock()
mock_subscription.plan_version = mock_plan_version
mock_tenant.billing_subscription = mock_subscription
# Mock has_feature to return correct values for billing feature codes
def has_feature_impl(feature_code):
feature_map = {
'sms_enabled': True,
'integrations_enabled': False,
'api_access': False,
'custom_domain': False,
'white_label': False,
'can_manage_oauth': False,
'can_use_plugins': True,
'can_create_plugins': False,
'can_use_tasks': True,
'can_export_data': False,
'can_add_video_conferencing': False,
'team_permissions': False,
'masked_calling_enabled': False,
'can_use_pos': False,
'mobile_app_access': True,
'can_use_contracts': False,
'multi_location': False,
}
return feature_map.get(feature_code, False)
mock_tenant.has_feature = Mock(side_effect=has_feature_impl)
request.user = Mock()
request.user.tenant = mock_tenant
@@ -306,7 +322,7 @@ class TestCurrentBusinessView:
assert response.data['id'] == 1
assert response.data['name'] == 'Demo Business'
assert response.data['subdomain'] == 'demo'
assert response.data['tier'] == 'PROFESSIONAL'
assert response.data['plan'] == 'pro'
assert response.data['plan_permissions']['sms_reminders'] is True
assert response.data['plan_permissions']['plugins'] is True