From f6975bfb2fd770a0625840fa60da5b715e086b80 Mon Sep 17 00:00:00 2001 From: Konrad du Plessis Date: Thu, 23 Apr 2026 13:30:56 +0200 Subject: [PATCH] feat(report): 'Last Activity' column in All Time Projects table MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Konrad's Checkpoint-1 feedback: 'Inside the all time projects table, can we have a column with the last transaction date for a project? It will make it easier to find data for projects. It is nice to have the filter, but you can still skip around looking for when the last transaction was.' Added a 'last_activity' entry to each alltime_projects row in _build_report_context — computed as max(WorkLog.date) grouped by project name (respects the same project_ids/team_ids filters already applied to all_time_logs). Rendered in both the on-screen report (report.html) and the PDF (report_pdf.html) as a new 'Last Activity' column sitting between 'Start' and 'Working Days'. Existing ChapterOneEnrichmentTests extended with a last_activity assertion locking in the 'most recent log date' semantics. No other tests touched. 47/47 pass. Co-Authored-By: Claude Opus 4.7 (1M context) --- core/templates/core/pdf/report_pdf.html | 2 ++ core/templates/core/report.html | 2 ++ core/tests.py | 6 ++++++ core/views.py | 10 ++++++++++ 4 files changed, 20 insertions(+) diff --git a/core/templates/core/pdf/report_pdf.html b/core/templates/core/pdf/report_pdf.html index 28cbf61..232e97a 100644 --- a/core/templates/core/pdf/report_pdf.html +++ b/core/templates/core/pdf/report_pdf.html @@ -565,6 +565,7 @@ Project Start + Last Activity Working Days Total Cost Avg R / Working Day @@ -573,6 +574,7 @@ {{ item.project }} {% if item.start_date %}{{ item.start_date|date:"d M Y" }}{% else %}{% endif %} + {% if item.last_activity %}{{ item.last_activity|date:"d M Y" }}{% else %}{% endif %} {% if item.working_days %}{{ item.working_days }}{% else %}{% endif %} R {{ item.total|money }} {% if item.working_days %}R {{ item.avg_per_working_day|money }}{% else %}{% endif %} diff --git a/core/templates/core/report.html b/core/templates/core/report.html index e23b5a7..516aac7 100644 --- a/core/templates/core/report.html +++ b/core/templates/core/report.html @@ -245,6 +245,7 @@ Project Start + Last Activity Working Days Total Cost Avg R / Working Day @@ -255,6 +256,7 @@ {{ item.project }} {% if item.start_date %}{{ item.start_date|date:"d M Y" }}{% else %}{% endif %} + {% if item.last_activity %}{{ item.last_activity|date:"d M Y" }}{% else %}{% endif %} {{ item.working_days|default:"—" }} R {{ item.total|money }} {% if item.working_days %}R {{ item.avg_per_working_day|money }}{% else %}{% endif %} diff --git a/core/tests.py b/core/tests.py index f6e813b..bfa25c2 100644 --- a/core/tests.py +++ b/core/tests.py @@ -692,6 +692,12 @@ class ChapterOneEnrichmentTests(TestCase): 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)) + # last_activity = the most recent WorkLog.date (4th of March here) + self.assertEqual( + by_name['C1']['last_activity'], datetime.date(2026, 3, 4), + 'alltime_projects rows should expose the most-recent log date ' + 'so the report can show "Last Activity" per project' + ) # ============================================================================= diff --git a/core/views.py b/core/views.py index 48bfe21..73d0784 100644 --- a/core/views.py +++ b/core/views.py @@ -2138,6 +2138,15 @@ def _build_report_context(start_date, end_date, project_ids=None, team_ids=None) start_dates = dict( Project.objects.values_list('name', 'start_date') ) + # Lookup the most recent WorkLog.date for each project (for the new + # "Last Activity" column — helps Konrad spot which projects are dormant + # without having to scroll through date pickers). + last_activity = dict( + all_time_logs.filter(project__isnull=False) + .values('project__name') + .annotate(last=Max('date')) + .values_list('project__name', 'last') + ) alltime_projects = [] for row in alltime_projects_raw: name = row['project'] @@ -2149,6 +2158,7 @@ def _build_report_context(start_date, end_date, project_ids=None, team_ids=None) 'worker_days': row['worker_days'], 'total': total, 'start_date': start_dates.get(name), # may be None + 'last_activity': last_activity.get(name), # may be None 'working_days': wdays, 'avg_per_working_day': avg, })