Add _company_cost_velocity helper + 3 tests

Computes company-wide avg daily and monthly labour cost for the
executive report's hero KPI band (cards 3 and 4). Denominator is
working days (distinct work-log dates), not calendar days — true
cost-of-a-productive-day metric per design section 2.

Monthly = daily * 30.44 (the 365.25/12 month-length approximation,
which keeps annualised totals correct on average).

Tests cover: empty DB returns zero, known values with assertAlmostEqual
for the 30.44 multiplication, and that multiple workers on one date
count as 1 working day (not N).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Konrad du Plessis 2026-04-22 22:20:14 +02:00
parent 6be6a09056
commit e74f48f050
2 changed files with 94 additions and 0 deletions

View File

@ -476,3 +476,61 @@ class SupervisorPickerQuerysetTests(TestCase):
username='an_admin', password='pass', is_staff=True
)
self.assertIn(admin, _supervisor_user_queryset())
# =============================================================================
# === TESTS FOR EXECUTIVE REPORT v2 ===
# Covers the new helpers introduced in the report rebuild (Apr 2026):
# _company_cost_velocity, _current_outstanding_in_scope, _team_project_activity,
# and the multi-filter extension of _build_report_context.
# =============================================================================
class CompanyCostVelocityTests(TestCase):
"""Company-wide avg daily and monthly labour cost (hero KPI card 3 & 4)."""
def test_empty_db_returns_zero(self):
from core.views import _company_cost_velocity
result = _company_cost_velocity()
self.assertEqual(result['avg_daily'], Decimal('0.00'))
self.assertEqual(result['avg_monthly'], Decimal('0.00'))
self.assertEqual(result['working_days'], 0)
def test_known_values(self):
from core.views import _company_cost_velocity
# Setup: 2 workers (daily_rate = 4000/20 = R 200 each), each works 5 distinct dates.
# Lifetime cost = 2 workers * 5 days * R 200 = R 2000. Working days = 5.
# Avg daily = 2000 / 5 = R 400.
# Avg monthly = 400 * 30.44 = R 12,176.
admin = User.objects.create_user(username='admin-cv', is_staff=True)
project = Project.objects.create(name='P')
w1 = Worker.objects.create(name='W1', id_number='W1', monthly_salary=Decimal('4000'))
w2 = Worker.objects.create(name='W2', id_number='W2', monthly_salary=Decimal('4000'))
for d in range(1, 6): # 5 distinct dates
log = WorkLog.objects.create(
date=datetime.date(2026, 3, d),
project=project, supervisor=admin,
)
log.workers.add(w1, w2)
result = _company_cost_velocity()
self.assertEqual(result['working_days'], 5)
self.assertEqual(result['avg_daily'], Decimal('400.00'))
# Tolerance: ±1 cent for the 30.44 multiplication
self.assertAlmostEqual(
float(result['avg_monthly']), 12176.00, delta=0.01
)
def test_duplicate_dates_not_double_counted(self):
"""Two workers working the same date = 1 distinct date, not 2."""
from core.views import _company_cost_velocity
admin = User.objects.create_user(username='admin-cv2', is_staff=True)
project = Project.objects.create(name='P2')
w1 = Worker.objects.create(name='X', id_number='X1', monthly_salary=Decimal('4000'))
w2 = Worker.objects.create(name='Y', id_number='Y1', monthly_salary=Decimal('4000'))
log = WorkLog.objects.create(
date=datetime.date(2026, 3, 1), project=project, supervisor=admin,
)
log.workers.add(w1, w2)
result = _company_cost_velocity()
self.assertEqual(result['working_days'], 1) # not 2

View File

@ -216,6 +216,42 @@ def _compute_outstanding(project_ids=None, team_ids=None):
}
# =============================================================================
# === COMPANY COST VELOCITY ===
# Lifetime "what does a typical FoxFitt working day cost us?" metric.
# Denominator = COUNT(DISTINCT work_log.date) — true working days, not
# calendar days (rain days, weekends, permit delays don't dilute the rate).
# Used by the hero KPI band on the payroll report.
# =============================================================================
def _company_cost_velocity():
"""Return company-wide avg daily and monthly labour cost (lifetime)."""
# Total lifetime labour cost: sum of (worker.daily_rate) over every
# (log, worker) pair that has ever been logged.
total_cost = Decimal('0.00')
for wl in WorkLog.objects.prefetch_related('workers').all():
for worker in wl.workers.all():
total_cost += worker.daily_rate
# Distinct work-log dates = working days
working_days = WorkLog.objects.values('date').distinct().count()
if working_days == 0:
avg_daily = Decimal('0.00')
else:
avg_daily = (total_cost / working_days).quantize(Decimal('0.01'))
# 30.44 = 365.25 / 12 — standard month-length approximation.
# Keeps annualised totals correct on average.
avg_monthly = (avg_daily * Decimal('30.44')).quantize(Decimal('0.01'))
return {
'avg_daily': avg_daily,
'avg_monthly': avg_monthly,
'working_days': working_days,
}
# === HOME DASHBOARD ===
# The main page users see after logging in. Shows different content
# depending on whether the user is an admin or supervisor.