7.2
This commit is contained in:
parent
091645a299
commit
fc8c69d0d5
Binary file not shown.
@ -49,14 +49,22 @@
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>ID Number</th>
|
||||
<th>Teams</th>
|
||||
<th class="text-end">Active Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for worker in workers %}
|
||||
<tr>
|
||||
<td>{{ worker.name }}</td>
|
||||
<td><strong>{{ worker.name }}</strong></td>
|
||||
<td>{{ worker.id_no }}</td>
|
||||
<td>
|
||||
{% for team in worker.teams.all %}
|
||||
<span class="badge bg-light text-dark border">{{ team.name }}</span>
|
||||
{% empty %}
|
||||
<span class="text-muted small">No Team</span>
|
||||
{% endfor %}
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<div class="form-check form-switch d-flex justify-content-end">
|
||||
<input class="form-check-input resource-toggle" type="checkbox" role="switch"
|
||||
@ -68,7 +76,7 @@
|
||||
</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr><td colspan="3" class="text-center text-muted py-4">No workers found.</td></tr>
|
||||
<tr><td colspan="4" class="text-center text-muted py-4">No workers found.</td></tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
@ -93,7 +101,7 @@
|
||||
<tbody>
|
||||
{% for project in projects %}
|
||||
<tr>
|
||||
<td>{{ project.name }}</td>
|
||||
<td><strong>{{ project.name }}</strong></td>
|
||||
<td>{{ project.description|truncatechars:50 }}</td>
|
||||
<td class="text-end">
|
||||
<div class="form-check form-switch d-flex justify-content-end">
|
||||
@ -132,7 +140,7 @@
|
||||
<tbody>
|
||||
{% for team in teams %}
|
||||
<tr>
|
||||
<td>{{ team.name }}</td>
|
||||
<td><strong>{{ team.name }}</strong></td>
|
||||
<td>{{ team.supervisor.username|default:"-" }}</td>
|
||||
<td>{{ team.workers.count }}</td>
|
||||
<td class="text-end">
|
||||
|
||||
@ -4,49 +4,154 @@
|
||||
{% block title %}Work Log History | LabourFlow{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="dashboard-header">
|
||||
<div class="dashboard-header pb-5">
|
||||
<div class="container">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<div>
|
||||
<h1 class="display-5 mb-2">Work Log History</h1>
|
||||
<p class="lead opacity-75">View and filter historical daily work logs.</p>
|
||||
<p class="lead opacity-75">Filter and review historical daily work logs.</p>
|
||||
</div>
|
||||
<a href="{% url 'log_attendance' %}" class="btn btn-accent shadow-sm">
|
||||
+ New Entry
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Filter Card -->
|
||||
<div class="card border-0 shadow-sm">
|
||||
<div class="card-body p-4">
|
||||
<form method="get" class="row g-3">
|
||||
<div class="col-md-3">
|
||||
<label class="form-label small text-muted text-uppercase fw-bold">Worker</label>
|
||||
<select name="worker" class="form-select">
|
||||
<option value="">All Workers</option>
|
||||
{% for w in workers %}
|
||||
<option value="{{ w.id }}" {% if selected_worker == w.id %}selected{% endif %}>{{ w.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label class="form-label small text-muted text-uppercase fw-bold">Team</label>
|
||||
<select name="team" class="form-select">
|
||||
<option value="">All Teams</option>
|
||||
{% for t in teams %}
|
||||
<option value="{{ t.id }}" {% if selected_team == t.id %}selected{% endif %}>{{ t.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label small text-muted text-uppercase fw-bold">Project</label>
|
||||
<select name="project" class="form-select">
|
||||
<option value="">All Projects</option>
|
||||
{% for p in projects %}
|
||||
<option value="{{ p.id }}" {% if selected_project == p.id %}selected{% endif %}>{{ p.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label class="form-label small text-muted text-uppercase fw-bold">Payment</label>
|
||||
<select name="payment_status" class="form-select">
|
||||
<option value="all" {% if selected_payment_status == 'all' %}selected{% endif %}>All Status</option>
|
||||
<option value="paid" {% if selected_payment_status == 'paid' %}selected{% endif %}>Paid</option>
|
||||
<option value="unpaid" {% if selected_payment_status == 'unpaid' %}selected{% endif %}>Unpaid</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-2 d-flex align-items-end">
|
||||
<button type="submit" class="btn btn-primary w-100">Filter</button>
|
||||
</div>
|
||||
</form>
|
||||
{% if selected_worker or selected_team or selected_project or selected_payment_status and selected_payment_status != 'all' %}
|
||||
<div class="mt-3">
|
||||
<a href="{% url 'work_log_list' %}" class="btn btn-sm btn-link text-decoration-none text-muted">
|
||||
<i class="bi bi-x-circle"></i> Clear all filters
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container mb-5 mt-n4">
|
||||
<div class="card p-4 shadow-sm">
|
||||
<div class="card shadow-sm border-0">
|
||||
<div class="card-body p-0">
|
||||
{% if logs %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover align-middle">
|
||||
<thead class="table-light">
|
||||
<table class="table table-hover align-middle mb-0">
|
||||
<thead class="bg-light">
|
||||
<tr>
|
||||
<th>Date</th>
|
||||
<th class="ps-4">Date</th>
|
||||
<th>Project</th>
|
||||
<th>Supervisor</th>
|
||||
<th>Labourers</th>
|
||||
<th>Notes</th>
|
||||
<th>Action</th>
|
||||
<th>Status / Payslip</th>
|
||||
<th>Supervisor</th>
|
||||
<th class="pe-4 text-end">Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for log in logs %}
|
||||
<tr>
|
||||
<td>{{ log.date }}</td>
|
||||
<td><strong>{{ log.project.name }}</strong></td>
|
||||
<td>{{ log.supervisor.username|default:"System" }}</td>
|
||||
<td class="ps-4">
|
||||
<span class="fw-bold">{{ log.date|date:"D, d M Y" }}</span>
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge bg-primary bg-opacity-10 text-primary">
|
||||
{{ log.workers.count }} Workers
|
||||
<span class="badge bg-secondary bg-opacity-10 text-secondary border border-secondary border-opacity-25 px-2 py-1">
|
||||
{{ log.project.name }}
|
||||
</span>
|
||||
</td>
|
||||
<td><small class="text-muted">{{ log.notes|truncatechars:30 }}</small></td>
|
||||
<td>
|
||||
<a href="/admin/core/worklog/{{ log.id }}/change/" class="btn btn-sm btn-outline-secondary">Edit</a>
|
||||
{% if selected_worker %}
|
||||
<span class="text-primary fw-medium">
|
||||
{% for w in log.workers.all %}
|
||||
{% if w.id == selected_worker %}
|
||||
{{ w.name }}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</span>
|
||||
{% if log.workers.count > 1 %}
|
||||
<small class="text-muted">(+{{ log.workers.count|add:"-1" }} others)</small>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<div class="d-flex flex-wrap gap-1">
|
||||
{% for w in log.workers.all|slice:":3" %}
|
||||
<span class="small bg-light px-2 py-1 rounded border">{{ w.name|truncatechars:12 }}</span>
|
||||
{% endfor %}
|
||||
{% if log.workers.count > 3 %}
|
||||
<span class="small text-muted align-self-center ms-1">+{{ log.workers.count|add:"-3" }}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% with payslip=log.paid_in.first %}
|
||||
{% if payslip %}
|
||||
<a href="{% url 'payslip_detail' payslip.id %}" class="text-decoration-none">
|
||||
<span class="badge bg-success bg-opacity-10 text-success border border-success border-opacity-25">
|
||||
<i class="bi bi-check-circle-fill"></i> Paid (Slip #{{ payslip.id }})
|
||||
</span>
|
||||
</a>
|
||||
{% else %}
|
||||
<span class="badge bg-warning bg-opacity-10 text-warning border border-warning border-opacity-25">
|
||||
Pending
|
||||
</span>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
</td>
|
||||
<td>
|
||||
<small class="text-muted">{{ log.supervisor.username|default:"System" }}</small>
|
||||
</td>
|
||||
<td class="pe-4 text-end">
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-sm btn-outline-light text-dark border-0" type="button" data-bs-toggle="dropdown">
|
||||
<i class="bi bi-three-dots-vertical"></i>
|
||||
</button>
|
||||
<ul class="dropdown-content dropdown-menu dropdown-menu-end">
|
||||
<li><a class="dropdown-item" href="/admin/core/worklog/{{ log.id }}/change/">Edit Entry</a></li>
|
||||
{% if log.notes %}
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<li class="px-3 py-1"><small class="text-muted">Note: {{ log.notes }}</small></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
@ -55,10 +160,20 @@
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="text-center py-5">
|
||||
<p class="text-muted">No work logs recorded yet.</p>
|
||||
<a href="{% url 'log_attendance' %}" class="btn btn-primary">Log First Attendance</a>
|
||||
<i class="bi bi-search display-1 text-muted opacity-25 mb-3 d-block"></i>
|
||||
<h4 class="text-muted">No logs found matching filters.</h4>
|
||||
<p class="text-muted mb-4">Try adjusting your filters or record a new entry.</p>
|
||||
<a href="{% url 'log_attendance' %}" class="btn btn-primary">Log Attendance</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.mt-n4 { margin-top: -1.5rem !important; }
|
||||
.bg-accent { background-color: var(--bs-accent); }
|
||||
.btn-accent { background-color: #ffde59; color: #000; border: none; font-weight: 600; }
|
||||
.btn-accent:hover { background-color: #e6c850; }
|
||||
</style>
|
||||
{% endblock %}
|
||||
@ -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,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user