Fix attendance start date, history worker filter, and add Amount column

1. Attendance form: Force start date to blank by clearing Django 5.x auto-fill
   from model default (default=timezone.now). Added self.fields['date'].initial=None
   in AttendanceLogForm.__init__().

2. History list view: When filtering by a specific worker, show only that
   worker's name in the Workers column (not all workers on the log). Uses
   filtered_worker_obj passed from the view.

3. History list view: Added Amount column (admin-only) showing daily cost.
   When filtering by worker, shows that worker's daily_rate. When unfiltered,
   shows total via new WorkLog.display_amount property (sum of all workers'
   daily_rate, uses prefetch cache for efficiency).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Konrad du Plessis 2026-02-23 00:13:46 +02:00
parent 7fd32a0aee
commit b6fca98c17
4 changed files with 41 additions and 6 deletions

View File

@ -92,6 +92,11 @@ class AttendanceLogForm(forms.ModelForm):
# Make team optional (it already is on the model, but make the form match)
self.fields['team'].required = False
# Force start date to be blank — don't pre-fill with today's date.
# Django 5.x auto-fills form fields from model defaults (default=timezone.now),
# but we want the user to consciously pick a date every time.
self.fields['date'].initial = None
def clean(self):
"""Validate the date range makes sense."""
cleaned_data = super().clean()

View File

@ -77,6 +77,12 @@ class WorkLog(models.Model):
overtime_amount = models.DecimalField(max_digits=3, decimal_places=2, choices=OVERTIME_CHOICES, default=Decimal('0.00'))
priced_workers = models.ManyToManyField(Worker, related_name='priced_overtime_logs', blank=True)
@property
def display_amount(self):
"""Total daily cost for all workers on this log (sum of daily_rate).
Works efficiently with prefetch_related('workers')."""
return sum(w.daily_rate for w in self.workers.all())
def __str__(self):
return f"{self.date} - {self.project.name}"

View File

@ -592,6 +592,7 @@
<th scope="col">Workers</th>
<th scope="col">Overtime</th>
<th scope="col">Status</th>
{% if is_admin %}<th scope="col">Amount</th>{% endif %}
<th scope="col" class="pe-4">Supervisor</th>
</tr>
</thead>
@ -601,11 +602,16 @@
<td class="ps-4 align-middle">{{ log.date }}</td>
<td class="align-middle"><strong>{{ log.project.name }}</strong></td>
<td class="align-middle">
{# Show worker names as comma-separated list #}
{% for w in log.workers.all %}
{{ w.name }}{% if not forloop.last %}, {% endif %}
{% endfor %}
<span class="badge bg-secondary ms-1">{{ log.workers.count }}</span>
{# When filtering by a specific worker, show only that worker.
Otherwise show all workers on this log. #}
{% if filtered_worker_obj %}
{{ filtered_worker_obj.name }}
{% else %}
{% for w in log.workers.all %}
{{ w.name }}{% if not forloop.last %}, {% endif %}
{% endfor %}
<span class="badge bg-secondary ms-1">{{ log.workers.count }}</span>
{% endif %}
</td>
<td class="align-middle">
{% if log.overtime_amount > 0 %}
@ -622,6 +628,17 @@
<span class="badge bg-danger bg-opacity-75"><i class="fas fa-clock me-1"></i>Unpaid</span>
{% endif %}
</td>
{% if is_admin %}
<td class="align-middle">
{# Daily cost — when filtering by worker show that worker's rate,
otherwise show the total for all workers on the log #}
{% if filtered_worker_obj %}
<span class="text-success fw-semibold">R {{ filtered_worker_obj.daily_rate }}</span>
{% else %}
<span class="text-success fw-semibold">R {{ log.display_amount }}</span>
{% endif %}
</td>
{% endif %}
<td class="pe-4 align-middle">
{% if log.supervisor %}
{{ log.supervisor.get_full_name|default:log.supervisor.username }}
@ -632,7 +649,7 @@
</tr>
{% empty %}
<tr>
<td colspan="6" class="text-center py-5 text-muted">
<td colspan="{% if is_admin %}7{% else %}6{% endif %}" class="text-center py-5 text-muted">
<i class="fas fa-inbox fa-2x mb-3 d-block opacity-50"></i>
No work history found.
{% if selected_worker or selected_project or selected_status %}

View File

@ -413,6 +413,12 @@ def work_history(request):
# Count filtered results BEFORE adding joins (more efficient SQL)
filtered_log_count = logs.count() if has_active_filters else 0
# If filtering by worker, look up the Worker object so the template can
# show just that worker's name instead of all workers on the log.
filtered_worker_obj = None
if worker_filter:
filtered_worker_obj = Worker.objects.filter(id=worker_filter).first()
# Add related data and order by date (newest first)
logs = logs.select_related(
'project', 'supervisor'
@ -458,6 +464,7 @@ def work_history(request):
'has_active_filters': has_active_filters,
'total_log_count': total_log_count,
'filtered_log_count': filtered_log_count,
'filtered_worker_obj': filtered_worker_obj,
}
# === CALENDAR MODE ===