diff --git a/djangorota/djangorota/templates/rota/partials/leave_list.html b/djangorota/djangorota/templates/rota/partials/leave_list.html
index c5959d7..b88ceab 100644
--- a/djangorota/djangorota/templates/rota/partials/leave_list.html
+++ b/djangorota/djangorota/templates/rota/partials/leave_list.html
@@ -2,7 +2,14 @@
{% for l in leaves %}
- - {{ l.start_date }} → {{ l.end_date }}{% if l.reason %} ({{ l.reason }}){% endif %}
+ -
+ {{ l.start_date }} → {{ l.end_date }}{% if l.reason %} ({{ l.reason }}){% endif %}
+
+
{% empty %}
- No leave recorded
{% endfor %}
diff --git a/djangorota/rota/urls.py b/djangorota/rota/urls.py
index c1e5b79..d844929 100644
--- a/djangorota/rota/urls.py
+++ b/djangorota/rota/urls.py
@@ -19,6 +19,7 @@ urlpatterns = [
path("worker//leaves.json", views.leaves_json, name="worker_leaves_json"),
path("leave/request/", views.request_leave, name="request_leave"),
path("leave/request/token//", views.request_leave, name="request_leave_token"),
+ path("leave//delete/", views.leave_delete, name="leave_delete"),
path("worker//regenerate_token/", views.regenerate_worker_token, name="regenerate_worker_token"),
path("run//", views.rota_run_detail, name="rota_run_detail"),
path("run//export/html/", views.rota_run_export_html, name="rota_run_export_html"),
diff --git a/djangorota/rota/views.py b/djangorota/rota/views.py
index 1036d52..2e23373 100644
--- a/djangorota/rota/views.py
+++ b/djangorota/rota/views.py
@@ -512,7 +512,7 @@ def request_leave(request, token=None):
# Render updated leave list and return OOB swaps: update leave-list and clear modal.
leave_list_html = render_to_string(
"rota/partials/leave_list.html",
- {"leaves": worker.leaves.all()},
+ {"leaves": worker.leaves.all(), "token": token},
request=request,
)
resp = (
@@ -563,7 +563,7 @@ def request_leave(request, token=None):
)
return HttpResponse(modal_html)
- return render(request, "rota/leave_request.html", {"form": form, "worker": worker})
+ return render(request, "rota/leave_request.html", {"form": form, "worker": worker, "token": token})
def rota_options(request, rota_id):
@@ -952,6 +952,51 @@ def leaves_json(request, worker_id):
return JsonResponse({'leaves': data})
+@require_POST
+def leave_delete(request, leave_id):
+ """Delete a Leave record. Allowed if the requester is staff, or if a valid token matches the worker, or the authenticated user's email matches the worker email."""
+ from .models import Leave
+
+ leave = get_object_or_404(Leave, pk=leave_id)
+ worker = leave.worker
+
+ # permission checks
+ allowed = False
+ token = request.POST.get('token') or request.GET.get('token')
+ try:
+ if request.user and request.user.is_authenticated and request.user.is_staff:
+ allowed = True
+ elif request.user and request.user.is_authenticated and getattr(request.user, 'email', None) and request.user.email == (worker.email or ''):
+ allowed = True
+ elif token:
+ try:
+ import uuid
+ token_uuid = uuid.UUID(token)
+ if worker.request_token == token_uuid:
+ allowed = True
+ except Exception:
+ allowed = False
+ except Exception:
+ allowed = False
+
+ if not allowed:
+ return HttpResponseBadRequest('Permission denied')
+
+ # perform delete
+ leave.delete()
+
+ # render updated leave list for the worker
+ leave_list_html = render_to_string(
+ "rota/partials/leave_list.html",
+ {"leaves": worker.leaves.all(), "token": token},
+ request=request,
+ )
+ resp = f'{leave_list_html}
'
+ response = HttpResponse(resp)
+ response["HX-Trigger"] = "leaveUpdated"
+ return response
+
+
@require_POST
def rota_run_export_regenerate(request, run_id):
"""Regenerate the HTML export for a completed run and persist it on the RotaRun.