diff --git a/backend/igny8_core/modules/billing/views.py b/backend/igny8_core/modules/billing/views.py index 0393342c..a8b31973 100644 --- a/backend/igny8_core/modules/billing/views.py +++ b/backend/igny8_core/modules/billing/views.py @@ -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 }, ])