Migration 0015 adds Project FK (SET_NULL, nullable) to Absence. When is_paid=True, the auto-Bonus PayrollAdjustment inherits the project for cost-attribution. Form + admin + list + edit + log templates expose the field. List view filter now uses absence.project_id directly (was indirect via worker__work_logs). 5 new tests. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
99 lines
4.1 KiB
HTML
99 lines
4.1 KiB
HTML
{% extends 'base.html' %}
|
|
|
|
{% block title %}Edit Absence | FoxFitt{% endblock %}
|
|
|
|
{% block content %}
|
|
{% comment %}
|
|
=== EDIT ABSENCE PAGE ===
|
|
Single form for editing one Absence row. The is_paid checkbox is
|
|
the magic field — toggling it on creates a Bonus PayrollAdjustment
|
|
at the worker's daily rate; toggling it off deletes the adjustment
|
|
(UNLESS it's already been paid, in which case the view surfaces an
|
|
error).
|
|
|
|
Why two forms? HTML doesn't allow nested <form> tags. The delete
|
|
action needs its own form to POST to /absences/<id>/delete/. So
|
|
the delete form lives OUTSIDE the edit form (hidden), and the
|
|
Delete button inside the edit form uses the HTML5 `form="..."`
|
|
attribute to submit the delete form instead of its parent edit
|
|
form.
|
|
{% endcomment %}
|
|
<div class="container py-4">
|
|
<h1 class="page-title mb-3"><i class="fas fa-pen me-2"></i>Edit Absence</h1>
|
|
|
|
{% if messages %}
|
|
{% for m in messages %}<div class="alert alert-{{ m.tags }}">{{ m }}</div>{% endfor %}
|
|
{% endif %}
|
|
|
|
{% comment %}
|
|
Hidden sibling form so the Delete button can submit to its own URL.
|
|
Hidden via inline style — only the button inside the edit form is visible.
|
|
{% endcomment %}
|
|
<form method="post" action="{% url 'absence_delete' absence.id %}"
|
|
onsubmit="return confirm('Delete this absence?');"
|
|
id="absence-delete-form" style="display: none;">
|
|
{% csrf_token %}
|
|
</form>
|
|
|
|
<form method="post" class="card">
|
|
{% csrf_token %}
|
|
<div class="card-body p-3 p-md-4">
|
|
<div class="row g-3">
|
|
<div class="col-12 col-md-6">
|
|
<label class="form-label" for="{{ form.worker.id_for_label }}">Worker</label>
|
|
{{ form.worker }}
|
|
{{ form.worker.errors }}
|
|
</div>
|
|
<div class="col-12 col-md-6">
|
|
<label class="form-label" for="{{ form.date.id_for_label }}">Date</label>
|
|
{{ form.date }}
|
|
{{ form.date.errors }}
|
|
</div>
|
|
<div class="col-12 col-md-6">
|
|
<label class="form-label" for="{{ form.project.id_for_label }}">Project (optional)</label>
|
|
{{ form.project }}
|
|
{{ form.project.errors }}
|
|
<small class="text-muted">Which project was the worker absent from?</small>
|
|
</div>
|
|
<div class="col-12 col-md-6">
|
|
<label class="form-label" for="{{ form.reason.id_for_label }}">Reason</label>
|
|
{{ form.reason }}
|
|
{{ form.reason.errors }}
|
|
</div>
|
|
<div class="col-12 col-md-6 d-flex align-items-end">
|
|
<div class="form-check">
|
|
{{ form.is_paid }}
|
|
<label class="form-check-label" for="{{ form.is_paid.id_for_label }}">
|
|
Paid at daily rate
|
|
</label>
|
|
</div>
|
|
</div>
|
|
<div class="col-12">
|
|
<label class="form-label" for="{{ form.notes.id_for_label }}">Notes</label>
|
|
{{ form.notes }}
|
|
{{ form.notes.errors }}
|
|
</div>
|
|
</div>
|
|
|
|
{% if form.non_field_errors %}
|
|
<div class="alert alert-danger mt-3">{{ form.non_field_errors }}</div>
|
|
{% endif %}
|
|
|
|
<div class="d-flex justify-content-between mt-3">
|
|
{% comment %}
|
|
`form="absence-delete-form"` makes this button submit the
|
|
hidden delete form rather than its enclosing edit form.
|
|
{% endcomment %}
|
|
<button type="submit" form="absence-delete-form" class="btn btn-outline-danger">
|
|
<i class="fas fa-trash me-1"></i>Delete
|
|
</button>
|
|
<div>
|
|
<a href="{% url 'absence_list' %}" class="btn btn-outline-secondary">Cancel</a>
|
|
<button type="submit" class="btn btn-accent">Save</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
{% endblock %}
|