# Feature Gating System

## Overview

The feature gating system provides tier-based access control for platform features. It allows restricting functionality based on store subscription tiers (Essential, Professional, Business, Enterprise) with contextual upgrade prompts when features are locked.

**Implemented:** December 31, 2025

## Architecture

### Database Models

Located in `models/database/feature.py`:

| Model | Purpose |
|-------|---------|
| `Feature` | Feature definitions with tier requirements |
| `StoreFeatureOverride` | Per-store feature overrides (enable/disable) |

### Feature Model Structure

```python
class Feature(Base):
    __tablename__ = "features"

    id: int                    # Primary key
    code: str                  # Unique feature code (e.g., "analytics_dashboard")
    name: str                  # Display name
    description: str           # User-facing description
    category: str              # Feature category
    minimum_tier_code: str     # Minimum tier required (essential/professional/business/enterprise)
    minimum_tier_order: int    # Tier order for comparison (1-4)
    is_active: bool           # Whether feature is available
    created_at: datetime
    updated_at: datetime
```

### Tier Ordering

| Tier | Order | Code |
|------|-------|------|
| Essential | 1 | `essential` |
| Professional | 2 | `professional` |
| Business | 3 | `business` |
| Enterprise | 4 | `enterprise` |

## Feature Categories

30 features organized into 8 categories:

### 1. Analytics
| Feature Code | Name | Min Tier |
|-------------|------|----------|
| `basic_analytics` | Basic Analytics | Essential |
| `analytics_dashboard` | Analytics Dashboard | Professional |
| `advanced_analytics` | Advanced Analytics | Business |
| `custom_reports` | Custom Reports | Enterprise |

### 2. Product Management
| Feature Code | Name | Min Tier |
|-------------|------|----------|
| `basic_products` | Product Management | Essential |
| `bulk_product_edit` | Bulk Product Edit | Professional |
| `product_variants` | Product Variants | Professional |
| `product_bundles` | Product Bundles | Business |
| `inventory_alerts` | Inventory Alerts | Professional |

### 3. Order Management
| Feature Code | Name | Min Tier |
|-------------|------|----------|
| `basic_orders` | Order Management | Essential |
| `order_automation` | Order Automation | Professional |
| `advanced_fulfillment` | Advanced Fulfillment | Business |
| `multi_warehouse` | Multi-Warehouse | Enterprise |

### 4. Marketing
| Feature Code | Name | Min Tier |
|-------------|------|----------|
| `discount_codes` | Discount Codes | Professional |
| `abandoned_cart` | Abandoned Cart Recovery | Business |
| `email_marketing` | Email Marketing | Business |
| `loyalty_program` | Loyalty Program | Enterprise |

### 5. Support
| Feature Code | Name | Min Tier |
|-------------|------|----------|
| `basic_support` | Email Support | Essential |
| `priority_support` | Priority Support | Professional |
| `phone_support` | Phone Support | Business |
| `dedicated_manager` | Dedicated Account Manager | Enterprise |

### 6. Integration
| Feature Code | Name | Min Tier |
|-------------|------|----------|
| `basic_api` | Basic API Access | Professional |
| `advanced_api` | Advanced API Access | Business |
| `webhooks` | Webhooks | Business |
| `custom_integrations` | Custom Integrations | Enterprise |

### 7. Branding
| Feature Code | Name | Min Tier |
|-------------|------|----------|
| `basic_theme` | Theme Customization | Essential |
| `custom_domain` | Custom Domain | Professional |
| `white_label` | White Label | Enterprise |
| `custom_checkout` | Custom Checkout | Enterprise |

### 8. Team
| Feature Code | Name | Min Tier |
|-------------|------|----------|
| `team_management` | Team Management | Professional |
| `role_permissions` | Role Permissions | Business |
| `audit_logs` | Audit Logs | Business |

## Services

### FeatureService

Located in `app/services/feature_service.py`:

```python
class FeatureService:
    """Service for managing tier-based feature access."""

    # In-memory caching (refreshed every 5 minutes)
    _feature_cache: dict[str, Feature] = {}
    _cache_timestamp: datetime | None = None
    CACHE_TTL_SECONDS = 300

    def has_feature(self, db: Session, store_id: int, feature_code: str) -> bool:
        """Check if store has access to a feature."""

    def get_available_features(self, db: Session, store_id: int) -> list[str]:
        """Get list of feature codes available to store."""

    def get_all_features_with_status(self, db: Session, store_id: int) -> list[dict]:
        """Get all features with availability status for store."""

    def get_feature_info(self, db: Session, feature_code: str) -> dict | None:
        """Get full feature information including tier requirements."""
```

