2026-01-26 10:30:49 -05:00

184 lines
5.5 KiB
Python

"""
Repository for Revenue model operations.
"""
from typing import Optional, List
from datetime import date
from django.db.models import QuerySet, Q
from django.utils import timezone
from backend.core.models import Revenue
from backend.core.repositories.base import BaseRepository
class RevenueRepository(BaseRepository[Revenue]):
"""
Repository for Revenue model operations.
"""
model = Revenue
@classmethod
def get_by_account(cls, account_id: str) -> QuerySet[Revenue]:
"""
Get revenues by account.
Args:
account_id: The account ID
Returns:
QuerySet of revenues for the account
"""
return Revenue.objects.filter(account_id=account_id)
@classmethod
def get_active(cls) -> QuerySet[Revenue]:
"""
Get active revenues.
Returns:
QuerySet of active revenues
"""
current_date = timezone.now().date()
return Revenue.objects.filter(
start_date__lte=current_date
).filter(
Q(end_date__isnull=True) | Q(end_date__gte=current_date)
)
@classmethod
def get_active_by_account(cls, account_id: str) -> Optional[Revenue]:
"""
Get active revenue for an account.
Args:
account_id: The account ID
Returns:
Active revenue for the account or None if not found
"""
current_date = timezone.now().date()
return Revenue.objects.filter(
account_id=account_id,
start_date__lte=current_date
).filter(
Q(end_date__isnull=True) | Q(end_date__gte=current_date)
).first()
@classmethod
def end_revenue(cls, revenue_id: str) -> Optional[Revenue]:
"""
End a revenue record by setting its end date to today.
Args:
revenue_id: The revenue ID
Returns:
The updated revenue or None if not found
"""
return cls.update(revenue_id, {'end_date': timezone.now().date()})
@classmethod
def get_by_date_range(cls, start_date: date = None, end_date: date = None) -> QuerySet[Revenue]:
"""
Get revenues that were active during a date range.
Args:
start_date: Start date (inclusive)
end_date: End date (inclusive)
Returns:
QuerySet of revenues active during the date range
"""
queryset = Revenue.objects.all()
if start_date:
# Exclude revenues that ended before the start date
queryset = queryset.exclude(
end_date__isnull=False,
end_date__lt=start_date
)
if end_date:
# Exclude revenues that started after the end date
queryset = queryset.exclude(start_date__gt=end_date)
return queryset
@classmethod
def search(cls, search_term: str, search_fields: List[str] = None) -> QuerySet[Revenue]:
"""
Search revenue records.
Args:
search_term: The search term
search_fields: Optional list of fields to search (ignored, using predefined fields)
Returns:
QuerySet of matching revenue records
"""
return super().search(
search_term,
['account__name']
)
@classmethod
def get_total_revenue(cls, account_id: str = None, start_date: date = None, end_date: date = None) -> float:
"""
Get total revenue for an account or all accounts within a date range.
Args:
account_id: Optional account ID to filter by
start_date: Optional start date for the period
end_date: Optional end date for the period
Returns:
Total revenue
"""
revenues = cls.get_by_date_range(start_date, end_date)
if account_id:
revenues = revenues.filter(account_id=account_id)
# Calculate proportional revenue for records that span beyond the range
total_revenue = 0
for revenue in revenues:
# Get the effective start and end dates for the calculation
# (intersection of revenue period and requested period)
effective_start = max(revenue.start_date, start_date) if start_date else revenue.start_date
current_date = timezone.now().date()
if revenue.end_date:
effective_end = min(revenue.end_date, end_date) if end_date else revenue.end_date
else:
effective_end = end_date if end_date else current_date
# Calculate days in range
days_in_range = (effective_end - effective_start).days + 1
# Calculate total days for revenue period
if revenue.end_date:
total_days = (revenue.end_date - revenue.start_date).days + 1
else:
total_days = (current_date - revenue.start_date).days + 1
# Avoid division by zero
if total_days <= 0:
total_days = 1
# Calculate proportional revenue
proportional_revenue = revenue.amount * (days_in_range / total_days)
total_revenue += proportional_revenue
return float(total_revenue)
@classmethod
def get_inactive(cls) -> QuerySet[Revenue]:
"""
Get inactive revenues.
Returns:
QuerySet of inactive revenues
"""
current_date = timezone.now().date()
return Revenue.objects.filter(end_date__lt=current_date)