avoid overlapping shift registrations for helpers

This commit is contained in:
xAndy 2025-05-13 12:39:37 +02:00
parent 7e4ff4366b
commit 8a194f3fc7
3 changed files with 53 additions and 0 deletions

View File

@ -97,6 +97,40 @@ class Helper(models.Model):
token.send() token.send()
return token return token
def has_overlapping_shift(self, shift):
new_shift_end = shift.start_at + shift.duration
return (
ShiftRegistration.objects.annotate(
shift_end=ExpressionWrapper(
F("shift__start_at") + F("shift__duration"),
output_field=models.DateTimeField(),
)
)
.filter(
helper=self,
shift__deleted=False,
state__in=[
ShiftRegistration.RegState.REGISTERED,
ShiftRegistration.RegState.CHECKED_IN,
],
)
.filter(
# Case 1: Start time falls between new shift's start and end
Q(
shift__start_at__gte=shift.start_at,
shift__start_at__lt=new_shift_end,
)
|
# Case 2: End time falls between new shift's start and end
Q(shift_end__gt=shift.start_at, shift_end__lte=new_shift_end)
|
# Case 3: Completely encompasses the new shift
Q(shift__start_at__lte=shift.start_at, shift_end__gte=new_shift_end)
)
.first()
)
# current or next shift # current or next shift
def important_shift(self): def important_shift(self):
ret = ( ret = (

View File

@ -13,7 +13,11 @@
<div class="notification">Diese Schicht wurde gelöscht.</div> <div class="notification">Diese Schicht wurde gelöscht.</div>
{% endif %} {% endif %}
{% if not can_register and not is_registered %} {% if not can_register and not is_registered %}
{% if has_overlap %}
<div class="notification is-warning">Du hast bereits eine überlappende Schicht zu dieser Zeit: <a href="{% url 'shift' overlapping_shift.id %}">{{ overlapping_shift.room.name }} ({{ overlapping_shift.start_at }})</a></div>
{% else %}
<div class="notification">Diese Schicht ist bereits besetzt.</div> <div class="notification">Diese Schicht ist bereits besetzt.</div>
{% endif %}
{% endif %} {% endif %}
<div class="content"> <div class="content">
<p> <p>

View File

@ -211,6 +211,13 @@ def shift(request, shiftid):
context["can_register"] = False context["can_register"] = False
if reg[0].can_cancel(): if reg[0].can_cancel():
context["can_cancel"] = True context["can_cancel"] = True
elif context["can_register"]:
# Check for overlapping shifts
overlapping_reg = helper.has_overlapping_shift(shift)
if overlapping_reg:
context["can_register"] = False
context["has_overlap"] = True
context["overlapping_shift"] = overlapping_reg.shift
if request.method == "POST": if request.method == "POST":
if EmptyForm(request.POST).is_valid(): if EmptyForm(request.POST).is_valid():
@ -236,6 +243,14 @@ def shift(request, shiftid):
) )
return redirect("index") return redirect("index")
if context["can_register"]: if context["can_register"]:
overlapping_reg = helper.has_overlapping_shift(shift)
if overlapping_reg:
messages.add_message(
request,
messages.ERROR,
"Du hast bereits eine überlappende Schicht zu dieser Zeit.",
)
return redirect("shift", shiftid=shift.pk)
s = ShiftRegistration(helper=helper, shift=shift) s = ShiftRegistration(helper=helper, shift=shift)
s.save() s.save()
messages.add_message( messages.add_message(