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:
parent
7fd32a0aee
commit
b6fca98c17
@ -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()
|
||||
|
||||
@ -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}"
|
||||
|
||||
|
||||
@ -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 %}
|
||||
|
||||
@ -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 ===
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user