184 lines
5.5 KiB
Python
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)
|