""" Repository for Customer model operations. """ from typing import Optional from django.db.models import Q, QuerySet from django.utils import timezone from backend.core.repositories.base import BaseRepository from backend.core.models import Customer class CustomerRepository(BaseRepository[Customer]): """ Repository for Customer model operations. """ model = Customer @classmethod def get_active(cls): """ Get all active customers. Returns: QuerySet of active customers """ return Customer.objects.filter( Q(end_date__isnull=True) | Q(end_date__gt=timezone.now().date()) ) @classmethod def get_inactive(cls): """ Get all inactive customers. Returns: QuerySet of inactive customers """ return Customer.objects.filter( end_date__isnull=False, end_date__lte=timezone.now().date() ) @classmethod def mark_inactive(cls, customer_id: str) -> Optional[Customer]: """ Mark a customer as inactive. Args: customer_id: The customer ID Returns: The updated customer or None if not found """ return cls.update(customer_id, {'end_date': timezone.now().date()}) @classmethod def search(cls, search_term: str, include_inactive: bool = False): """ Search customers by name or contact info. Args: search_term: The search term include_inactive: Whether to include inactive customers Returns: QuerySet of matching customers """ queryset = super().search( search_term, [ 'name', 'primary_contact_first_name', 'primary_contact_last_name', 'primary_contact_email', 'secondary_contact_first_name', 'secondary_contact_last_name', 'secondary_contact_email', 'billing_contact_first_name', 'billing_contact_last_name', 'billing_email' ] ) if not include_inactive: queryset = queryset.filter( Q(end_date__isnull=True) | Q(end_date__gt=timezone.now().date()) ) return queryset @classmethod def get_by_email(cls, email: str) -> Optional[Customer]: """ Get a customer by email. Args: email: The email address Returns: The customer or None if not found """ return Customer.objects.filter( Q(primary_contact_email=email) | Q(secondary_contact_email=email) | Q(billing_email=email) ).first() @classmethod def get_with_accounts(cls, customer_id: str) -> Optional[Customer]: """ Get a customer with prefetched accounts. Args: customer_id: The customer ID Returns: The customer with accounts or None if not found """ try: return Customer.objects.prefetch_related('accounts').get(pk=customer_id) except Customer.DoesNotExist: return None @classmethod def get_with_projects(cls, customer_id: str) -> Optional[Customer]: """ Get a customer with prefetched projects. Args: customer_id: The customer ID Returns: The customer with projects or None if not found """ try: return Customer.objects.prefetch_related('projects').get(pk=customer_id) except Customer.DoesNotExist: return None @classmethod def get_with_invoices(cls, customer_id: str) -> Optional[Customer]: """ Get a customer with prefetched invoices. Args: customer_id: The customer ID Returns: The customer with invoices or None if not found """ try: return Customer.objects.prefetch_related('invoices').get(pk=customer_id) except Customer.DoesNotExist: return None @classmethod def filter_customers(cls, name=None, city=None, state=None, start_date=None, end_date=None) -> QuerySet[Customer]: """ Filter customers by multiple criteria. """ queryset = Customer.objects.all() if name: queryset = queryset.filter(name__icontains=name) if city: queryset = queryset.filter(billing_city__icontains=city) if state: queryset = queryset.filter(billing_state__iexact=state) if start_date: queryset = queryset.filter(start_date__gte=start_date) if end_date: queryset = queryset.filter(end_date__lte=end_date) return queryset