Enrich alltime_projects context with working_days + avg_per_working_day
Chapter I of the executive report needs per-project working-day count and avg rand per working day. Instead of modifying the shared _get_labour_costs helper (used by other sections with different column sets), enrich the output INSIDE _build_report_context: wrap the raw result and add working_days (distinct work-log dates per project) and avg_per_working_day (total_cost / working_days, null-safe). Also attaches start_date from the Project model (may be None if not set). 1 test added. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
e8ba2c6745
commit
ea1e4bdbcb
@ -668,3 +668,27 @@ class TeamProjectActivityTests(TestCase):
|
|||||||
r = _team_project_activity(self.logs_qs)
|
r = _team_project_activity(self.logs_qs)
|
||||||
team_names = [row['team_name'] for row in r['rows']]
|
team_names = [row['team_name'] for row in r['rows']]
|
||||||
self.assertNotIn('GhostTeam', team_names)
|
self.assertNotIn('GhostTeam', team_names)
|
||||||
|
|
||||||
|
|
||||||
|
class ChapterOneEnrichmentTests(TestCase):
|
||||||
|
"""Chapter I — All Time Projects gains working_days and avg_per_working_day."""
|
||||||
|
|
||||||
|
def test_alltime_projects_includes_working_days_and_avg(self):
|
||||||
|
from core.views import _build_report_context
|
||||||
|
admin = User.objects.create_user(username='c1', is_staff=True)
|
||||||
|
proj = Project.objects.create(name='C1', start_date=datetime.date(2026, 1, 1))
|
||||||
|
w = Worker.objects.create(name='W', id_number='W1', monthly_salary=Decimal('4000'))
|
||||||
|
# 4 distinct dates, 1 worker each; daily_rate=200; total = R 800; working_days=4; avg=200
|
||||||
|
for d in (1, 2, 3, 4):
|
||||||
|
log = WorkLog.objects.create(
|
||||||
|
date=datetime.date(2026, 3, d), project=proj, supervisor=admin,
|
||||||
|
)
|
||||||
|
log.workers.add(w)
|
||||||
|
ctx = _build_report_context(
|
||||||
|
datetime.date(2026, 1, 1), datetime.date(2026, 12, 31),
|
||||||
|
)
|
||||||
|
by_name = {p['project']: p for p in ctx['alltime_projects']}
|
||||||
|
self.assertIn('C1', by_name)
|
||||||
|
self.assertEqual(by_name['C1']['working_days'], 4)
|
||||||
|
self.assertEqual(by_name['C1']['avg_per_working_day'], Decimal('200.00'))
|
||||||
|
self.assertEqual(by_name['C1']['start_date'], datetime.date(2026, 1, 1))
|
||||||
|
|||||||
@ -2114,7 +2114,36 @@ def _build_report_context(start_date, end_date, project_id=None, team_id=None):
|
|||||||
all_time_logs = all_time_logs.filter(project_id=project_id)
|
all_time_logs = all_time_logs.filter(project_id=project_id)
|
||||||
if team_id:
|
if team_id:
|
||||||
all_time_logs = all_time_logs.filter(team_id=team_id)
|
all_time_logs = all_time_logs.filter(team_id=team_id)
|
||||||
alltime_projects = _get_labour_costs(all_time_logs, 'project__name', 'project')
|
# === CHAPTER I — All Time Projects (enriched) ===
|
||||||
|
# Adds working_days and avg_per_working_day (the 2026-04-23 design).
|
||||||
|
# Can't just extend _get_labour_costs because that helper is used by
|
||||||
|
# other sections with different columns. Wrap it here instead.
|
||||||
|
alltime_projects_raw = _get_labour_costs(all_time_logs, 'project__name', 'project')
|
||||||
|
# Build a lookup of working_days per project (distinct work-log dates)
|
||||||
|
project_working_days = dict(
|
||||||
|
all_time_logs.filter(project__isnull=False)
|
||||||
|
.values('project_id', 'project__name')
|
||||||
|
.annotate(days=Count('date', distinct=True))
|
||||||
|
.values_list('project__name', 'days')
|
||||||
|
)
|
||||||
|
# Lookup project start_date from the Project model (authoritative source)
|
||||||
|
start_dates = dict(
|
||||||
|
Project.objects.values_list('name', 'start_date')
|
||||||
|
)
|
||||||
|
alltime_projects = []
|
||||||
|
for row in alltime_projects_raw:
|
||||||
|
name = row['project']
|
||||||
|
wdays = project_working_days.get(name, 0)
|
||||||
|
total = row['total'] or Decimal('0.00')
|
||||||
|
avg = (total / wdays).quantize(Decimal('0.01')) if wdays else Decimal('0.00')
|
||||||
|
alltime_projects.append({
|
||||||
|
'project': name,
|
||||||
|
'worker_days': row['worker_days'],
|
||||||
|
'total': total,
|
||||||
|
'start_date': start_dates.get(name), # may be None
|
||||||
|
'working_days': wdays,
|
||||||
|
'avg_per_working_day': avg,
|
||||||
|
})
|
||||||
alltime_teams = _get_labour_costs(
|
alltime_teams = _get_labour_costs(
|
||||||
all_time_logs.filter(team__isnull=False), 'team__name', 'team'
|
all_time_logs.filter(team__isnull=False), 'team__name', 'team'
|
||||||
)
|
)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user