From fc8c69d0d5a24e824efafbdb8c75e3738f9e8f21 Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Tue, 3 Feb 2026 22:24:29 +0000 Subject: [PATCH] 7.2 --- core/__pycache__/views.cpython-311.pyc | Bin 14893 -> 17145 bytes core/templates/core/manage_resources.html | 18 +- core/templates/core/work_log_list.html | 201 +++++++++++++++++----- core/views.py | 56 +++++- 4 files changed, 221 insertions(+), 54 deletions(-) diff --git a/core/__pycache__/views.cpython-311.pyc b/core/__pycache__/views.cpython-311.pyc index 39f37601bf7c81fb1730ebc9503aff333c953936..366ef823ccf75506193f010e7dda5608b495891c 100644 GIT binary patch delta 4300 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 Jo-#vM{{c~XJ$(QG delta 2670 zcmZuzU2GIp6rS1LZnvH7^k=u*|8`rT+og0Dim3ec2WbICKmnCa%sSnzQc< zX|t#pBOxl>M5Dn6ANUJuNUFy8pyADEOlVU>GLeJ_jZYdAUerY6Id^D5VA6g)bM85J z?)mPy-|b(o%bz&vg{rDb37>O^U(KuO(`uvq`{~59C0WuXJ!q(Ab*5U_cM@;ZW`RNLu>)$(Ry7E)AGjgd+ zPVj$%@5igL2DjWAJH&F1?Uv=N$$|ql=!9B=I{roZo|bkpgB0m6STDH&U2_=JlQY6Q zL(A)X$V?KfBJ3aGyA&pK9COgTi}kY(su!y30?t9Yd#dWYxv%~*468#z^c z&uWgt3c6OvF*r6`(=ZTd-+^N}8Da6EE%f8>t&<4+MJ2`G38;MMitf^k>{Kj9Nz$~W z`*i=boR`mI_+?5F&t54R*aZz}zub_sfTb4~BZy8-IHm!Q;ApZ;Fa=_79z_TA6VNaUTL3G;tNXrx{{n%k{1(m zO8XY*bT?;K6RW|_w(O#(=w1jh-eoouQzP7m^n^wyYK zK^tdTV74_0IcCEv=>C8rPa_>R3fI;6yc(ZVyB2EHuIp;=yxMzlbWUBnpswXt z6Ayh!iT&;%ORR`w3PuRh1fmxs z5`U{d)+QRUwx}Hi%P>%9?TIl<106oZz;9-+_8UIjrPR6~{GG2+Dm4|*E&&O}Vta`t zY?^IdKP;ChG1=<1ieQyd^naYcE*d*3xYc z3W8g=b!2pS|IU5cQU240%2JpHsK4Ecks~=)bn;dKOnUd@wF9C_6_4@?PvNuJLPL~w zmSBljS6yTV7^!uKMTc0cj(Uc8J~-Op2-?Lq4iXsOsOLP59ACgAKq}=%cH`KF7BYkH?l>lOFKHzaRd3 bxlbOI|B&v&kRoqE&t2HeFK>A~K%e;!s;YU_ diff --git a/core/templates/core/manage_resources.html b/core/templates/core/manage_resources.html index b79cd4c..025f0c5 100644 --- a/core/templates/core/manage_resources.html +++ b/core/templates/core/manage_resources.html @@ -49,14 +49,22 @@ Name ID Number + Teams Active Status {% for worker in workers %} - {{ worker.name }} + {{ worker.name }} {{ worker.id_no }} + + {% for team in worker.teams.all %} + {{ team.name }} + {% empty %} + No Team + {% endfor %} +
{% empty %} - No workers found. + No workers found. {% endfor %} @@ -93,7 +101,7 @@ {% for project in projects %} - {{ project.name }} + {{ project.name }} {{ project.description|truncatechars:50 }}
@@ -132,7 +140,7 @@ {% for team in teams %} - {{ team.name }} + {{ team.name }} {{ team.supervisor.username|default:"-" }} {{ team.workers.count }} @@ -210,4 +218,4 @@ }); }); -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/core/templates/core/work_log_list.html b/core/templates/core/work_log_list.html index d15e9e7..a6954d6 100644 --- a/core/templates/core/work_log_list.html +++ b/core/templates/core/work_log_list.html @@ -4,61 +4,176 @@ {% block title %}Work Log History | LabourFlow{% endblock %} {% block content %} -
+
-
+

Work Log History

-

View and filter historical daily work logs.

+

Filter and review historical daily work logs.

