diff --git a/migrations/0009_alter_wish_options_wish_depends_on.py b/migrations/0009_alter_wish_options_wish_depends_on.py new file mode 100644 index 0000000000000000000000000000000000000000..278f14b3ec3aaf41177a79fa857d1c293ca62477 --- /dev/null +++ b/migrations/0009_alter_wish_options_wish_depends_on.py @@ -0,0 +1,23 @@ +# Generated by Django 5.0.6 on 2024-08-06 14:11 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ('wishlist', '0008_wish_favorite'), + ] + + operations = [ + migrations.AlterModelOptions( + name='wish', + options={'ordering': ('-favorite',)}, + ), + migrations.AddField( + model_name='wish', + name='depends_on', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, + related_name='dependent_wishes', to='wishlist.wish'), + ), + ] diff --git a/models.py b/models.py index 150314884126d78e5f89532b14e05cdf59e53076..cab0abafe6ca031e1a4c1846eb7013bdceed7fc6 100644 --- a/models.py +++ b/models.py @@ -19,11 +19,16 @@ class Wish(models.Model): User, related_name="reserved_wishes", through="Reservation" ) favorite = models.BooleanField(default=False) + depends_on = models.ForeignKey('Wish', on_delete=models.SET_NULL, related_name="dependent_wishes", null=True, + blank=True) class Meta: ordering = ("-favorite",) def is_reservation_possible(self): + if self.depends_on is not None and len(self.depends_on.reserved_by.all()) == 0: + return False + reserved = (Reservation.objects.filter(wish=self).aggregate(Sum("quantity")))[ "quantity__sum" ] @@ -31,6 +36,9 @@ class Wish(models.Model): return reserved < self.quantity return True + def __str__(self): + return self.text + class Group(models.Model): name = models.TextField(max_length=100) diff --git a/templates/wishlist/create-wish.html b/templates/wishlist/create-wish.html index 2f63d7e25596266c53eef8973c571f3a8acdbc34..60cbbec250d16194600ea4aca4116537380cdd3f 100644 --- a/templates/wishlist/create-wish.html +++ b/templates/wishlist/create-wish.html @@ -29,11 +29,19 @@ </label> {{ form.quantity }} </div> + <div class="form-control w-full max-w-xs"> + <label class="label" for="{{ form.depends_on.id_for_label }}"> + <span class="label-text">{{ form.depends_on.label_tag }}</span> + <span class="label-text-alt">{{ form.depends_on.errors }}</span> + </label> + {{ form.depends_on }} + </div> <div class="card-actions justify-end mt-4"> <button class="btn btn-primary" type="submit">Hinzufügen</button> </div> </div> </form> + <div class="hidden select select-bordered w-full max-w-xs"></div> </div> </div> </div> diff --git a/templates/wishlist/edit-wish.html b/templates/wishlist/edit-wish.html index bb629d86e7d8ce72a82ffaea15500628c61612ea..006afd5e961f87b49d0d027967c518547ee15320 100644 --- a/templates/wishlist/edit-wish.html +++ b/templates/wishlist/edit-wish.html @@ -29,6 +29,13 @@ </label> {{ form.quantity }} </div> + <div class="form-control w-full max-w-xs"> + <label class="label" for="{{ form.depends_on.id_for_label }}"> + <span class="label-text">{{ form.depends_on.label_tag }}</span> + <span class="label-text-alt">{{ form.depends_on.errors }}</span> + </label> + {{ form.depends_on }} + </div> <div class="card-actions justify-end mt-4"> <button class="btn btn-primary" type="submit">Speichern</button> </div> diff --git a/templates/wishlist/own-wish-list.html b/templates/wishlist/own-wish-list.html index 6fc0591a55a4bba85ac2982de3798bec79e1f4e5..aeb625974aed2024832552ce589d294460a04921 100644 --- a/templates/wishlist/own-wish-list.html +++ b/templates/wishlist/own-wish-list.html @@ -20,74 +20,7 @@ <a href="{% url 'createWish' list_owner.id %}" class="btn w-full mb-4 lg:hidden">Neuen Wunsch hinzufügen</a> <div> {% for wish in wishes %} - <div class="card shadow-lg compact"> - <div class="card-body"> - <h2 class="card-title w-full flex flex-row justify-between"> - <span> - {{ wish.text }} - {% if wish.wish_for != user and wish.reserved_by.all|length > 0 %} - <div class="badge ml-2"> - Reserviert von - {% for reservation in wish.reservation_set.all %} - {% if wish.quantity > 1 or reservation.quantity > wish.quantity %} - {{ reservation.quantity }}x - {% endif %} - {{ reservation.user }}{% if not forloop.last %}, {% endif %} - {% endfor %} - </div> - {% endif %} - </span> - <a {% if list_owner == user %}href="{% url 'toggleFavorite' wish.id %}"{% endif %} - class="text-4xl"> - {% if wish.favorite %}★{% elif list_owner == user %}☆{% endif %} - </a> - </h2> - {% if wish.quantity > 1 %} - <p> - <span class="italic font-bold">Anzahl: {{ wish.quantity }}</span> - </p> - {% endif %} - {% if wish.owner != list_owner %} - <p> - <span class="italic">Vorschlag von {{ wish.owner }}</span> - <span class="tooltip tooltip-bottom inline-flex" - data-tip="{{ list_owner }} kann diesen Vorschlag nicht sehen. Für weitere Fragen bitte an {{ wish.owner }} wenden."> - <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" - class="w-4 h-4 stroke-current"> - <path stroke-linecap="round" - stroke-linejoin="round" - stroke-width="2" - d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path> - </svg> - ️ - </span> - </p> - {% endif %} - {% if wish.link %} - <div> - <a class="link" href="{{ wish.link }}" target="_blank" - rel="noopener noreferrer">Link</a> - </div> - {% endif %} - <div class="card-actions"> - {% if wish.owner == user %} - <a class="btn btn-sm" - href="{% url 'deleteWish' wish.id %}?list_owner={{ list_owner.id }}">Löschen</a> - <a class="btn btn-sm" - href="{% url 'editWish' wish.id %}?list_owner={{ list_owner.id }}">Bearbeiten</a> - {% endif %} - {% if wish.wish_for != user and wish.is_reservation_possible %} - <a class="btn btn-sm" - href="{% url 'reserveWish' wish.id %}?list_owner={{ list_owner.id }}">Reservieren</a>{% endif %} - {% if wish.wish_for != user and user in wish.reserved_by.all %} - <a class="btn btn-sm" - href="{% url 'cancelReserveWish' wish.id %}?list_owner={{ list_owner.id }}"> - Nicht mehr reservieren - </a> - {% endif %} - </div> - </div> - </div> + {% include "wishlist/wish_template.html" with wish=wish %} {% empty %} <div class="alert alert-success shadow-lg"> <svg xmlns="http://www.w3.org/2000/svg" class="stroke-current shrink-0 h-6 w-6" fill="none" diff --git a/templates/wishlist/wish_template.html b/templates/wishlist/wish_template.html new file mode 100644 index 0000000000000000000000000000000000000000..d06619d1acf22047ddde8c4c45d715e3799b8745 --- /dev/null +++ b/templates/wishlist/wish_template.html @@ -0,0 +1,79 @@ +<div class="card shadow-lg compact"> + <div class="card-body"> + <h2 class="card-title w-full flex flex-row justify-between"> + <span> + {{ wish.text }} + {% if wish.wish_for != user and wish.reserved_by.all|length > 0 %} + <div class="badge ml-2"> + Reserviert von + {% for reservation in wish.reservation_set.all %} + {% if wish.quantity > 1 or reservation.quantity > wish.quantity %} + {{ reservation.quantity }}x + {% endif %} + {{ reservation.user }}{% if not forloop.last %}, {% endif %} + {% endfor %} + </div> + {% endif %} + </span> + <a {% if list_owner == user %}href="{% url 'toggleFavorite' wish.id %}"{% endif %} + class="text-4xl"> + {% if wish.favorite %}★{% elif list_owner == user %}☆{% endif %} + </a> + </h2> + {% if wish.quantity > 1 %} + <p> + <span class="italic font-bold">Anzahl: {{ wish.quantity }}</span> + </p> + {% endif %} + {% if wish.owner != list_owner %} + <p> + <span class="italic">Vorschlag von {{ wish.owner }}</span> + <span class="tooltip tooltip-bottom inline-flex" + data-tip="{{ list_owner }} kann diesen Vorschlag nicht sehen. Für weitere Fragen bitte an {{ wish.owner }} wenden."> + <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" + class="w-4 h-4 stroke-current"> + <path stroke-linecap="round" + stroke-linejoin="round" + stroke-width="2" + d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path> + </svg> + ️ + </span> + </p> + {% endif %} + {% if wish.link %} + <div> + <a class="link" href="{{ wish.link }}" target="_blank" + rel="noopener noreferrer">Link</a> + </div> + {% endif %} + <div class="card-actions"> + {% if wish.owner == user %} + <a class="btn btn-sm" + href="{% url 'deleteWish' wish.id %}?list_owner={{ list_owner.id }}">Löschen</a> + <a class="btn btn-sm" + href="{% url 'editWish' wish.id %}?list_owner={{ list_owner.id }}">Bearbeiten</a> + {% endif %} + {% if wish.wish_for != user and wish.is_reservation_possible %} + <a class="btn btn-sm" + href="{% url 'reserveWish' wish.id %}?list_owner={{ list_owner.id }}">Reservieren</a>{% endif %} + {% if wish.wish_for != user and user in wish.reserved_by.all %} + <a class="btn btn-sm" + href="{% url 'cancelReserveWish' wish.id %}?list_owner={{ list_owner.id }}"> + Nicht mehr reservieren + </a> + {% endif %} + </div> + {% if wish.dependent_wishes.all %} + <div class="collapse collapse-arrow {% if wish.reserved_by.all|length > 0 %}collapse-open{% endif %}"> + <input type="checkbox" /> + <div class="collapse-title text-xl font-medium">Abhängige Wünsche</div> + <div class="collapse-content"> + {% for dep_wish in wish.dependent_wishes.all %} + {% include "wishlist/wish_template.html" with wish=dep_wish %} + {% endfor %} + </div> + </div> + {% endif %} + </div> +</div> diff --git a/views.py b/views.py index a6fb93bb93ec2c11f9052b386292ee9025226f29..f285ed19a70541b91339420da55d6d4d06c2df17 100644 --- a/views.py +++ b/views.py @@ -45,9 +45,9 @@ def wishListView(request: HttpRequest, list_owner): raise PermissionDenied() # Get all requests except those that are from others for the user if list_owner == request.user: - wishes = Wish.objects.filter(wish_for=list_owner).filter(owner=request.user) + wishes = Wish.objects.filter(wish_for=list_owner).filter(owner=request.user).filter(depends_on__isnull=True) else: - wishes = Wish.objects.filter(wish_for=list_owner) + wishes = Wish.objects.filter(wish_for=list_owner).filter(depends_on__isnull=True) return render( request, @@ -61,7 +61,7 @@ class CreateWishView(LoginRequiredMixin, CreateView): template_name = "wishlist/create-wish.html" form_class = modelform_factory( Wish, - fields=("text", "link", "quantity"), + fields=("text", "link", "quantity", "depends_on"), labels={"text": "Wunsch", "link": "Link", "quantity": "Anzahl"}, widgets={ "text": django.forms.TextInput( @@ -73,9 +73,15 @@ class CreateWishView(LoginRequiredMixin, CreateView): "quantity": django.forms.NumberInput( attrs={"class": "input input-bordered w-full max-w-xs", "min": 1} ), + "depends_on": django.forms.Select(attrs={"class": "select select-bordered w-full max-w-xs"}) }, ) + def get_form(self, form_class=None): + form = super().get_form(form_class) + form.fields['depends_on'].queryset = Wish.objects.filter(wish_for_id=self.kwargs.get("list_owner")) + return form + def form_valid(self, form): list_owner = self.kwargs["list_owner"] if form.is_valid(): @@ -88,6 +94,7 @@ class CreateWishView(LoginRequiredMixin, CreateView): quantity=data.get("quantity"), owner=self.request.user, wish_for=list_owner, + depends_on=data.get("depends_on") ) wish.save() if self.request.user != list_owner: @@ -172,7 +179,7 @@ class EditWishView(LoginRequiredMixin, UpdateView): template_name = "wishlist/edit-wish.html" form_class = modelform_factory( Wish, - fields=("text", "link", "quantity"), + fields=("text", "link", "quantity", "depends_on"), labels={"text": "Wunsch", "link": "Link", "quantity": "Anzahl"}, widgets={ "text": django.forms.TextInput( @@ -184,9 +191,15 @@ class EditWishView(LoginRequiredMixin, UpdateView): "quantity": django.forms.NumberInput( attrs={"class": "input input-bordered w-full max-w-xs", "min": 1} ), + "depends_on": django.forms.Select(attrs={"class": "select select-bordered w-full max-w-xs"}) }, ) + def get_form(self, form_class=None): + form = super().get_form(form_class) + form.fields['depends_on'].queryset = Wish.objects.filter(wish_for_id=self.request.GET.get("list_owner")) + return form + def get_context_data(self, *args, **kwargs): context = super().get_context_data(**kwargs) context["all_users"] = get_all_users_filtered(self.request)