diff --git a/frontend/src/pages/settings/QuotaSettings.tsx b/frontend/src/pages/settings/QuotaSettings.tsx
index 744ddef1..4198ca6f 100644
--- a/frontend/src/pages/settings/QuotaSettings.tsx
+++ b/frontend/src/pages/settings/QuotaSettings.tsx
@@ -9,7 +9,7 @@ import { useTranslation } from 'react-i18next';
import { useOutletContext, Link } from 'react-router-dom';
import {
AlertTriangle, Archive, Check, ChevronDown, ChevronUp,
- Clock, Download, Users, Briefcase, Calendar, RefreshCw
+ Clock, Download, Users, Briefcase, Calendar, RefreshCw, Bot
} from 'lucide-react';
import { Business, User, QuotaOverage } from '../../types';
import {
@@ -136,6 +136,10 @@ const QuotaSettings: React.FC = () => {
return ;
case 'MAX_SERVICES':
return ;
+ case 'MAX_AUTOMATION_RUNS':
+ return ;
+ case 'MAX_AUTOMATED_TASKS':
+ return ;
default:
return ;
}
diff --git a/smoothschedule/smoothschedule/identity/core/quota_service.py b/smoothschedule/smoothschedule/identity/core/quota_service.py
index 53e5da5e..193ff666 100644
--- a/smoothschedule/smoothschedule/identity/core/quota_service.py
+++ b/smoothschedule/smoothschedule/identity/core/quota_service.py
@@ -59,6 +59,12 @@ class QuotaService:
'display_name': 'automated tasks',
'count_method': 'count_automated_tasks',
},
+ 'MAX_AUTOMATION_RUNS': {
+ 'model': None, # No archivable model - informational only
+ 'display_name': 'automation runs this month',
+ 'count_method': 'count_automation_runs',
+ 'is_archivable': False, # Can't archive runs, just throttle
+ },
}
def __init__(self, tenant: Tenant):
@@ -94,6 +100,21 @@ class QuotaService:
from smoothschedule.scheduling.schedule.models import ScheduledTask
return ScheduledTask.objects.count()
+ def count_automation_runs(self) -> int:
+ """
+ Count automation flow executions this month.
+ Uses TenantDefaultFlow records to track usage.
+ """
+ from smoothschedule.integrations.activepieces.models import TenantDefaultFlow
+ from django.db.models import Sum
+
+ # Get total runs from all default flows
+ result = TenantDefaultFlow.objects.filter(
+ tenant=self.tenant
+ ).aggregate(total=Sum('runs_this_month'))
+
+ return result['total'] or 0
+
# =========================================================================
# Limit Retrieval
# =========================================================================
@@ -115,6 +136,7 @@ class QuotaService:
'MAX_RESOURCES': 'max_resources',
'MAX_SERVICES': 'max_services',
'MAX_AUTOMATED_TASKS': 'max_automated_tasks',
+ 'MAX_AUTOMATION_RUNS': 'max_automation_runs',
}
feature_code = feature_code_map.get(quota_type, quota_type.lower())
diff --git a/smoothschedule/smoothschedule/integrations/activepieces/migrations/0003_add_run_tracking_fields.py b/smoothschedule/smoothschedule/integrations/activepieces/migrations/0003_add_run_tracking_fields.py
new file mode 100644
index 00000000..bc5ffaec
--- /dev/null
+++ b/smoothschedule/smoothschedule/integrations/activepieces/migrations/0003_add_run_tracking_fields.py
@@ -0,0 +1,23 @@
+# Generated by Django 5.2.8 on 2025-12-22 06:50
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('activepieces', '0002_add_tenant_default_flow'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='tenantdefaultflow',
+ name='runs_month_started',
+ field=models.DateField(blank=True, help_text='When the current run count period started (resets monthly)', null=True),
+ ),
+ migrations.AddField(
+ model_name='tenantdefaultflow',
+ name='runs_this_month',
+ field=models.PositiveIntegerField(default=0, help_text='Number of times this flow has run this billing month'),
+ ),
+ ]
diff --git a/smoothschedule/smoothschedule/integrations/activepieces/models.py b/smoothschedule/smoothschedule/integrations/activepieces/models.py
index be3289be..0cf69c25 100644
--- a/smoothschedule/smoothschedule/integrations/activepieces/models.py
+++ b/smoothschedule/smoothschedule/integrations/activepieces/models.py
@@ -124,6 +124,16 @@ class TenantDefaultFlow(models.Model):
default=True,
help_text="Whether this flow is enabled in Activepieces",
)
+ # Usage tracking for quota management
+ runs_this_month = models.PositiveIntegerField(
+ default=0,
+ help_text="Number of times this flow has run this billing month",
+ )
+ runs_month_started = models.DateField(
+ null=True,
+ blank=True,
+ help_text="When the current run count period started (resets monthly)",
+ )
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
@@ -139,3 +149,22 @@ class TenantDefaultFlow(models.Model):
def __str__(self):
return f"{self.tenant.name} - {self.get_flow_type_display()}"
+
+ def increment_run_count(self):
+ """
+ Increment the run count for this flow.
+ Resets the counter if a new month has started.
+ """
+ from django.utils import timezone
+
+ today = timezone.now().date()
+ current_month_start = today.replace(day=1)
+
+ # Reset counter if new month
+ if self.runs_month_started is None or self.runs_month_started < current_month_start:
+ self.runs_this_month = 1
+ self.runs_month_started = current_month_start
+ else:
+ self.runs_this_month += 1
+
+ self.save(update_fields=['runs_this_month', 'runs_month_started'])