+ New Entry
+ + +
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+ {% if selected_worker or selected_team or selected_project or selected_payment_status and selected_payment_status != 'all' %} + + {% endif %} +
+
-
- {% if logs %} -
- - - - - - - - - - - - - {% for log in logs %} - - - - - - - - - {% endfor %} - -
DateProjectSupervisorLabourersNotesAction
{{ log.date }}{{ log.project.name }}{{ log.supervisor.username|default:"System" }} - - {{ log.workers.count }} Workers - - {{ log.notes|truncatechars:30 }} - Edit -
+
+
+ {% if logs %} +
+ + + + + + + + + + + + + {% for log in logs %} + + + + + + + + + {% endfor %} + +
DateProjectLabourersStatus / PayslipSupervisorAction
+ {{ log.date|date:"D, d M Y" }} + + + {{ log.project.name }} + + + {% if selected_worker %} + + {% for w in log.workers.all %} + {% if w.id == selected_worker %} + {{ w.name }} + {% endif %} + {% endfor %} + + {% if log.workers.count > 1 %} + (+{{ log.workers.count|add:"-1" }} others) + {% endif %} + {% else %} +
+ {% for w in log.workers.all|slice:":3" %} + {{ w.name|truncatechars:12 }} + {% endfor %} + {% if log.workers.count > 3 %} + +{{ log.workers.count|add:"-3" }} + {% endif %} +
+ {% endif %} +
+ {% with payslip=log.paid_in.first %} + {% if payslip %} + + + Paid (Slip #{{ payslip.id }}) + + + {% else %} + + Pending + + {% endif %} + {% endwith %} + + {{ log.supervisor.username|default:"System" }} + + +
+
+ {% else %} +
+ +

No logs found matching filters.

+

Try adjusting your filters or record a new entry.

+ Log Attendance +
+ {% endif %}
- {% else %} -
-

No work logs recorded yet.

- Log First Attendance -
- {% endif %}
-{% endblock %} + + +{% endblock %} \ No newline at end of file diff --git a/core/views.py b/core/views.py index 241de5c..956a7db 100644 --- a/core/views.py +++ b/core/views.py @@ -4,7 +4,7 @@ import json from django.shortcuts import render, redirect, get_object_or_404 from django.utils import timezone from django.contrib.auth.decorators import login_required -from django.db.models import Sum, Q +from django.db.models import Sum, Q, Prefetch from django.core.mail import send_mail from django.conf import settings from django.contrib import messages @@ -148,14 +148,58 @@ def log_attendance(request): return render(request, 'core/log_attendance.html', context) def work_log_list(request): - logs = WorkLog.objects.all().order_by('-date') - return render(request, 'core/work_log_list.html', {'logs': logs}) + """View work log history with advanced filtering.""" + worker_id = request.GET.get('worker') + team_id = request.GET.get('team') + project_id = request.GET.get('project') + payment_status = request.GET.get('payment_status') # 'paid', 'unpaid', 'all' + + logs = WorkLog.objects.all().prefetch_related('workers', 'project', 'supervisor', 'paid_in').order_by('-date', '-id') + + if worker_id: + logs = logs.filter(workers__id=worker_id) + + if team_id: + # Find workers in this team and filter logs containing them + team_workers = Worker.objects.filter(teams__id=team_id) + logs = logs.filter(workers__in=team_workers).distinct() + + if project_id: + logs = logs.filter(project_id=project_id) + + if payment_status == 'paid': + # Logs that are linked to at least one PayrollRecord + logs = logs.filter(paid_in__isnull=False).distinct() + elif payment_status == 'unpaid': + # This is tricky because a log can have multiple workers, some paid some not. + # But usually a WorkLog is marked paid when its workers are paid. + # If we filtered by worker, we can check if THAT worker is paid in that log. + if worker_id: + worker = get_object_or_404(Worker, pk=worker_id) + logs = logs.exclude(paid_in__worker=worker) + else: + logs = logs.filter(paid_in__isnull=True) + + # Context for filters + context = { + 'logs': logs, + 'workers': Worker.objects.filter(is_active=True).order_by('name'), + 'teams': Team.objects.filter(is_active=True).order_by('name'), + 'projects': Project.objects.filter(is_active=True).order_by('name'), + 'selected_worker': int(worker_id) if worker_id else None, + 'selected_team': int(team_id) if team_id else None, + 'selected_project': int(project_id) if project_id else None, + 'selected_payment_status': payment_status, + } + + return render(request, 'core/work_log_list.html', context) def manage_resources(request): """View to manage active status of resources.""" - workers = Worker.objects.all().order_by('name') + # Prefetch teams for workers to avoid N+1 in template + workers = Worker.objects.all().prefetch_related('teams').order_by('name') projects = Project.objects.all().order_by('name') - teams = Team.objects.all().order_by('name') + teams = Team.objects.all().prefetch_related('workers').order_by('name') context = { 'workers': workers, @@ -302,4 +346,4 @@ def payslip_detail(request, pk): 'record': record, 'logs': logs, } - return render(request, 'core/payslip.html', context) + return render(request, 'core/payslip.html', context) \ No newline at end of file