Phase 0: Remove plan limit checks from billing views
- Updated limits endpoint to show only credits and account management limits - Removed all operation limit references (keywords, clusters, content ideas, word count, images) - Limits endpoint now focuses on credit usage by operation type - Account management limits (users, sites) still shown
This commit is contained in:
@@ -207,7 +207,10 @@ class CreditUsageViewSet(AccountModelViewSet):
|
||||
|
||||
@action(detail=False, methods=['get'], url_path='limits', url_name='limits')
|
||||
def limits(self, request):
|
||||
"""Get plan limits and current usage statistics"""
|
||||
"""
|
||||
Get account limits and credit usage statistics (Phase 0: Credit-only system).
|
||||
Returns account management limits and credit usage only.
|
||||
"""
|
||||
# Try multiple ways to get account
|
||||
account = getattr(request, 'account', None)
|
||||
|
||||
@@ -225,13 +228,7 @@ class CreditUsageViewSet(AccountModelViewSet):
|
||||
except (AttributeError, UserModel.DoesNotExist, Exception) as e:
|
||||
account = None
|
||||
|
||||
# Debug logging
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
logger.info(f'Limits endpoint - User: {getattr(request, "user", None)}, Account: {account}, Account has plan: {account.plan if account else False}')
|
||||
|
||||
if not account:
|
||||
logger.warning(f'No account found in limits endpoint')
|
||||
# Return empty limits instead of error - frontend will show "no data" message
|
||||
return success_response(data={'limits': []}, request=request)
|
||||
|
||||
@@ -241,115 +238,16 @@ class CreditUsageViewSet(AccountModelViewSet):
|
||||
return success_response(data={'limits': []}, request=request)
|
||||
|
||||
# Import models
|
||||
from igny8_core.modules.planner.models import Keywords, Clusters, ContentIdeas
|
||||
from igny8_core.modules.writer.models import Tasks, Images
|
||||
from igny8_core.auth.models import User, Site
|
||||
|
||||
# Get current month boundaries
|
||||
now = timezone.now()
|
||||
start_of_month = now.replace(day=1, hour=0, minute=0, second=0, microsecond=0)
|
||||
start_of_day = now.replace(hour=0, minute=0, second=0, microsecond=0)
|
||||
|
||||
# Calculate usage statistics
|
||||
limits_data = []
|
||||
|
||||
# Planner Limits
|
||||
keywords_count = Keywords.objects.filter(account=account).count()
|
||||
clusters_count = Clusters.objects.filter(account=account).count()
|
||||
content_ideas_count = ContentIdeas.objects.filter(account=account).count()
|
||||
clusters_today = Clusters.objects.filter(account=account, created_at__gte=start_of_day).count()
|
||||
|
||||
limits_data.extend([
|
||||
{
|
||||
'title': 'Keywords',
|
||||
'limit': plan.max_keywords or 0,
|
||||
'used': keywords_count,
|
||||
'available': max(0, (plan.max_keywords or 0) - keywords_count),
|
||||
'unit': 'keywords',
|
||||
'category': 'planner',
|
||||
'percentage': (keywords_count / (plan.max_keywords or 1)) * 100 if plan.max_keywords else 0
|
||||
},
|
||||
{
|
||||
'title': 'Clusters',
|
||||
'limit': plan.max_clusters or 0,
|
||||
'used': clusters_count,
|
||||
'available': max(0, (plan.max_clusters or 0) - clusters_count),
|
||||
'unit': 'clusters',
|
||||
'category': 'planner',
|
||||
'percentage': (clusters_count / (plan.max_clusters or 1)) * 100 if plan.max_clusters else 0
|
||||
},
|
||||
{
|
||||
'title': 'Content Ideas',
|
||||
'limit': plan.max_content_ideas or 0,
|
||||
'used': content_ideas_count,
|
||||
'available': max(0, (plan.max_content_ideas or 0) - content_ideas_count),
|
||||
'unit': 'ideas',
|
||||
'category': 'planner',
|
||||
'percentage': (content_ideas_count / (plan.max_content_ideas or 1)) * 100 if plan.max_content_ideas else 0
|
||||
},
|
||||
{
|
||||
'title': 'Daily Cluster Limit',
|
||||
'limit': plan.daily_cluster_limit or 0,
|
||||
'used': clusters_today,
|
||||
'available': max(0, (plan.daily_cluster_limit or 0) - clusters_today),
|
||||
'unit': 'per day',
|
||||
'category': 'planner',
|
||||
'percentage': (clusters_today / (plan.daily_cluster_limit or 1)) * 100 if plan.daily_cluster_limit else 0
|
||||
},
|
||||
])
|
||||
|
||||
# Writer Limits
|
||||
tasks_today = Tasks.objects.filter(account=account, created_at__gte=start_of_day).count()
|
||||
tasks_month = Tasks.objects.filter(account=account, created_at__gte=start_of_month)
|
||||
word_count_month = tasks_month.aggregate(total=Sum('word_count'))['total'] or 0
|
||||
|
||||
limits_data.extend([
|
||||
{
|
||||
'title': 'Monthly Word Count',
|
||||
'limit': plan.monthly_word_count_limit or 0,
|
||||
'used': word_count_month,
|
||||
'available': max(0, (plan.monthly_word_count_limit or 0) - word_count_month),
|
||||
'unit': 'words',
|
||||
'category': 'writer',
|
||||
'percentage': (word_count_month / (plan.monthly_word_count_limit or 1)) * 100 if plan.monthly_word_count_limit else 0
|
||||
},
|
||||
{
|
||||
'title': 'Daily Content Tasks',
|
||||
'limit': plan.daily_content_tasks or 0,
|
||||
'used': tasks_today,
|
||||
'available': max(0, (plan.daily_content_tasks or 0) - tasks_today),
|
||||
'unit': 'per day',
|
||||
'category': 'writer',
|
||||
'percentage': (tasks_today / (plan.daily_content_tasks or 1)) * 100 if plan.daily_content_tasks else 0
|
||||
},
|
||||
])
|
||||
|
||||
# Image Limits
|
||||
images_month = Images.objects.filter(account=account, created_at__gte=start_of_month).count()
|
||||
images_today = Images.objects.filter(account=account, created_at__gte=start_of_day).count()
|
||||
|
||||
limits_data.extend([
|
||||
{
|
||||
'title': 'Monthly Images',
|
||||
'limit': plan.monthly_image_count or 0,
|
||||
'used': images_month,
|
||||
'available': max(0, (plan.monthly_image_count or 0) - images_month),
|
||||
'unit': 'images',
|
||||
'category': 'images',
|
||||
'percentage': (images_month / (plan.monthly_image_count or 1)) * 100 if plan.monthly_image_count else 0
|
||||
},
|
||||
{
|
||||
'title': 'Daily Image Generation',
|
||||
'limit': plan.daily_image_generation_limit or 0,
|
||||
'used': images_today,
|
||||
'available': max(0, (plan.daily_image_generation_limit or 0) - images_today),
|
||||
'unit': 'per day',
|
||||
'category': 'images',
|
||||
'percentage': (images_today / (plan.daily_image_generation_limit or 1)) * 100 if plan.daily_image_generation_limit else 0
|
||||
},
|
||||
])
|
||||
|
||||
# AI Credits
|
||||
# Credit Usage (Phase 0: Credit-only system)
|
||||
credits_used_month = CreditUsageLog.objects.filter(
|
||||
account=account,
|
||||
created_at__gte=start_of_month
|
||||
@@ -358,64 +256,89 @@ class CreditUsageViewSet(AccountModelViewSet):
|
||||
# Get credits by operation type
|
||||
cluster_credits = CreditUsageLog.objects.filter(
|
||||
account=account,
|
||||
operation_type='clustering',
|
||||
operation_type__in=['clustering'],
|
||||
created_at__gte=start_of_month
|
||||
).aggregate(total=Sum('credits_used'))['total'] or 0
|
||||
|
||||
content_credits = CreditUsageLog.objects.filter(
|
||||
account=account,
|
||||
operation_type='content',
|
||||
operation_type__in=['content', 'content_generation'],
|
||||
created_at__gte=start_of_month
|
||||
).aggregate(total=Sum('credits_used'))['total'] or 0
|
||||
|
||||
image_credits = CreditUsageLog.objects.filter(
|
||||
account=account,
|
||||
operation_type='image',
|
||||
operation_type__in=['images', 'image_generation', 'image_prompt_extraction'],
|
||||
created_at__gte=start_of_month
|
||||
).aggregate(total=Sum('credits_used'))['total'] or 0
|
||||
|
||||
plan_credits = plan.monthly_ai_credit_limit or plan.credits_per_month or 0
|
||||
idea_credits = CreditUsageLog.objects.filter(
|
||||
account=account,
|
||||
operation_type__in=['ideas', 'idea_generation'],
|
||||
created_at__gte=start_of_month
|
||||
).aggregate(total=Sum('credits_used'))['total'] or 0
|
||||
|
||||
# Use included_credits from plan (Phase 0: Credit-only)
|
||||
plan_credits = plan.included_credits or plan.credits_per_month or 0
|
||||
|
||||
limits_data.extend([
|
||||
{
|
||||
'title': 'Monthly AI Credits',
|
||||
'title': 'Monthly Credits',
|
||||
'limit': plan_credits,
|
||||
'used': credits_used_month,
|
||||
'available': max(0, plan_credits - credits_used_month),
|
||||
'unit': 'credits',
|
||||
'category': 'ai',
|
||||
'category': 'credits',
|
||||
'percentage': (credits_used_month / plan_credits * 100) if plan_credits else 0
|
||||
},
|
||||
{
|
||||
'title': 'Content AI Credits',
|
||||
'limit': plan.monthly_content_ai_credits or 0,
|
||||
'used': content_credits,
|
||||
'available': max(0, (plan.monthly_content_ai_credits or 0) - content_credits),
|
||||
'title': 'Current Balance',
|
||||
'limit': None, # No limit - shows current balance
|
||||
'used': None,
|
||||
'available': account.credits,
|
||||
'unit': 'credits',
|
||||
'category': 'ai',
|
||||
'percentage': (content_credits / (plan.monthly_content_ai_credits or 1)) * 100 if plan.monthly_content_ai_credits else 0
|
||||
'category': 'credits',
|
||||
'percentage': None
|
||||
},
|
||||
{
|
||||
'title': 'Image AI Credits',
|
||||
'limit': plan.monthly_image_ai_credits or 0,
|
||||
'used': image_credits,
|
||||
'available': max(0, (plan.monthly_image_ai_credits or 0) - image_credits),
|
||||
'unit': 'credits',
|
||||
'category': 'ai',
|
||||
'percentage': (image_credits / (plan.monthly_image_ai_credits or 1)) * 100 if plan.monthly_image_ai_credits else 0
|
||||
},
|
||||
{
|
||||
'title': 'Cluster AI Credits',
|
||||
'limit': plan.monthly_cluster_ai_credits or 0,
|
||||
'title': 'Clustering Credits',
|
||||
'limit': None,
|
||||
'used': cluster_credits,
|
||||
'available': max(0, (plan.monthly_cluster_ai_credits or 0) - cluster_credits),
|
||||
'available': None,
|
||||
'unit': 'credits',
|
||||
'category': 'ai',
|
||||
'percentage': (cluster_credits / (plan.monthly_cluster_ai_credits or 1)) * 100 if plan.monthly_cluster_ai_credits else 0
|
||||
'category': 'credits',
|
||||
'percentage': None
|
||||
},
|
||||
{
|
||||
'title': 'Content Generation Credits',
|
||||
'limit': None,
|
||||
'used': content_credits,
|
||||
'available': None,
|
||||
'unit': 'credits',
|
||||
'category': 'credits',
|
||||
'percentage': None
|
||||
},
|
||||
{
|
||||
'title': 'Image Generation Credits',
|
||||
'limit': None,
|
||||
'used': image_credits,
|
||||
'available': None,
|
||||
'unit': 'credits',
|
||||
'category': 'credits',
|
||||
'percentage': None
|
||||
},
|
||||
{
|
||||
'title': 'Idea Generation Credits',
|
||||
'limit': None,
|
||||
'used': idea_credits,
|
||||
'available': None,
|
||||
'unit': 'credits',
|
||||
'category': 'credits',
|
||||
'percentage': None
|
||||
},
|
||||
])
|
||||
|
||||
# General Limits
|
||||
# Account Management Limits (kept - not operation limits)
|
||||
users_count = User.objects.filter(account=account).count()
|
||||
sites_count = Site.objects.filter(account=account).count()
|
||||
|
||||
@@ -426,7 +349,7 @@ class CreditUsageViewSet(AccountModelViewSet):
|
||||
'used': users_count,
|
||||
'available': max(0, (plan.max_users or 0) - users_count),
|
||||
'unit': 'users',
|
||||
'category': 'general',
|
||||
'category': 'account',
|
||||
'percentage': (users_count / (plan.max_users or 1)) * 100 if plan.max_users else 0
|
||||
},
|
||||
{
|
||||
@@ -435,7 +358,7 @@ class CreditUsageViewSet(AccountModelViewSet):
|
||||
'used': sites_count,
|
||||
'available': max(0, (plan.max_sites or 0) - sites_count),
|
||||
'unit': 'sites',
|
||||
'category': 'general',
|
||||
'category': 'account',
|
||||
'percentage': (sites_count / (plan.max_sites or 1)) * 100 if plan.max_sites else 0
|
||||
},
|
||||
])
|
||||
|
||||
Reference in New Issue
Block a user