Backend: - Add HasQuota() permission factory for quota limits (resources, users, services, appointments, email templates, automated tasks) - Add HasFeaturePermission() factory for feature-based permissions (SMS, masked calling, custom domains, white label, plugins, webhooks, calendar sync, analytics) - Add has_feature() method to Tenant model for flexible permission checking - Add new tenant permission fields: can_create_plugins, can_use_webhooks, can_use_calendar_sync, can_export_data - Create Data Export API with CSV/JSON support for appointments, customers, resources, services - Create Analytics API with dashboard, appointments, revenue endpoints - Add calendar sync views and URL configuration Frontend: - Add usePlanFeatures hook for checking feature availability - Add UpgradePrompt components (inline, banner, overlay variants) - Add LockedSection wrapper and LockedButton for feature gating - Update settings pages with permission checks 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
400 lines
11 KiB
Markdown
400 lines
11 KiB
Markdown
# Analytics API Documentation
|
|
|
|
## Overview
|
|
|
|
The Analytics API provides detailed reporting and business insights for tenant businesses. All analytics endpoints are gated behind the `advanced_analytics` permission from the subscription plan.
|
|
|
|
## Permission Gating
|
|
|
|
All analytics endpoints require:
|
|
1. **Authentication**: User must be authenticated (`IsAuthenticated`)
|
|
2. **Feature Permission**: Tenant must have `advanced_analytics` permission enabled in their subscription plan
|
|
|
|
If a tenant doesn't have the `advanced_analytics` permission, they will receive a 403 Forbidden response:
|
|
|
|
```json
|
|
{
|
|
"detail": "Your current plan does not include Advanced Analytics. Please upgrade your subscription to access this feature."
|
|
}
|
|
```
|
|
|
|
## Endpoints
|
|
|
|
### Base URL
|
|
```
|
|
GET /api/analytics/
|
|
```
|
|
|
|
### 1. Dashboard Summary Statistics
|
|
|
|
**Endpoint:** `GET /api/analytics/analytics/dashboard/`
|
|
|
|
Returns high-level metrics for the tenant's dashboard including appointment counts, resource utilization, and peak times.
|
|
|
|
**Response Example:**
|
|
```json
|
|
{
|
|
"total_appointments_this_month": 42,
|
|
"total_appointments_all_time": 1250,
|
|
"active_resources_count": 5,
|
|
"active_services_count": 3,
|
|
"upcoming_appointments_count": 8,
|
|
"average_appointment_duration_minutes": 45.5,
|
|
"peak_booking_day": "Friday",
|
|
"peak_booking_hour": 14,
|
|
"period": {
|
|
"start_date": "2024-12-01T00:00:00Z",
|
|
"end_date": "2024-12-31T23:59:59Z"
|
|
}
|
|
}
|
|
```
|
|
|
|
**Metrics Explained:**
|
|
- `total_appointments_this_month`: Count of confirmed appointments in current calendar month
|
|
- `total_appointments_all_time`: Total count of all confirmed appointments ever
|
|
- `active_resources_count`: Number of unique resources with future appointments
|
|
- `active_services_count`: Number of unique services with future appointments
|
|
- `upcoming_appointments_count`: Appointments in the next 7 days
|
|
- `average_appointment_duration_minutes`: Average duration of all appointments (in minutes)
|
|
- `peak_booking_day`: Day of week with most appointments (Sunday-Saturday)
|
|
- `peak_booking_hour`: Hour of day (0-23) with most appointments
|
|
|
|
### 2. Appointment Analytics
|
|
|
|
**Endpoint:** `GET /api/analytics/analytics/appointments/`
|
|
|
|
Detailed appointment breakdown with trends and metrics.
|
|
|
|
**Query Parameters:**
|
|
- `days` (optional, default: 30): Number of days to analyze
|
|
- `status` (optional): Filter by status - 'confirmed', 'cancelled', 'no_show'
|
|
- `service_id` (optional): Filter by service ID
|
|
- `resource_id` (optional): Filter by resource ID
|
|
|
|
**Response Example:**
|
|
```json
|
|
{
|
|
"total": 285,
|
|
"by_status": {
|
|
"confirmed": 250,
|
|
"cancelled": 25,
|
|
"no_show": 10
|
|
},
|
|
"by_service": [
|
|
{
|
|
"service_id": 1,
|
|
"service_name": "Haircut",
|
|
"count": 150
|
|
},
|
|
{
|
|
"service_id": 2,
|
|
"service_name": "Color Treatment",
|
|
"count": 135
|
|
}
|
|
],
|
|
"by_resource": [
|
|
{
|
|
"resource_id": 1,
|
|
"resource_name": "Chair 1",
|
|
"count": 145
|
|
},
|
|
{
|
|
"resource_id": 2,
|
|
"resource_name": "Chair 2",
|
|
"count": 140
|
|
}
|
|
],
|
|
"daily_breakdown": [
|
|
{
|
|
"date": "2024-11-01",
|
|
"count": 8,
|
|
"status_breakdown": {
|
|
"confirmed": 7,
|
|
"cancelled": 1,
|
|
"no_show": 0
|
|
}
|
|
},
|
|
{
|
|
"date": "2024-11-02",
|
|
"count": 9,
|
|
"status_breakdown": {
|
|
"confirmed": 8,
|
|
"cancelled": 1,
|
|
"no_show": 0
|
|
}
|
|
}
|
|
],
|
|
"booking_trend_percent": 12.5,
|
|
"cancellation_rate_percent": 8.77,
|
|
"no_show_rate_percent": 3.51,
|
|
"period_days": 30
|
|
}
|
|
```
|
|
|
|
**Metrics Explained:**
|
|
- `total`: Total appointments in the period
|
|
- `by_status`: Count breakdown by appointment status
|
|
- `by_service`: Appointment count per service
|
|
- `by_resource`: Appointment count per resource
|
|
- `daily_breakdown`: Day-by-day breakdown with status details
|
|
- `booking_trend_percent`: Percentage change vs previous period (positive = growth)
|
|
- `cancellation_rate_percent`: Percentage of appointments cancelled
|
|
- `no_show_rate_percent`: Percentage of appointments where customer didn't show
|
|
- `period_days`: Number of days analyzed
|
|
|
|
**Usage Examples:**
|
|
|
|
```bash
|
|
# Get appointment analytics for last 7 days
|
|
curl "http://lvh.me:8000/api/analytics/analytics/appointments/?days=7"
|
|
|
|
# Get analytics for specific service
|
|
curl "http://lvh.me:8000/api/analytics/analytics/appointments/?service_id=1"
|
|
|
|
# Get only cancelled appointments in last 30 days
|
|
curl "http://lvh.me:8000/api/analytics/analytics/appointments/?status=cancelled"
|
|
```
|
|
|
|
### 3. Revenue Analytics
|
|
|
|
**Endpoint:** `GET /api/analytics/analytics/revenue/`
|
|
|
|
Revenue breakdown and payment analytics. **Requires both `advanced_analytics` AND `can_accept_payments` permissions.**
|
|
|
|
**Query Parameters:**
|
|
- `days` (optional, default: 30): Number of days to analyze
|
|
- `service_id` (optional): Filter by service ID
|
|
|
|
**Response Example:**
|
|
```json
|
|
{
|
|
"total_revenue_cents": 125000,
|
|
"transaction_count": 50,
|
|
"average_transaction_value_cents": 2500,
|
|
"by_service": [
|
|
{
|
|
"service_id": 1,
|
|
"service_name": "Haircut",
|
|
"revenue_cents": 75000,
|
|
"count": 30
|
|
},
|
|
{
|
|
"service_id": 2,
|
|
"service_name": "Color Treatment",
|
|
"revenue_cents": 50000,
|
|
"count": 20
|
|
}
|
|
],
|
|
"daily_breakdown": [
|
|
{
|
|
"date": "2024-11-01",
|
|
"revenue_cents": 3500,
|
|
"transaction_count": 7
|
|
},
|
|
{
|
|
"date": "2024-11-02",
|
|
"revenue_cents": 4200,
|
|
"transaction_count": 8
|
|
}
|
|
],
|
|
"period_days": 30
|
|
}
|
|
```
|
|
|
|
**Metrics Explained:**
|
|
- `total_revenue_cents`: Total revenue in cents (divide by 100 for dollars)
|
|
- `transaction_count`: Number of completed transactions
|
|
- `average_transaction_value_cents`: Average transaction value in cents
|
|
- `by_service`: Revenue breakdown by service
|
|
- `daily_breakdown`: Day-by-day revenue metrics
|
|
- `period_days`: Number of days analyzed
|
|
|
|
**Important Notes:**
|
|
- Amounts are in **cents** (multiply by 0.01 for dollars)
|
|
- Only includes completed/confirmed payments
|
|
- Requires tenant to have payment processing enabled
|
|
- Returns 403 if `can_accept_payments` is not enabled
|
|
|
|
## Permission Implementation
|
|
|
|
### How Feature Gating Works
|
|
|
|
The analytics endpoints use the `HasFeaturePermission` permission class:
|
|
|
|
```python
|
|
class AnalyticsViewSet(viewsets.ViewSet):
|
|
permission_classes = [IsAuthenticated, HasFeaturePermission('advanced_analytics')]
|
|
```
|
|
|
|
This permission class:
|
|
|
|
1. **Checks Authentication**: Ensures user is logged in
|
|
2. **Gets Tenant from Request**: Uses `request.tenant` (set by django-tenants middleware)
|
|
3. **Calls `tenant.has_feature('advanced_analytics')`**: Checks both:
|
|
- Direct boolean field on Tenant model (if exists)
|
|
- Subscription plan's `permissions` JSON field
|
|
4. **Raises 403 if Permission Not Found**: Returns error with upgrade message
|
|
|
|
### Adding Advanced Analytics to a Plan
|
|
|
|
To grant `advanced_analytics` permission to a subscription plan:
|
|
|
|
**Option 1: Django Admin**
|
|
```
|
|
1. Go to /admin/platform_admin/subscriptionplan/
|
|
2. Edit desired plan
|
|
3. In "Permissions" JSON field, add:
|
|
{
|
|
"advanced_analytics": true,
|
|
...other permissions...
|
|
}
|
|
4. Save
|
|
```
|
|
|
|
**Option 2: Django Management Command**
|
|
```bash
|
|
docker compose -f docker-compose.local.yml exec django python manage.py shell
|
|
|
|
# In the shell:
|
|
from platform_admin.models import SubscriptionPlan
|
|
plan = SubscriptionPlan.objects.get(name='Professional')
|
|
permissions = plan.permissions or {}
|
|
permissions['advanced_analytics'] = True
|
|
plan.permissions = permissions
|
|
plan.save()
|
|
```
|
|
|
|
**Option 3: Direct Tenant Field**
|
|
```bash
|
|
# If using direct field on Tenant:
|
|
from core.models import Tenant
|
|
tenant = Tenant.objects.get(schema_name='demo')
|
|
tenant.advanced_analytics = True # If field exists
|
|
tenant.save()
|
|
```
|
|
|
|
## Architecture
|
|
|
|
### File Structure
|
|
```
|
|
analytics/
|
|
├── __init__.py
|
|
├── apps.py # Django app configuration
|
|
├── views.py # AnalyticsViewSet with all endpoints
|
|
├── serializers.py # Read-only serializers for response validation
|
|
├── urls.py # URL routing
|
|
└── README.md # This file
|
|
```
|
|
|
|
### Key Classes
|
|
|
|
**AnalyticsViewSet** (`views.py`)
|
|
- Inherits from `viewsets.ViewSet` (read-only, no database models)
|
|
- Three action methods:
|
|
- `dashboard()` - Summary statistics
|
|
- `appointments()` - Detailed appointment analytics
|
|
- `revenue()` - Payment analytics (conditional)
|
|
- All methods return `Response` with calculated data
|
|
|
|
**Permission Chain**
|
|
```
|
|
Request → IsAuthenticated → HasFeaturePermission('advanced_analytics') → View
|
|
```
|
|
|
|
## Error Responses
|
|
|
|
### 401 Unauthorized
|
|
```json
|
|
{
|
|
"detail": "Authentication credentials were not provided."
|
|
}
|
|
```
|
|
|
|
### 403 Forbidden (Missing Permission)
|
|
```json
|
|
{
|
|
"detail": "Your current plan does not include Advanced Analytics. Please upgrade your subscription to access this feature."
|
|
}
|
|
```
|
|
|
|
### 403 Forbidden (Revenue Endpoint, Missing Payments Permission)
|
|
```json
|
|
{
|
|
"error": "Payment analytics not available",
|
|
"detail": "Your plan does not include payment processing."
|
|
}
|
|
```
|
|
|
|
## Testing
|
|
|
|
### Using cURL
|
|
|
|
```bash
|
|
# Get analytics with auth token
|
|
TOKEN="your_auth_token_here"
|
|
|
|
curl -H "Authorization: Token $TOKEN" \
|
|
"http://lvh.me:8000/api/analytics/analytics/dashboard/"
|
|
|
|
curl -H "Authorization: Token $TOKEN" \
|
|
"http://lvh.me:8000/api/analytics/analytics/appointments/?days=7"
|
|
|
|
curl -H "Authorization: Token $TOKEN" \
|
|
"http://lvh.me:8000/api/analytics/analytics/revenue/"
|
|
```
|
|
|
|
### Using Python Requests
|
|
|
|
```python
|
|
import requests
|
|
|
|
TOKEN = "your_auth_token_here"
|
|
headers = {"Authorization": f"Token {TOKEN}"}
|
|
|
|
# Dashboard
|
|
response = requests.get(
|
|
"http://lvh.me:8000/api/analytics/analytics/dashboard/",
|
|
headers=headers
|
|
)
|
|
print(response.json())
|
|
|
|
# Appointments with filter
|
|
response = requests.get(
|
|
"http://lvh.me:8000/api/analytics/analytics/appointments/",
|
|
headers=headers,
|
|
params={"days": 7, "service_id": 1}
|
|
)
|
|
print(response.json())
|
|
```
|
|
|
|
## Performance Considerations
|
|
|
|
- **Dashboard**: Performs multiple aggregate queries, suitable for ~10k+ appointments
|
|
- **Appointments**: Filters and iterates over appointments, may be slow with 100k+ records
|
|
- **Revenue**: Depends on payment transaction volume, usually fast
|
|
- **Caching**: Consider implementing Redis caching for frequently accessed analytics
|
|
|
|
## Future Enhancements
|
|
|
|
1. **Performance Optimization**
|
|
- Add database indexing on `start_time`, `status`, `created_at`
|
|
- Implement query result caching
|
|
- Use database aggregation instead of Python loops
|
|
|
|
2. **Additional Analytics**
|
|
- Customer demographics (repeat rate, lifetime value)
|
|
- Staff performance metrics (revenue per staff member)
|
|
- Channel attribution (how customers found you)
|
|
- Resource utilization rate (occupancy percentage)
|
|
|
|
3. **Export Features**
|
|
- CSV/Excel export
|
|
- PDF report generation
|
|
- Email report scheduling
|
|
|
|
4. **Advanced Filtering**
|
|
- Date range selection
|
|
- Multi-service filtering
|
|
- Resource utilization trends
|
|
- Seasonal analysis
|