diff --git a/shiftregister/fallback/admin.py b/shiftregister/fallback/admin.py index bc59f6e..8c30c5b 100644 --- a/shiftregister/fallback/admin.py +++ b/shiftregister/fallback/admin.py @@ -25,6 +25,7 @@ class FallbackAssignmentInline(admin.TabularInline): model = FallbackAssignment ordering = ("shift__start_at",) readonly_fields = ("shift",) + fk_name = "team_member" @admin.register(TeamMember) diff --git a/shiftregister/fallback/migrations/0009_fallbackassignment_traded_to.py b/shiftregister/fallback/migrations/0009_fallbackassignment_traded_to.py new file mode 100644 index 0000000..5bd8b3d --- /dev/null +++ b/shiftregister/fallback/migrations/0009_fallbackassignment_traded_to.py @@ -0,0 +1,25 @@ +# Generated by Django 5.0.4 on 2025-05-14 11:39 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("fallback", "0008_alter_teammember_comment"), + ] + + operations = [ + migrations.AddField( + model_name="fallbackassignment", + name="traded_to", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="received_trades", + to="fallback.teammember", + ), + ), + ] diff --git a/shiftregister/fallback/models.py b/shiftregister/fallback/models.py index e1e050f..e6e69c7 100644 --- a/shiftregister/fallback/models.py +++ b/shiftregister/fallback/models.py @@ -21,18 +21,19 @@ class TeamMember(models.Model): id = models.IntegerField(default=generate_id, editable=False, primary_key=True) name = models.CharField(max_length=100) comment = models.CharField(max_length=100, blank=True, default="") - fallback_shifts = models.ManyToManyField(Shift, through="FallbackAssignment") + fallback_shifts = models.ManyToManyField( + Shift, through="FallbackAssignment", through_fields=("team_member", "shift") + ) def url(self): return "https://helfen.kntkt.de" + reverse( "my_fallback_shifts", - kwargs={ - "team_member_id": urlsafe_b64encode( - self.id.to_bytes(3, byteorder="big") - ).decode("utf-8") - }, + kwargs={"team_member_id": self.url_id()}, ) + def url_id(self): + return urlsafe_b64encode(self.id.to_bytes(3, byteorder="big")).decode("utf-8") + def assign_random_shifts(self): needs_fallback = Q(deleted=False, calendar__needs_fallback=True) @@ -199,6 +200,15 @@ class FallbackAssignment(models.Model): shift = models.ForeignKey(Shift, on_delete=models.CASCADE) team_member = models.ForeignKey(TeamMember, on_delete=models.CASCADE) was_full = models.BooleanField(default=False) + traded_to = models.ForeignKey( + TeamMember, + on_delete=models.CASCADE, + null=True, + blank=True, + related_name="received_trades", + ) def __str__(self): + if self.traded_to: + return f"{self.shift} {self.team_member.name} -> {self.traded_to.name}" return f"{self.shift} {self.team_member.name}" diff --git a/shiftregister/fallback/templates/my_fallback_shifts.html b/shiftregister/fallback/templates/my_fallback_shifts.html index d371307..29ad9d3 100644 --- a/shiftregister/fallback/templates/my_fallback_shifts.html +++ b/shiftregister/fallback/templates/my_fallback_shifts.html @@ -9,6 +9,22 @@
+ +Hallo {{ team_member.name }}, hier deine Teamschichten für das Festival: @@ -26,6 +42,7 @@ Diese Schichtzuteilung wurde maschinell erstellt und ist auch ohne Unterschrift
Tausch-ID | Wann | Wie lange | Wo | @@ -38,13 +55,19 @@ Diese Schichtzuteilung wurde maschinell erstellt und ist auch ohne Unterschrift {% for assignment in assignments %} {% with assignment.shift as shift %}|||
---|---|---|---|---|---|---|
{{ assignment.id }} {% if assignment.traded_to %}*{% endif %} | {{ shift.start_at }} | {{ shift.duration|duration }} | {{ shift.room.name }} | {{ shift.registration_count }}/{{ shift.required_helpers|default:shift.room.required_helpers }} | -{% for assignment in shift.fallbackassignment_set.all %} - {{ assignment.team_member.name }}{% if not forloop.last %}, {% endif %} +{% for fa in shift.fallbackassignment_set.all %} + {% if fa.traded_to %} + {{ fa.traded_to.name }} + {% else %} + {{ fa.team_member.name }} + {% endif %} + {% if not forloop.last %}, {% endif %} {% endfor %} | @@ -61,7 +84,7 @@ Diese Schichtzuteilung wurde maschinell erstellt und ist auch ohne Unterschrift {% if user.is_authenticated %} {% else %} Noch keine Schichten zugewiesen, bitte wende dich an den Infopoint. diff --git a/shiftregister/fallback/views.py b/shiftregister/fallback/views.py index 4ce94eb..52ef2f7 100644 --- a/shiftregister/fallback/views.py +++ b/shiftregister/fallback/views.py @@ -1,11 +1,13 @@ from base64 import urlsafe_b64decode +from django.contrib import messages from django.contrib.auth.decorators import login_required -from django.db.models import Count +from django.db.models import Count, Q from django.http import HttpResponse -from django.shortcuts import get_object_or_404, render +from django.shortcuts import get_object_or_404, redirect, render +from django.urls import reverse -from shiftregister.fallback.models import TeamMember +from shiftregister.fallback.models import FallbackAssignment, TeamMember # Create your views here. @@ -22,15 +24,44 @@ def my_fallback_shifts(request, team_member_id): is_draw = False if request.method == "POST": - team_member.assign_random_shifts() - is_draw = True + if "draw_shifts" in request.POST: + team_member.assign_random_shifts() + is_draw = True + elif "take_shift" in request.POST: + assignment_id = request.POST.get("assignment_id") + try: + assignment = FallbackAssignment.objects.get(pk=assignment_id) + if assignment.team_member == team_member: + assignment.traded_to = None + messages.success(request, f"Schicht erfolgreich zurückgenommen") + elif assignment.traded_to == team_member: + assignment.traded_to = None + messages.success(request, f"Schicht erfolgreich zurückgegeben") + else: + assignment.traded_to = team_member + messages.success(request, f"Schicht erfolgreich übernommen") + assignment.save() + return redirect( + reverse( + "my_fallback_shifts", + kwargs={"team_member_id": team_member.url_id()}, + ) + ) + except FallbackAssignment.DoesNotExist: + messages.error(request, "Ungültige Schicht-ID") + + assignments = ( + FallbackAssignment.objects.filter( + Q(team_member=team_member, traded_to__isnull=True) + | Q(traded_to=team_member) + ) + .prefetch_related("shift", "traded_to", "team_member") + .order_by("shift__start_at") + ) context = { "team_member": team_member, - "assignments": team_member.fallbackassignment_set.order_by( - "shift__start_at" - ).all(), - # "shifts": team_member.fallback_shifts.order_by("start_at").all(), + "assignments": assignments, "is_draw": is_draw, } return render(request, "my_fallback_shifts.html", context) diff --git a/shiftregister/signage/templates/team_dashboard.html b/shiftregister/signage/templates/team_dashboard.html index 239884b..d4d325b 100644 --- a/shiftregister/signage/templates/team_dashboard.html +++ b/shiftregister/signage/templates/team_dashboard.html @@ -18,7 +18,18 @@ |
{{ shift.room.name }} | {{ shift.start_at }} | -{% for fa in shift.fallbackassignment_set.all %}{% if not fa.was_full %}{{ fa.team_member.name }}{% if not forloop.last %}, {% endif %}{% endif %}{% endfor %} | ++ {% for fa in shift.fallbackassignment_set.all %} + {% if not fa.was_full %} + {% if fa.traded_to %} + {{ fa.traded_to.name }} ({{ fa.id }}) + {% else %} + {{ fa.team_member.name }} ({{ fa.id }}) + {% endif %} + {% if not forloop.last %}, {% endif %} + {% endif %} + {% endfor %} + | |||