### UsageService

Located in `app/services/usage_service.py`:

```python
class UsageService:
    """Service for tracking and managing store usage against tier limits."""

    def get_usage_summary(self, db: Session, store_id: int) -> dict:
        """Get comprehensive usage summary with limits and upgrade info."""

    def check_limit(self, db: Session, store_id: int, limit_type: str) -> dict:
        """Check specific limit with detailed info."""

    def get_upgrade_info(self, db: Session, store_id: int) -> dict:
        """Get upgrade recommendations based on current usage."""
```

## Backend Enforcement

### Decorator Pattern

```python
from app.core.feature_gate import require_feature

@router.get("/analytics/advanced")
@require_feature("advanced_analytics")
async def get_advanced_analytics(
    db: Session = Depends(get_db),
    store_id: int = Depends(get_current_store_id)
):
    # Only accessible if store has advanced_analytics feature
    pass
```

### Dependency Pattern

```python
from app.core.feature_gate import RequireFeature

@router.get("/marketing/loyalty")
async def get_loyalty_program(
    db: Session = Depends(get_db),
    _: None = Depends(RequireFeature("loyalty_program"))
):
    # Only accessible if store has loyalty_program feature
    pass
```

### Exception Handling

When a feature is not available, `FeatureNotAvailableException` is raised:

```python
class FeatureNotAvailableException(Exception):
    def __init__(self, feature_code: str, required_tier: str):
        self.feature_code = feature_code
        self.required_tier = required_tier
        super().__init__(f"Feature '{feature_code}' requires {required_tier} tier")
```

HTTP Response (403):
```json
{
    "detail": "Feature 'advanced_analytics' requires Professional tier or higher",
    "feature_code": "advanced_analytics",
    "required_tier": "Professional",
    "upgrade_url": "/store/orion/billing"
}
```

## API Endpoints

### Store Features API

Base: `/api/v1/store/features`

| Endpoint | Method | Description |
|----------|--------|-------------|
| `/features/available` | GET | List available feature codes |
| `/features` | GET | All features with availability status |
| `/features/{code}` | GET | Single feature info |
| `/features/{code}/check` | GET | Quick availability check |

### Store Usage API

Base: `/api/v1/store/usage`

| Endpoint | Method | Description |
|----------|--------|-------------|
| `/usage` | GET | Full usage summary with limits |
| `/usage/check/{limit_type}` | GET | Check specific limit (orders/products/team_members) |
| `/usage/upgrade-info` | GET | Upgrade recommendations |

### Admin Features API

Base: `/api/v1/admin/features`

| Endpoint | Method | Description |
|----------|--------|-------------|
| `/features` | GET | List all features |
| `/features/{id}` | GET | Get feature details |
| `/features/{id}` | PUT | Update feature |
| `/features/{id}/toggle` | POST | Toggle feature active status |
| `/features/stores/{store_id}/overrides` | GET | Get store overrides |
| `/features/stores/{store_id}/overrides` | POST | Create override |

## Frontend Integration

### Alpine.js Feature Store

Located in `static/shared/js/feature-store.js`:

```javascript
// Usage in templates
$store.features.has('analytics_dashboard')  // Check feature
$store.features.loaded                       // Loading state
$store.features.getFeature('advanced_api')   // Get feature details
```

### Alpine.js Upgrade Store

Located in `static/shared/js/upgrade-prompts.js`:

```javascript
// Usage in templates
$store.upgrade.shouldShowLimitWarning('orders')
$store.upgrade.getUsageString('products')
$store.upgrade.hasUpgradeRecommendation
```

### Jinja2 Macros

Located in `app/templates/shared/macros/feature_gate.html`:

#### Feature Gate Container
```jinja2
{% from "shared/macros/feature_gate.html" import feature_gate %}

{% call feature_gate("analytics_dashboard") %}
    <div>Analytics content here - only visible if feature available</div>
{% endcall %}
```

#### Feature Locked Card
```jinja2
{% from "shared/macros/feature_gate.html" import feature_locked %}

{{ feature_locked("advanced_analytics", "Advanced Analytics", "Get deeper insights") }}
```

