from django.conf import settings from django.db import models from django.db.models import Q from decimal import Decimal from core.models.base import BaseModel from core.models.profile import TeamProfile from core.models.service import Service from core.models.project import Project class Report(BaseModel): """Report records""" date = models.DateField() team_member = models.ForeignKey(TeamProfile, on_delete=models.PROTECT, related_name='reports') services = models.ManyToManyField(Service, related_name='reports', blank=True) projects = models.ManyToManyField(Project, related_name='reports', blank=True) class Meta: ordering = ['-date'] indexes = [ models.Index(fields=['team_member', 'date']), ] verbose_name = "Report" verbose_name_plural = "Reports" def __str__(self): return f"Report for {self.team_member.full_name} on {self.date}" def get_service_labor_share(self, service): """Get this team member's share of labor for a service (excluding Dispatch)""" if not service.account_address: return Decimal('0.00') # Get the labor rate for the service's account address labor = service.account_address.labors.filter( Q(start_date__lte=self.date) & (Q(end_date__isnull=True) | Q(end_date__gte=self.date)) ).first() if not labor: return Decimal('0.00') # Count team members assigned to this service, excluding Dispatch team_members = service.team_members.exclude(id=settings.DISPATCH_TEAM_PROFILE_ID) team_member_count = team_members.count() if team_member_count == 0: return Decimal('0.00') # Only include this team member's share if they're assigned to the service (and not Dispatch) if not team_members.filter(id=self.team_member.id).exists(): return Decimal('0.00') # Divide labor rate by number of team members (excluding Dispatch) return labor.amount / team_member_count def get_project_labor_share(self, project): """Get this team member's share of labor for a project (excluding Dispatch)""" # Count team members assigned to this project, excluding Dispatch team_members = project.team_members.exclude(id=settings.DISPATCH_TEAM_PROFILE_ID) team_member_count = team_members.count() if team_member_count == 0: return Decimal('0.00') # Only include this team member's share if they're assigned to the project (and not Dispatch) if not team_members.filter(id=self.team_member.id).exists(): return Decimal('0.00') # Divide project labor by number of team members (excluding Dispatch) return project.labor / team_member_count def get_services_labor_total(self): """Calculate total labor share for all services in this report""" total = Decimal('0.00') for service in self.services.all(): total += self.get_service_labor_share(service) return total def get_projects_labor_total(self): """Calculate total labor share for all projects in this report""" total = Decimal('0.00') for project in self.projects.all(): total += self.get_project_labor_share(project) return total def get_total_labor_value(self): """Calculate total labor share for both services and projects""" return self.get_services_labor_total() + self.get_projects_labor_total() def get_labor_breakdown(self): """Get a detailed breakdown of labor shares for this specific team member""" services_data = [] for service in self.services.all(): # Count team members excluding Dispatch team_members = service.team_members.exclude(id=settings.DISPATCH_TEAM_PROFILE_ID) team_member_count = team_members.count() is_assigned = team_members.filter(id=self.team_member.id).exists() labor_rate = Decimal('0.00') if service.account_address: labor = service.account_address.labors.filter( Q(start_date__lte=self.date) & (Q(end_date__isnull=True) | Q(end_date__gte=self.date)) ).first() labor_rate = labor.amount if labor else Decimal('0.00') share = self.get_service_labor_share(service) services_data.append({ 'service_id': service.id, 'account_name': service.account_address.account.name if service.account_address else None, 'address': service.account_address.name if service.account_address else None, 'total_labor_rate': labor_rate, 'team_member_count': team_member_count, 'is_team_member_assigned': is_assigned, 'labor_share': share }) projects_data = [] for project in self.projects.all(): # Count team members excluding Dispatch team_members = project.team_members.exclude(id=settings.DISPATCH_TEAM_PROFILE_ID) team_member_count = team_members.count() is_assigned = team_members.filter(id=self.team_member.id).exists() share = self.get_project_labor_share(project) projects_data.append({ 'project_id': project.id, 'project_name': project.name, 'total_labor_amount': project.labor, 'team_member_count': team_member_count, 'is_team_member_assigned': is_assigned, 'labor_share': share }) return { 'team_member_id': self.team_member.id, 'team_member_name': self.team_member.full_name, 'services': services_data, 'projects': projects_data, 'services_total': self.get_services_labor_total(), 'projects_total': self.get_projects_labor_total(), 'grand_total': self.get_total_labor_value() }