730 lines
24 KiB
Python
730 lines
24 KiB
Python
"""
|
|
Centralized Subscription Plan Configuration
|
|
Owner: Tenant Service
|
|
Single source of truth for all subscription tiers, quotas, features, and limits
|
|
"""
|
|
|
|
from typing import Optional, Dict, Any, List
|
|
from enum import Enum
|
|
from decimal import Decimal
|
|
|
|
|
|
class SubscriptionTier(str, Enum):
|
|
"""Subscription tier enumeration"""
|
|
STARTER = "starter"
|
|
PROFESSIONAL = "professional"
|
|
ENTERPRISE = "enterprise"
|
|
|
|
|
|
class BillingCycle(str, Enum):
|
|
"""Billing cycle options"""
|
|
MONTHLY = "monthly"
|
|
YEARLY = "yearly"
|
|
|
|
|
|
# ============================================================================
|
|
# PRICING CONFIGURATION
|
|
# ============================================================================
|
|
|
|
class PlanPricing:
|
|
"""Pricing for each subscription tier"""
|
|
|
|
MONTHLY_PRICES = {
|
|
SubscriptionTier.STARTER: Decimal("49.00"),
|
|
SubscriptionTier.PROFESSIONAL: Decimal("149.00"),
|
|
SubscriptionTier.ENTERPRISE: Decimal("499.00"), # Base price, custom quotes available
|
|
}
|
|
|
|
YEARLY_PRICES = {
|
|
SubscriptionTier.STARTER: Decimal("490.00"), # ~17% discount (2 months free)
|
|
SubscriptionTier.PROFESSIONAL: Decimal("1490.00"), # ~17% discount
|
|
SubscriptionTier.ENTERPRISE: Decimal("4990.00"), # Base price, custom quotes available
|
|
}
|
|
|
|
@staticmethod
|
|
def get_price(tier: str, billing_cycle: str = "monthly") -> Decimal:
|
|
"""Get price for tier and billing cycle"""
|
|
tier_enum = SubscriptionTier(tier.lower())
|
|
if billing_cycle == "yearly":
|
|
return PlanPricing.YEARLY_PRICES[tier_enum]
|
|
return PlanPricing.MONTHLY_PRICES[tier_enum]
|
|
|
|
|
|
# ============================================================================
|
|
# QUOTA LIMITS CONFIGURATION
|
|
# ============================================================================
|
|
|
|
class QuotaLimits:
|
|
"""
|
|
Resource quotas and limits for each subscription tier
|
|
None = Unlimited
|
|
"""
|
|
|
|
# ===== Team & Organization Limits =====
|
|
MAX_USERS = {
|
|
SubscriptionTier.STARTER: 5,
|
|
SubscriptionTier.PROFESSIONAL: 20,
|
|
SubscriptionTier.ENTERPRISE: None, # Unlimited
|
|
}
|
|
|
|
MAX_LOCATIONS = {
|
|
SubscriptionTier.STARTER: 1,
|
|
SubscriptionTier.PROFESSIONAL: 3,
|
|
SubscriptionTier.ENTERPRISE: None, # Unlimited
|
|
}
|
|
|
|
# ===== Product & Inventory Limits =====
|
|
MAX_PRODUCTS = {
|
|
SubscriptionTier.STARTER: 50,
|
|
SubscriptionTier.PROFESSIONAL: 500,
|
|
SubscriptionTier.ENTERPRISE: None, # Unlimited
|
|
}
|
|
|
|
MAX_RECIPES = {
|
|
SubscriptionTier.STARTER: 25,
|
|
SubscriptionTier.PROFESSIONAL: 250,
|
|
SubscriptionTier.ENTERPRISE: None, # Unlimited
|
|
}
|
|
|
|
MAX_SUPPLIERS = {
|
|
SubscriptionTier.STARTER: 10,
|
|
SubscriptionTier.PROFESSIONAL: 100,
|
|
SubscriptionTier.ENTERPRISE: None, # Unlimited
|
|
}
|
|
|
|
# ===== ML & Analytics Quotas (Daily Limits) =====
|
|
TRAINING_JOBS_PER_DAY = {
|
|
SubscriptionTier.STARTER: 1,
|
|
SubscriptionTier.PROFESSIONAL: 5,
|
|
SubscriptionTier.ENTERPRISE: None, # Unlimited
|
|
}
|
|
|
|
FORECAST_GENERATION_PER_DAY = {
|
|
SubscriptionTier.STARTER: 10,
|
|
SubscriptionTier.PROFESSIONAL: 100,
|
|
SubscriptionTier.ENTERPRISE: None, # Unlimited
|
|
}
|
|
|
|
# ===== Data Limits =====
|
|
DATASET_SIZE_ROWS = {
|
|
SubscriptionTier.STARTER: 1000,
|
|
SubscriptionTier.PROFESSIONAL: 10000,
|
|
SubscriptionTier.ENTERPRISE: None, # Unlimited
|
|
}
|
|
|
|
FORECAST_HORIZON_DAYS = {
|
|
SubscriptionTier.STARTER: 7,
|
|
SubscriptionTier.PROFESSIONAL: 90,
|
|
SubscriptionTier.ENTERPRISE: 365,
|
|
}
|
|
|
|
HISTORICAL_DATA_ACCESS_DAYS = {
|
|
SubscriptionTier.STARTER: 30, # 1 month
|
|
SubscriptionTier.PROFESSIONAL: 365, # 1 year
|
|
SubscriptionTier.ENTERPRISE: None, # Unlimited
|
|
}
|
|
|
|
# ===== Import/Export Limits =====
|
|
BULK_IMPORT_ROWS = {
|
|
SubscriptionTier.STARTER: 100,
|
|
SubscriptionTier.PROFESSIONAL: 1000,
|
|
SubscriptionTier.ENTERPRISE: 10000,
|
|
}
|
|
|
|
BULK_EXPORT_ROWS = {
|
|
SubscriptionTier.STARTER: 1000,
|
|
SubscriptionTier.PROFESSIONAL: 10000,
|
|
SubscriptionTier.ENTERPRISE: None, # Unlimited
|
|
}
|
|
|
|
# ===== Integration Limits =====
|
|
POS_SYNC_INTERVAL_MINUTES = {
|
|
SubscriptionTier.STARTER: 60, # Hourly
|
|
SubscriptionTier.PROFESSIONAL: 15, # Every 15 minutes
|
|
SubscriptionTier.ENTERPRISE: 5, # Every 5 minutes (near real-time)
|
|
}
|
|
|
|
API_CALLS_PER_HOUR = {
|
|
SubscriptionTier.STARTER: 100,
|
|
SubscriptionTier.PROFESSIONAL: 1000,
|
|
SubscriptionTier.ENTERPRISE: 10000,
|
|
}
|
|
|
|
WEBHOOK_ENDPOINTS = {
|
|
SubscriptionTier.STARTER: 2,
|
|
SubscriptionTier.PROFESSIONAL: 10,
|
|
SubscriptionTier.ENTERPRISE: None, # Unlimited
|
|
}
|
|
|
|
# ===== Storage Limits =====
|
|
FILE_STORAGE_GB = {
|
|
SubscriptionTier.STARTER: 1,
|
|
SubscriptionTier.PROFESSIONAL: 10,
|
|
SubscriptionTier.ENTERPRISE: 100,
|
|
}
|
|
|
|
REPORT_RETENTION_DAYS = {
|
|
SubscriptionTier.STARTER: 30,
|
|
SubscriptionTier.PROFESSIONAL: 180,
|
|
SubscriptionTier.ENTERPRISE: 365,
|
|
}
|
|
|
|
@staticmethod
|
|
def get_limit(quota_type: str, tier: str) -> Optional[int]:
|
|
"""
|
|
Get quota limit for a specific type and tier
|
|
|
|
Args:
|
|
quota_type: Quota type (e.g., 'MAX_USERS')
|
|
tier: Subscription tier
|
|
|
|
Returns:
|
|
Optional[int]: Limit value or None for unlimited
|
|
"""
|
|
tier_enum = SubscriptionTier(tier.lower())
|
|
|
|
quota_map = {
|
|
'MAX_USERS': QuotaLimits.MAX_USERS,
|
|
'MAX_LOCATIONS': QuotaLimits.MAX_LOCATIONS,
|
|
'MAX_PRODUCTS': QuotaLimits.MAX_PRODUCTS,
|
|
'MAX_RECIPES': QuotaLimits.MAX_RECIPES,
|
|
'MAX_SUPPLIERS': QuotaLimits.MAX_SUPPLIERS,
|
|
'TRAINING_JOBS_PER_DAY': QuotaLimits.TRAINING_JOBS_PER_DAY,
|
|
'FORECAST_GENERATION_PER_DAY': QuotaLimits.FORECAST_GENERATION_PER_DAY,
|
|
'DATASET_SIZE_ROWS': QuotaLimits.DATASET_SIZE_ROWS,
|
|
'FORECAST_HORIZON_DAYS': QuotaLimits.FORECAST_HORIZON_DAYS,
|
|
'HISTORICAL_DATA_ACCESS_DAYS': QuotaLimits.HISTORICAL_DATA_ACCESS_DAYS,
|
|
'BULK_IMPORT_ROWS': QuotaLimits.BULK_IMPORT_ROWS,
|
|
'BULK_EXPORT_ROWS': QuotaLimits.BULK_EXPORT_ROWS,
|
|
'POS_SYNC_INTERVAL_MINUTES': QuotaLimits.POS_SYNC_INTERVAL_MINUTES,
|
|
'API_CALLS_PER_HOUR': QuotaLimits.API_CALLS_PER_HOUR,
|
|
'WEBHOOK_ENDPOINTS': QuotaLimits.WEBHOOK_ENDPOINTS,
|
|
'FILE_STORAGE_GB': QuotaLimits.FILE_STORAGE_GB,
|
|
'REPORT_RETENTION_DAYS': QuotaLimits.REPORT_RETENTION_DAYS,
|
|
}
|
|
|
|
quotas = quota_map.get(quota_type, {})
|
|
return quotas.get(tier_enum)
|
|
|
|
|
|
# ============================================================================
|
|
# FEATURE ACCESS CONFIGURATION
|
|
# ============================================================================
|
|
|
|
class PlanFeatures:
|
|
"""
|
|
Feature availability by subscription tier
|
|
Each tier includes all features from lower tiers
|
|
"""
|
|
|
|
# ===== Core Features (All Tiers) =====
|
|
CORE_FEATURES = [
|
|
'inventory_management',
|
|
'sales_tracking',
|
|
'basic_recipes',
|
|
'production_planning',
|
|
'basic_reporting',
|
|
'mobile_app_access',
|
|
'email_support',
|
|
'easy_step_by_step_onboarding', # NEW: Value-add onboarding
|
|
]
|
|
|
|
# ===== Starter Tier Features =====
|
|
STARTER_FEATURES = CORE_FEATURES + [
|
|
'basic_forecasting',
|
|
'demand_prediction',
|
|
'waste_tracking',
|
|
'order_management',
|
|
'customer_management',
|
|
'supplier_management',
|
|
'batch_tracking',
|
|
'expiry_alerts',
|
|
]
|
|
|
|
# ===== Professional Tier Features =====
|
|
PROFESSIONAL_FEATURES = STARTER_FEATURES + [
|
|
# Advanced Analytics
|
|
'advanced_analytics',
|
|
'custom_reports',
|
|
'sales_analytics',
|
|
'supplier_performance',
|
|
'waste_analysis',
|
|
'profitability_analysis',
|
|
|
|
# External Data Integration
|
|
'weather_data_integration',
|
|
'traffic_data_integration',
|
|
|
|
# Multi-location
|
|
'multi_location_support',
|
|
'location_comparison',
|
|
'inventory_transfer',
|
|
|
|
# Advanced Forecasting
|
|
'batch_scaling',
|
|
'recipe_feasibility_check',
|
|
'seasonal_patterns',
|
|
'longer_forecast_horizon',
|
|
|
|
# Scenario Analysis (Professional+)
|
|
'scenario_modeling',
|
|
'what_if_analysis',
|
|
'risk_assessment',
|
|
|
|
# Integration
|
|
'pos_integration',
|
|
'accounting_export',
|
|
'basic_api_access',
|
|
|
|
# Support
|
|
'priority_email_support',
|
|
'phone_support',
|
|
]
|
|
|
|
# ===== Enterprise Tier Features =====
|
|
ENTERPRISE_FEATURES = PROFESSIONAL_FEATURES + [
|
|
# Advanced ML & AI
|
|
'advanced_ml_parameters',
|
|
'model_artifacts_access',
|
|
'custom_algorithms',
|
|
|
|
# Advanced Integration
|
|
'full_api_access',
|
|
'unlimited_webhooks',
|
|
'erp_integration',
|
|
'custom_integrations',
|
|
|
|
# Enterprise Features
|
|
'multi_tenant_management',
|
|
'white_label_option',
|
|
'custom_branding',
|
|
'sso_saml',
|
|
'advanced_permissions',
|
|
'audit_logs_export',
|
|
'compliance_reports',
|
|
|
|
# Advanced Analytics
|
|
'benchmarking',
|
|
'competitive_analysis',
|
|
'market_insights',
|
|
'predictive_maintenance',
|
|
|
|
# Premium Support
|
|
'dedicated_account_manager',
|
|
'priority_support',
|
|
'24_7_support',
|
|
'custom_training',
|
|
'onsite_support', # Optional add-on
|
|
]
|
|
|
|
@staticmethod
|
|
def get_features(tier: str) -> List[str]:
|
|
"""Get all features for a tier"""
|
|
tier_enum = SubscriptionTier(tier.lower())
|
|
|
|
feature_map = {
|
|
SubscriptionTier.STARTER: PlanFeatures.STARTER_FEATURES,
|
|
SubscriptionTier.PROFESSIONAL: PlanFeatures.PROFESSIONAL_FEATURES,
|
|
SubscriptionTier.ENTERPRISE: PlanFeatures.ENTERPRISE_FEATURES,
|
|
}
|
|
|
|
return feature_map.get(tier_enum, PlanFeatures.CORE_FEATURES)
|
|
|
|
@staticmethod
|
|
def has_feature(tier: str, feature: str) -> bool:
|
|
"""Check if a tier has access to a feature"""
|
|
features = PlanFeatures.get_features(tier)
|
|
return feature in features
|
|
|
|
@staticmethod
|
|
def requires_professional_tier(feature: str) -> bool:
|
|
"""Check if feature requires Professional+ tier"""
|
|
return (
|
|
feature not in PlanFeatures.STARTER_FEATURES and
|
|
feature in PlanFeatures.PROFESSIONAL_FEATURES
|
|
)
|
|
|
|
@staticmethod
|
|
def requires_enterprise_tier(feature: str) -> bool:
|
|
"""Check if feature requires Enterprise tier"""
|
|
return (
|
|
feature not in PlanFeatures.PROFESSIONAL_FEATURES and
|
|
feature in PlanFeatures.ENTERPRISE_FEATURES
|
|
)
|
|
|
|
|
|
# ============================================================================
|
|
# FEATURE DISPLAY CONFIGURATION (User-Facing)
|
|
# ============================================================================
|
|
|
|
class FeatureCategories:
|
|
"""User-friendly feature categorization for pricing display"""
|
|
|
|
CATEGORIES = {
|
|
"daily_operations": {
|
|
"icon": "🏪",
|
|
"translation_key": "categories.daily_operations",
|
|
},
|
|
"smart_forecasting": {
|
|
"icon": "🤖",
|
|
"translation_key": "categories.smart_forecasting",
|
|
},
|
|
"smart_ordering": {
|
|
"icon": "📦",
|
|
"translation_key": "categories.smart_ordering",
|
|
},
|
|
"business_insights": {
|
|
"icon": "📊",
|
|
"translation_key": "categories.business_insights",
|
|
},
|
|
"multi_location": {
|
|
"icon": "🏢",
|
|
"translation_key": "categories.multi_location",
|
|
},
|
|
"integrations": {
|
|
"icon": "🔌",
|
|
"translation_key": "categories.integrations",
|
|
},
|
|
"support": {
|
|
"icon": "👥",
|
|
"translation_key": "categories.support",
|
|
},
|
|
}
|
|
|
|
|
|
class UserFacingFeatures:
|
|
"""User-friendly feature descriptions for non-technical bakery owners"""
|
|
|
|
FEATURE_DISPLAY = {
|
|
# Daily Operations
|
|
"inventory_management": {
|
|
"translation_key": "features.inventory_management",
|
|
"tooltip_key": "features.inventory_management_tooltip",
|
|
"category": "daily_operations",
|
|
},
|
|
"sales_tracking": {
|
|
"translation_key": "features.sales_tracking",
|
|
"tooltip_key": "features.sales_tracking_tooltip",
|
|
"category": "daily_operations",
|
|
},
|
|
"basic_recipes": {
|
|
"translation_key": "features.basic_recipes",
|
|
"tooltip_key": "features.basic_recipes_tooltip",
|
|
"category": "daily_operations",
|
|
},
|
|
"production_planning": {
|
|
"translation_key": "features.production_planning",
|
|
"tooltip_key": "features.production_planning_tooltip",
|
|
"category": "daily_operations",
|
|
},
|
|
|
|
# Smart Forecasting
|
|
"basic_forecasting": {
|
|
"translation_key": "features.basic_forecasting",
|
|
"tooltip_key": "features.basic_forecasting_tooltip",
|
|
"category": "smart_forecasting",
|
|
},
|
|
"demand_prediction": {
|
|
"translation_key": "features.demand_prediction",
|
|
"category": "smart_forecasting",
|
|
},
|
|
"seasonal_patterns": {
|
|
"translation_key": "features.seasonal_patterns",
|
|
"tooltip_key": "features.seasonal_patterns_tooltip",
|
|
"category": "smart_forecasting",
|
|
},
|
|
"weather_data_integration": {
|
|
"translation_key": "features.weather_data_integration",
|
|
"tooltip_key": "features.weather_data_integration_tooltip",
|
|
"category": "smart_forecasting",
|
|
},
|
|
"traffic_data_integration": {
|
|
"translation_key": "features.traffic_data_integration",
|
|
"tooltip_key": "features.traffic_data_integration_tooltip",
|
|
"category": "smart_forecasting",
|
|
},
|
|
|
|
# Smart Ordering
|
|
"supplier_management": {
|
|
"translation_key": "features.supplier_management",
|
|
"tooltip_key": "features.supplier_management_tooltip",
|
|
"category": "smart_ordering",
|
|
},
|
|
"waste_tracking": {
|
|
"translation_key": "features.waste_tracking",
|
|
"tooltip_key": "features.waste_tracking_tooltip",
|
|
"category": "smart_ordering",
|
|
},
|
|
"expiry_alerts": {
|
|
"translation_key": "features.expiry_alerts",
|
|
"tooltip_key": "features.expiry_alerts_tooltip",
|
|
"category": "smart_ordering",
|
|
},
|
|
|
|
# Business Insights
|
|
"basic_reporting": {
|
|
"translation_key": "features.basic_reporting",
|
|
"category": "business_insights",
|
|
},
|
|
"advanced_analytics": {
|
|
"translation_key": "features.advanced_analytics",
|
|
"tooltip_key": "features.advanced_analytics_tooltip",
|
|
"category": "business_insights",
|
|
},
|
|
"profitability_analysis": {
|
|
"translation_key": "features.profitability_analysis",
|
|
"category": "business_insights",
|
|
},
|
|
|
|
# Multi-Location
|
|
"multi_location_support": {
|
|
"translation_key": "features.multi_location_support",
|
|
"category": "multi_location",
|
|
},
|
|
"inventory_transfer": {
|
|
"translation_key": "features.inventory_transfer",
|
|
"category": "multi_location",
|
|
},
|
|
"location_comparison": {
|
|
"translation_key": "features.location_comparison",
|
|
"category": "multi_location",
|
|
},
|
|
|
|
# Integrations
|
|
"pos_integration": {
|
|
"translation_key": "features.pos_integration",
|
|
"tooltip_key": "features.pos_integration_tooltip",
|
|
"category": "integrations",
|
|
},
|
|
"accounting_export": {
|
|
"translation_key": "features.accounting_export",
|
|
"category": "integrations",
|
|
},
|
|
"full_api_access": {
|
|
"translation_key": "features.full_api_access",
|
|
"category": "integrations",
|
|
},
|
|
|
|
# Support
|
|
"email_support": {
|
|
"translation_key": "features.email_support",
|
|
"category": "support",
|
|
},
|
|
"phone_support": {
|
|
"translation_key": "features.phone_support",
|
|
"category": "support",
|
|
},
|
|
"dedicated_account_manager": {
|
|
"translation_key": "features.dedicated_account_manager",
|
|
"category": "support",
|
|
},
|
|
"24_7_support": {
|
|
"translation_key": "features.support_24_7",
|
|
"category": "support",
|
|
},
|
|
}
|
|
|
|
|
|
# ============================================================================
|
|
# SUBSCRIPTION PLAN METADATA
|
|
# ============================================================================
|
|
|
|
class SubscriptionPlanMetadata:
|
|
"""Complete metadata for each subscription plan"""
|
|
|
|
PLANS = {
|
|
SubscriptionTier.STARTER: {
|
|
"name": "Starter",
|
|
"description_key": "plans.starter.description",
|
|
"tagline_key": "plans.starter.tagline",
|
|
"popular": False,
|
|
"monthly_price": PlanPricing.MONTHLY_PRICES[SubscriptionTier.STARTER],
|
|
"yearly_price": PlanPricing.YEARLY_PRICES[SubscriptionTier.STARTER],
|
|
"trial_days": 14,
|
|
"features": PlanFeatures.STARTER_FEATURES,
|
|
|
|
# Hero features (displayed prominently)
|
|
"hero_features": [
|
|
"inventory_management",
|
|
"basic_forecasting",
|
|
"supplier_management",
|
|
"waste_tracking",
|
|
],
|
|
|
|
# ROI & Business Value
|
|
"roi_badge": {
|
|
"savings_min": 300,
|
|
"savings_max": 500,
|
|
"currency": "EUR",
|
|
"period": "month",
|
|
"translation_key": "plans.starter.roi_badge",
|
|
},
|
|
"business_metrics": {
|
|
"waste_reduction": "20-30%",
|
|
"time_saved_hours_week": "5-8",
|
|
"stockout_reduction": "85-95%",
|
|
},
|
|
|
|
"limits": {
|
|
"users": QuotaLimits.MAX_USERS[SubscriptionTier.STARTER],
|
|
"locations": QuotaLimits.MAX_LOCATIONS[SubscriptionTier.STARTER],
|
|
"products": QuotaLimits.MAX_PRODUCTS[SubscriptionTier.STARTER],
|
|
"forecasts_per_day": QuotaLimits.FORECAST_GENERATION_PER_DAY[SubscriptionTier.STARTER],
|
|
"forecast_horizon_days": QuotaLimits.FORECAST_HORIZON_DAYS[SubscriptionTier.STARTER],
|
|
},
|
|
"support_key": "plans.starter.support",
|
|
"recommended_for_key": "plans.starter.recommended_for",
|
|
},
|
|
SubscriptionTier.PROFESSIONAL: {
|
|
"name": "Professional",
|
|
"description_key": "plans.professional.description",
|
|
"tagline_key": "plans.professional.tagline",
|
|
"popular": True, # Most popular plan
|
|
"monthly_price": PlanPricing.MONTHLY_PRICES[SubscriptionTier.PROFESSIONAL],
|
|
"yearly_price": PlanPricing.YEARLY_PRICES[SubscriptionTier.PROFESSIONAL],
|
|
"trial_days": 14,
|
|
"features": PlanFeatures.PROFESSIONAL_FEATURES,
|
|
|
|
# Hero features (displayed prominently)
|
|
"hero_features": [
|
|
"weather_data_integration",
|
|
"multi_location_support",
|
|
"advanced_analytics",
|
|
"phone_support",
|
|
],
|
|
|
|
# ROI & Business Value
|
|
"roi_badge": {
|
|
"savings_min": 800,
|
|
"savings_max": 1200,
|
|
"currency": "EUR",
|
|
"period": "month",
|
|
"translation_key": "plans.professional.roi_badge",
|
|
},
|
|
"business_metrics": {
|
|
"waste_reduction": "30-40%",
|
|
"time_saved_hours_week": "11-17",
|
|
"procurement_cost_savings": "5-15%",
|
|
},
|
|
|
|
"limits": {
|
|
"users": QuotaLimits.MAX_USERS[SubscriptionTier.PROFESSIONAL],
|
|
"locations": QuotaLimits.MAX_LOCATIONS[SubscriptionTier.PROFESSIONAL],
|
|
"products": QuotaLimits.MAX_PRODUCTS[SubscriptionTier.PROFESSIONAL],
|
|
"forecasts_per_day": QuotaLimits.FORECAST_GENERATION_PER_DAY[SubscriptionTier.PROFESSIONAL],
|
|
"forecast_horizon_days": QuotaLimits.FORECAST_HORIZON_DAYS[SubscriptionTier.PROFESSIONAL],
|
|
},
|
|
"support_key": "plans.professional.support",
|
|
"recommended_for_key": "plans.professional.recommended_for",
|
|
},
|
|
SubscriptionTier.ENTERPRISE: {
|
|
"name": "Enterprise",
|
|
"description_key": "plans.enterprise.description",
|
|
"tagline_key": "plans.enterprise.tagline",
|
|
"popular": False,
|
|
"monthly_price": PlanPricing.MONTHLY_PRICES[SubscriptionTier.ENTERPRISE],
|
|
"yearly_price": PlanPricing.YEARLY_PRICES[SubscriptionTier.ENTERPRISE],
|
|
"trial_days": 30,
|
|
"features": PlanFeatures.ENTERPRISE_FEATURES,
|
|
|
|
# Hero features (displayed prominently)
|
|
"hero_features": [
|
|
"full_api_access",
|
|
"custom_algorithms",
|
|
"dedicated_account_manager",
|
|
"24_7_support",
|
|
],
|
|
|
|
# ROI & Business Value
|
|
"roi_badge": {
|
|
"translation_key": "plans.enterprise.roi_badge",
|
|
"custom": True,
|
|
},
|
|
"business_metrics": {
|
|
"waste_reduction": "Custom",
|
|
"time_saved_hours_week": "Custom",
|
|
"scale": "Unlimited",
|
|
},
|
|
|
|
"limits": {
|
|
"users": "Unlimited",
|
|
"locations": "Unlimited",
|
|
"products": "Unlimited",
|
|
"forecasts_per_day": "Unlimited",
|
|
"forecast_horizon_days": QuotaLimits.FORECAST_HORIZON_DAYS[SubscriptionTier.ENTERPRISE],
|
|
},
|
|
"support_key": "plans.enterprise.support",
|
|
"recommended_for_key": "plans.enterprise.recommended_for",
|
|
"custom_pricing": True,
|
|
"contact_sales": True,
|
|
},
|
|
}
|
|
|
|
@staticmethod
|
|
def get_plan_info(tier: str) -> Dict[str, Any]:
|
|
"""Get complete plan information"""
|
|
tier_enum = SubscriptionTier(tier.lower())
|
|
return SubscriptionPlanMetadata.PLANS.get(tier_enum, {})
|
|
|
|
@staticmethod
|
|
def get_all_plans() -> Dict[SubscriptionTier, Dict[str, Any]]:
|
|
"""Get information for all plans"""
|
|
return SubscriptionPlanMetadata.PLANS
|
|
|
|
|
|
# ============================================================================
|
|
# HELPER FUNCTIONS
|
|
# ============================================================================
|
|
|
|
def get_training_job_quota(tier: str) -> Optional[int]:
|
|
"""Get training job daily quota for tier"""
|
|
return QuotaLimits.get_limit('TRAINING_JOBS_PER_DAY', tier)
|
|
|
|
|
|
def get_forecast_quota(tier: str) -> Optional[int]:
|
|
"""Get forecast generation daily quota for tier"""
|
|
return QuotaLimits.get_limit('FORECAST_GENERATION_PER_DAY', tier)
|
|
|
|
|
|
def get_dataset_size_limit(tier: str) -> Optional[int]:
|
|
"""Get dataset size limit for tier"""
|
|
return QuotaLimits.get_limit('DATASET_SIZE_ROWS', tier)
|
|
|
|
|
|
def get_forecast_horizon_limit(tier: str) -> int:
|
|
"""Get forecast horizon limit for tier"""
|
|
return QuotaLimits.get_limit('FORECAST_HORIZON_DAYS', tier) or 7
|
|
|
|
|
|
def get_historical_data_limit(tier: str) -> Optional[int]:
|
|
"""Get historical data access limit for tier"""
|
|
return QuotaLimits.get_limit('HISTORICAL_DATA_ACCESS_DAYS', tier)
|
|
|
|
|
|
def can_access_feature(tier: str, feature: str) -> bool:
|
|
"""Check if tier can access a feature"""
|
|
return PlanFeatures.has_feature(tier, feature)
|
|
|
|
|
|
def get_tier_comparison() -> Dict[str, Any]:
|
|
"""
|
|
Get feature comparison across all tiers
|
|
Useful for pricing pages
|
|
"""
|
|
return {
|
|
"tiers": ["starter", "professional", "enterprise"],
|
|
"features": {
|
|
"core": PlanFeatures.CORE_FEATURES,
|
|
"starter_only": list(set(PlanFeatures.STARTER_FEATURES) - set(PlanFeatures.CORE_FEATURES)),
|
|
"professional_only": list(set(PlanFeatures.PROFESSIONAL_FEATURES) - set(PlanFeatures.STARTER_FEATURES)),
|
|
"enterprise_only": list(set(PlanFeatures.ENTERPRISE_FEATURES) - set(PlanFeatures.PROFESSIONAL_FEATURES)),
|
|
},
|
|
"pricing": {
|
|
tier.value: {
|
|
"monthly": float(PlanPricing.MONTHLY_PRICES[tier]),
|
|
"yearly": float(PlanPricing.YEARLY_PRICES[tier]),
|
|
}
|
|
for tier in SubscriptionTier
|
|
},
|
|
}
|