from rest_framework import serializers from django.contrib.auth import get_user_model from .models import ( Account, Transaction, Budget, Expense, Profile, Deadline, Household, HouseholdMembership, PendingHouseholdInvite, FinancialYear, YearlyIncome, YearlyBudgetItem, ) User = get_user_model() class AccountSerializer(serializers.ModelSerializer): class Meta: model = Account exclude = ['user'] class TransactionSerializer(serializers.ModelSerializer): class Meta: model = Transaction fields = '__all__' def validate(self, data): request = self.context.get('request') if not request: return data user = request.user source = data.get('source_account') or (self.instance.source_account if self.instance else None) dest = data.get('destination_account') or (self.instance.destination_account if self.instance else None) if source and source.user != user: raise serializers.ValidationError('Source account does not belong to you.') if dest and dest.user != user: raise serializers.ValidationError('Destination account does not belong to you.') return data class BudgetSerializer(serializers.ModelSerializer): class Meta: model = Budget fields = '__all__' class ExpenseSerializer(serializers.ModelSerializer): class Meta: model = Expense fields = '__all__' class ProfileSerializer(serializers.ModelSerializer): totp_enabled = serializers.BooleanField(read_only=True) email = serializers.SerializerMethodField() def get_email(self, obj): return obj.email or (obj.user.email if obj.user else '') class Meta: model = Profile exclude = ['user', 'totp_secret', 'email_verify_token', 'email_verify_token_expires', 'password_reset_token_hash', 'password_reset_token_expires'] class DeadlineSerializer(serializers.ModelSerializer): class Meta: model = Deadline exclude = ['user'] class HouseholdMembershipSerializer(serializers.ModelSerializer): user_email = serializers.EmailField(source='user.email', read_only=True) invited_by_email = serializers.EmailField(source='invited_by.email', read_only=True) class Meta: model = HouseholdMembership fields = ['id', 'user', 'user_email', 'invited_by_email', 'status', 'role', 'effective_from_year', 'effective_until_year', 'created_at'] read_only_fields = ['id', 'user', 'user_email', 'invited_by_email', 'created_at'] class PendingHouseholdInviteSerializer(serializers.ModelSerializer): invited_by_email = serializers.EmailField(source='invited_by.email', read_only=True) class Meta: model = PendingHouseholdInvite fields = ['id', 'invited_email', 'invited_by_email', 'effective_from_year', 'created_at'] read_only_fields = fields class HouseholdSerializer(serializers.ModelSerializer): memberships = HouseholdMembershipSerializer(many=True, read_only=True) pending_invites = PendingHouseholdInviteSerializer(many=True, read_only=True) created_by_email = serializers.EmailField(source='created_by.email', read_only=True) class Meta: model = Household fields = ['id', 'name', 'created_by_email', 'memberships', 'pending_invites', 'created_at'] read_only_fields = ['id', 'created_by_email', 'memberships', 'pending_invites', 'created_at'] class YearlyIncomeSerializer(serializers.ModelSerializer): member_email = serializers.EmailField(source='member.email', read_only=True) class Meta: model = YearlyIncome fields = ['id', 'member', 'member_email', 'name', 'amount', 'active', 'notes'] read_only_fields = ['id', 'member_email'] class YearlyBudgetItemSerializer(serializers.ModelSerializer): class Meta: model = YearlyBudgetItem fields = ['id', 'name', 'amount', 'active', 'notes'] read_only_fields = ['id'] class FinancialYearSerializer(serializers.ModelSerializer): incomes = YearlyIncomeSerializer(many=True, read_only=True) budget_items = YearlyBudgetItemSerializer(many=True, read_only=True) total_income = serializers.SerializerMethodField() total_fixed_costs = serializers.SerializerMethodField() owner_type = serializers.SerializerMethodField() class Meta: model = FinancialYear fields = ['id', 'year', 'is_active', 'notes', 'owner_type', 'household_id', 'total_income', 'total_fixed_costs', 'incomes', 'budget_items', 'created_at'] read_only_fields = ['id', 'created_at', 'owner_type', 'household_id', 'total_income', 'total_fixed_costs'] def get_total_income(self, obj): return sum(i.amount for i in obj.incomes.filter(active=True)) def get_total_fixed_costs(self, obj): return sum(b.amount for b in obj.budget_items.filter(active=True)) def get_owner_type(self, obj): return 'household' if obj.household_id else 'personal' class RegisterSerializer(serializers.Serializer): email = serializers.EmailField() password = serializers.CharField(min_length=8, write_only=True) def validate_email(self, value): if User.objects.filter(email=value).exists(): raise serializers.ValidationError('Email already registered.') return value def create(self, validated_data): email = validated_data['email'] user = User.objects.create_user( username=email, email=email, password=validated_data['password'], ) from .models import PendingHouseholdInvite, HouseholdMembership for invite in PendingHouseholdInvite.objects.filter(invited_email__iexact=email): HouseholdMembership.objects.get_or_create( household=invite.household, user=user, defaults={ 'invited_by': invite.invited_by, 'status': 'pending', 'effective_from_year': invite.effective_from_year, }, ) invite.delete() return user