""" Serializers for Billing Models """ from typing import Any, Dict, Optional from decimal import Decimal from rest_framework import serializers from .models import CreditTransaction, CreditUsageLog from igny8_core.auth.models import Account from igny8_core.business.billing.models import PaymentMethodConfig, Payment class CreditTransactionSerializer(serializers.ModelSerializer): """Serializer for credit transactions""" transaction_type_display: serializers.CharField = serializers.CharField( source='get_transaction_type_display', read_only=True ) class Meta: model = CreditTransaction fields = [ 'id', 'transaction_type', 'transaction_type_display', 'amount', 'balance_after', 'description', 'metadata', 'created_at' ] read_only_fields = ['created_at', 'account'] class CreditUsageLogSerializer(serializers.ModelSerializer): """Serializer for credit usage logs""" operation_type_display: serializers.CharField = serializers.CharField( source='get_operation_type_display', read_only=True ) class Meta: model = CreditUsageLog fields = [ 'id', 'operation_type', 'operation_type_display', 'credits_used', 'cost_usd', 'model_used', 'tokens_input', 'tokens_output', 'related_object_type', 'related_object_id', 'metadata', 'created_at' ] read_only_fields = ['created_at', 'account'] class CreditBalanceSerializer(serializers.Serializer): """Serializer for credit balance response""" credits: serializers.IntegerField = serializers.IntegerField() plan_credits_per_month: serializers.IntegerField = serializers.IntegerField() credits_used_this_month: serializers.IntegerField = serializers.IntegerField() credits_remaining: serializers.IntegerField = serializers.IntegerField() class UsageSummarySerializer(serializers.Serializer): """Serializer for usage summary response""" period: serializers.DictField = serializers.DictField() total_credits_used: serializers.IntegerField = serializers.IntegerField() total_cost_usd: serializers.DecimalField = serializers.DecimalField(max_digits=10, decimal_places=2) by_operation: serializers.DictField = serializers.DictField() by_model: serializers.DictField = serializers.DictField() class PaymentMethodConfigSerializer(serializers.ModelSerializer): """Serializer for payment method configuration""" payment_method_display: serializers.CharField = serializers.CharField( source='get_payment_method_display', read_only=True ) class Meta: model = PaymentMethodConfig fields = [ 'id', 'country_code', 'payment_method', 'payment_method_display', 'is_enabled', 'display_name', 'instructions', 'bank_name', 'account_number', 'swift_code', 'wallet_type', 'wallet_id', 'sort_order' ] read_only_fields = ['id'] class PaymentConfirmationSerializer(serializers.Serializer): """Serializer for manual payment confirmation""" invoice_id: serializers.IntegerField = serializers.IntegerField(required=True) payment_method: serializers.ChoiceField = serializers.ChoiceField( choices=['bank_transfer', 'local_wallet'], required=True ) manual_reference: serializers.CharField = serializers.CharField( required=True, max_length=255, help_text="Transaction reference number" ) manual_notes: serializers.CharField = serializers.CharField( required=False, allow_blank=True, help_text="Additional notes about the payment" ) amount: serializers.DecimalField = serializers.DecimalField( max_digits=10, decimal_places=2, required=True ) proof_url: serializers.URLField = serializers.URLField( required=False, allow_blank=True, help_text="URL to receipt/proof of payment" ) def validate_proof_url(self, value: Optional[str]) -> Optional[str]: """Validate proof_url is a valid URL format""" if value and not value.strip(): raise serializers.ValidationError("Proof URL cannot be empty if provided") if value: # Additional validation: must be http or https if not value.startswith(('http://', 'https://')): raise serializers.ValidationError("Proof URL must start with http:// or https://") return value def validate_amount(self, value: Optional[Decimal]) -> Decimal: """Validate amount has max 2 decimal places""" if value is None: raise serializers.ValidationError("Amount is required") if value <= 0: raise serializers.ValidationError("Amount must be greater than 0") # Check decimal precision (max 2 decimal places) if value.as_tuple().exponent < -2: raise serializers.ValidationError("Amount can have maximum 2 decimal places") return value class LimitCardSerializer(serializers.Serializer): """Serializer for individual limit card""" title: serializers.CharField = serializers.CharField() limit: serializers.IntegerField = serializers.IntegerField() used: serializers.IntegerField = serializers.IntegerField() available: serializers.IntegerField = serializers.IntegerField() unit: serializers.CharField = serializers.CharField() category: serializers.CharField = serializers.CharField() percentage: serializers.FloatField = serializers.FloatField() class UsageLimitsSerializer(serializers.Serializer): """Serializer for usage limits response""" limits: LimitCardSerializer = LimitCardSerializer(many=True) class AIModelConfigSerializer(serializers.Serializer): """ Serializer for AI Model Configuration (Read-Only API) Provides model information for frontend dropdowns and displays """ model_name = serializers.CharField(read_only=True) display_name = serializers.CharField(read_only=True) model_type = serializers.CharField(read_only=True) provider = serializers.CharField(read_only=True) # Text model fields input_cost_per_1m = serializers.DecimalField( max_digits=10, decimal_places=4, read_only=True, allow_null=True ) output_cost_per_1m = serializers.DecimalField( max_digits=10, decimal_places=4, read_only=True, allow_null=True ) context_window = serializers.IntegerField(read_only=True, allow_null=True) max_output_tokens = serializers.IntegerField(read_only=True, allow_null=True) # Image model fields cost_per_image = serializers.DecimalField( max_digits=10, decimal_places=4, read_only=True, allow_null=True ) valid_sizes = serializers.ListField(read_only=True, allow_null=True) # Capabilities supports_json_mode = serializers.BooleanField(read_only=True) supports_vision = serializers.BooleanField(read_only=True) supports_function_calling = serializers.BooleanField(read_only=True) # Status is_default = serializers.BooleanField(read_only=True) sort_order = serializers.IntegerField(read_only=True) # Computed field pricing_display = serializers.SerializerMethodField() def get_pricing_display(self, obj): """Generate pricing display string based on model type""" if obj.model_type == 'text': return f"${obj.input_cost_per_1m}/{obj.output_cost_per_1m} per 1M" elif obj.model_type == 'image': return f"${obj.cost_per_image} per image" return ""