#### Upgrade Banner
```jinja2
{% from "shared/macros/feature_gate.html" import upgrade_banner %}

{{ upgrade_banner("custom_domain") }}
```

#### Usage Limit Warning
```jinja2
{% from "shared/macros/feature_gate.html" import limit_warning %}

{{ limit_warning("orders") }}  {# Shows warning when approaching limit #}
```

#### Usage Progress Bar
```jinja2
{% from "shared/macros/feature_gate.html" import usage_bar %}

{{ usage_bar("products", "Products") }}
```

#### Tier Badge
```jinja2
{% from "shared/macros/feature_gate.html" import tier_badge %}

{{ tier_badge() }}  {# Shows current tier as colored badge #}
```

## Store Dashboard Integration

The store dashboard (`/store/{code}/dashboard`) now includes:

1. **Tier Badge**: Shows current subscription tier in header
2. **Usage Bars**: Visual progress bars for orders, products, team members
3. **Upgrade Prompts**: Contextual upgrade recommendations when approaching limits
4. **Feature Gates**: Locked sections for premium features

## Admin Features Page

Located at `/admin/features`:

- View all 30 features in categorized table
- Toggle features on/off globally
- Filter by category
- Search by name/code
- View tier requirements

## Admin Tier Management UI

Located at `/admin/subscription-tiers`:

### Overview

The subscription tiers admin page provides full CRUD functionality for managing subscription tiers and their feature assignments.

### Features

1. **Stats Cards**: Display total tiers, active tiers, public tiers, and estimated MRR
2. **Tier Table**: Sortable list of all tiers with:
   - Display order
   - Code (colored badge by tier)
   - Name
   - Monthly/Annual pricing
   - Feature count
   - Status (Active/Private/Inactive)
   - Actions (Edit Features, Edit, Activate/Deactivate)

3. **Create/Edit Modal**: Form with all tier fields:
   - Code and Name
   - Monthly and Annual pricing (in cents)
   - Display order
   - Stripe IDs (optional)
   - Description
   - Active/Public toggles

4. **Feature Assignment Slide-over Panel**:
   - Opens when clicking the puzzle-piece icon
   - Shows all features grouped by category
   - Binary features: checkbox selection with Select all/Deselect all per category
   - Quantitative features: checkbox + numeric limit input for `limit_value`
   - Feature count in footer
   - Save to update tier's feature assignments via `TierFeatureLimitEntry[]`

### Files

| File | Purpose |
|------|---------|
| `app/templates/admin/subscription-tiers.html` | Page template |
| `static/admin/js/subscription-tiers.js` | Alpine.js component |
| `app/routes/admin_pages.py` | Route registration |

### API Endpoints Used

| Action | Method | Endpoint |
|--------|--------|----------|
| Load tiers | GET | `/api/v1/admin/subscriptions/tiers` |
| Load stats | GET | `/api/v1/admin/subscriptions/stats` |
| Create tier | POST | `/api/v1/admin/subscriptions/tiers` |
| Update tier | PATCH | `/api/v1/admin/subscriptions/tiers/{code}` |
| Delete tier | DELETE | `/api/v1/admin/subscriptions/tiers/{code}` |
| Load feature catalog | GET | `/api/v1/admin/subscriptions/features/catalog` |
| Get tier feature limits | GET | `/api/v1/admin/subscriptions/features/tiers/{code}/limits` |
| Update tier feature limits | PUT | `/api/v1/admin/subscriptions/features/tiers/{code}/limits` |

## Migration

The features are seeded via Alembic migration:

```
alembic/versions/n2c3d4e5f6a7_add_features_table.py
```

This creates:
- `features` table with 30 default features
- `store_feature_overrides` table for per-store exceptions

## Testing

Unit tests located in:
- `tests/unit/services/test_feature_service.py`
- `tests/unit/services/test_usage_service.py`

Run tests:
```bash
pytest tests/unit/services/test_feature_service.py -v
pytest tests/unit/services/test_usage_service.py -v
```

## Architecture Compliance

All JavaScript files follow architecture rules:
- JS-003: Alpine components use `store*` naming convention
- JS-005: Init guards prevent duplicate initialization
- JS-006: Async operations have try/catch error handling
- JS-008: API calls use `apiClient` (not raw `fetch()`)
- JS-009: Notifications use `Utils.showToast()`

## Related Documentation

- [Subscription Billing](subscription-system.md) - Core subscription system
- [Subscription Workflow Plan](subscription-workflow.md) - Implementation roadmap
