""" Admin interface for auth models """ from django import forms from django.contrib import admin from django.contrib.auth.admin import UserAdmin as BaseUserAdmin from igny8_core.admin.base import AccountAdminMixin from .models import User, Account, Plan, Subscription, Site, Sector, SiteUserAccess, Industry, IndustrySector, SeedKeyword, PasswordResetToken class AccountAdminForm(forms.ModelForm): """Custom form for Account admin with dynamic payment method choices from PaymentMethodConfig""" class Meta: model = Account fields = '__all__' def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) from igny8_core.business.billing.models import PaymentMethodConfig, AccountPaymentMethod if self.instance and self.instance.pk: # Get country from billing_country, fallback to wildcard '*' for global country = self.instance.billing_country or '*' # Get enabled payment methods for this country OR global (*) available_methods = PaymentMethodConfig.objects.filter( country_code__in=[country, '*'], is_enabled=True ).order_by('country_code', 'sort_order').values_list('payment_method', 'display_name') if available_methods: # Build choices from PaymentMethodConfig choices = [] seen = set() for method_type, display_name in available_methods: if method_type not in seen: choices.append((method_type, display_name or method_type.replace('_', ' ').title())) seen.add(method_type) else: # Fallback to model choices if no configs choices = Account.PAYMENT_METHOD_CHOICES self.fields['payment_method'].widget = forms.Select(choices=choices) # Get current default from AccountPaymentMethod default_method = AccountPaymentMethod.objects.filter( account=self.instance, is_default=True, is_enabled=True ).first() if default_method: self.fields['payment_method'].initial = default_method.type self.fields['payment_method'].help_text = f'✓ Current: {default_method.display_name} ({default_method.get_type_display()})' else: self.fields['payment_method'].help_text = 'Select from available payment methods based on country' def save(self, commit=True): """When payment_method changes, update/create AccountPaymentMethod""" from igny8_core.business.billing.models import AccountPaymentMethod, PaymentMethodConfig instance = super().save(commit=False) if commit: instance.save() # Get selected payment method selected_type = self.cleaned_data.get('payment_method') if selected_type: # Get config for display name and instructions country = instance.billing_country or '*' config = PaymentMethodConfig.objects.filter( country_code__in=[country, '*'], payment_method=selected_type, is_enabled=True ).first() # Create or update AccountPaymentMethod account_method, created = AccountPaymentMethod.objects.get_or_create( account=instance, type=selected_type, defaults={ 'display_name': config.display_name if config else selected_type.replace('_', ' ').title(), 'is_default': True, 'is_enabled': True, 'instructions': config.instructions if config else '', 'country_code': instance.billing_country or '', } ) if not created: # Update existing and set as default account_method.is_default = True account_method.is_enabled = True if config: account_method.display_name = config.display_name account_method.instructions = config.instructions account_method.save() # Unset other methods as default AccountPaymentMethod.objects.filter( account=instance ).exclude(id=account_method.id).update(is_default=False) return instance @admin.register(Plan) class PlanAdmin(admin.ModelAdmin): """Plan admin - Global, no account filtering needed""" list_display = ['name', 'slug', 'price', 'billing_cycle', 'max_sites', 'max_users', 'max_keywords', 'max_content_words', 'included_credits', 'is_active'] list_filter = ['is_active', 'billing_cycle', 'is_internal'] search_fields = ['name', 'slug'] readonly_fields = ['created_at'] fieldsets = ( ('Plan Info', { 'fields': ('name', 'slug', 'price', 'billing_cycle', 'features', 'is_active', 'is_internal') }), ('Account Management Limits', { 'fields': ('max_users', 'max_sites', 'max_industries', 'max_author_profiles'), 'description': 'Persistent limits for account-level resources' }), ('Hard Limits (Persistent)', { 'fields': ('max_keywords', 'max_clusters'), 'description': 'Total allowed - never reset' }), ('Monthly Limits (Reset on Billing Cycle)', { 'fields': ('max_content_ideas', 'max_content_words', 'max_images_basic', 'max_images_premium', 'max_image_prompts'), 'description': 'Monthly allowances - reset at billing cycle' }), ('Billing & Credits', { 'fields': ('included_credits', 'extra_credit_price', 'allow_credit_topup', 'auto_credit_topup_threshold', 'auto_credit_topup_amount', 'credits_per_month') }), ('Stripe Integration', { 'fields': ('stripe_product_id', 'stripe_price_id') }), ) @admin.register(Account) class AccountAdmin(AccountAdminMixin, admin.ModelAdmin): form = AccountAdminForm list_display = ['name', 'slug', 'owner', 'plan', 'status', 'credits', 'created_at'] list_filter = ['status', 'plan'] search_fields = ['name', 'slug'] readonly_fields = ['created_at', 'updated_at'] def get_queryset(self, request): """Override to filter by account for non-superusers""" qs = super().get_queryset(request) if request.user.is_superuser or (hasattr(request.user, 'is_developer') and request.user.is_developer()): return qs # Owners can see their own accounts if hasattr(request.user, 'role') and request.user.role == 'owner': return qs.filter(owner=request.user) # Admins can see their account try: user_account = getattr(request.user, 'account', None) if user_account: return qs.filter(id=user_account.id) except (AttributeError, Exception): # If account access fails (e.g., column mismatch), return empty pass return qs.none() def has_delete_permission(self, request, obj=None): if obj and getattr(obj, 'slug', '') == 'aws-admin': return False return super().has_delete_permission(request, obj) @admin.register(Subscription) class SubscriptionAdmin(AccountAdminMixin, admin.ModelAdmin): list_display = ['account', 'status', 'current_period_start', 'current_period_end'] list_filter = ['status'] search_fields = ['account__name', 'stripe_subscription_id'] readonly_fields = ['created_at', 'updated_at'] @admin.register(PasswordResetToken) class PasswordResetTokenAdmin(admin.ModelAdmin): list_display = ['user', 'token', 'used', 'expires_at', 'created_at'] list_filter = ['used', 'expires_at', 'created_at'] search_fields = ['user__email', 'token'] readonly_fields = ['created_at', 'token'] def get_queryset(self, request): """Filter by account for non-superusers""" qs = super().get_queryset(request) if request.user.is_superuser or (hasattr(request.user, 'is_developer') and request.user.is_developer()): return qs user_account = getattr(request.user, 'account', None) if user_account: return qs.filter(user__account=user_account) return qs.none() class SectorInline(admin.TabularInline): """Inline admin for sectors within Site admin.""" model = Sector extra = 0 fields = ['industry_sector', 'name', 'slug', 'status', 'is_active', 'get_keywords_count', 'get_clusters_count'] readonly_fields = ['get_keywords_count', 'get_clusters_count'] def get_keywords_count(self, obj): if obj.pk: return getattr(obj, 'keywords_set', obj.keywords_set).count() return 0 get_keywords_count.short_description = 'Keywords' def get_clusters_count(self, obj): if obj.pk: return getattr(obj, 'clusters_set', obj.clusters_set).count() return 0 get_clusters_count.short_description = 'Clusters' @admin.register(Site) class SiteAdmin(AccountAdminMixin, admin.ModelAdmin): list_display = ['name', 'slug', 'account', 'industry', 'domain', 'status', 'is_active', 'get_api_key_status', 'get_sectors_count'] list_filter = ['status', 'is_active', 'account', 'industry', 'hosting_type'] search_fields = ['name', 'slug', 'domain', 'industry__name'] readonly_fields = ['created_at', 'updated_at', 'get_api_key_display'] inlines = [SectorInline] actions = ['generate_api_keys'] fieldsets = ( ('Site Info', { 'fields': ('name', 'slug', 'account', 'domain', 'description', 'industry', 'site_type', 'hosting_type', 'status', 'is_active') }), ('WordPress Integration', { 'fields': ('get_api_key_display',), 'description': 'WordPress integration API key. Use SiteIntegration model for full integration settings.' }), ('SEO Metadata', { 'fields': ('seo_metadata',), 'classes': ('collapse',) }), ('Timestamps', { 'fields': ('created_at', 'updated_at'), 'classes': ('collapse',) }), ) def get_api_key_display(self, obj): """Display API key with copy button""" if obj.wp_api_key: from django.utils.html import format_html return format_html( '
{}'
''
'