From 62900b8c2ec802c847bc504f3fc438f014c551df Mon Sep 17 00:00:00 2001 From: imperosol Date: Thu, 14 May 2026 12:28:59 +0200 Subject: [PATCH] exclude products over clic limit from eboutic --- core/static/core/components/card.scss | 7 ++++++- core/static/core/forms.scss | 2 +- counter/models.py | 14 +++----------- counter/tests/test_counter.py | 2 +- counter/views/click.py | 2 +- eboutic/templates/eboutic/eboutic_main.jinja | 6 +++++- eboutic/views.py | 17 ++++++++++++----- 7 files changed, 29 insertions(+), 21 deletions(-) diff --git a/core/static/core/components/card.scss b/core/static/core/components/card.scss index 941b32a5..2fe346a2 100644 --- a/core/static/core/components/card.scss +++ b/core/static/core/components/card.scss @@ -29,7 +29,12 @@ align-items: center; gap: 20px; - &.clickable:hover { + &:disabled { + background-color: darken($primary-neutral-light-color, 5%); + opacity: 65%; + } + + &.clickable:not(:disabled):hover { background-color: darken($primary-neutral-light-color, 5%); } diff --git a/core/static/core/forms.scss b/core/static/core/forms.scss index 2abffbba..0982b3d0 100644 --- a/core/static/core/forms.scss +++ b/core/static/core/forms.scss @@ -23,7 +23,7 @@ border-radius: 5px; color: black; - &:hover { + &:not(.link-like):not(:disabled):hover { background: hsl(0, 0%, 83%); } } diff --git a/counter/models.py b/counter/models.py index 889a47e4..2a2dcf09 100644 --- a/counter/models.py +++ b/counter/models.py @@ -22,7 +22,7 @@ import string from datetime import date, datetime, timedelta from datetime import timezone as tz from decimal import Decimal -from typing import TYPE_CHECKING, Literal, Self +from typing import Literal, Self from dict2xml import dict2xml from django.conf import settings @@ -48,9 +48,6 @@ from core.utils import get_start_of_semester from counter.fields import CurrencyField from subscription.models import Subscription -if TYPE_CHECKING: - from collections.abc import Sequence - def get_eboutic() -> Counter: return Counter.objects.filter(type="EBOUTIC").order_by("id").first() @@ -773,10 +770,8 @@ class Counter(models.Model): # but they share the same primary key return self.type == "BAR" and any(b.pk == customer.pk for b in self.barmen_list) - def get_prices_for( - self, customer: Customer, *, order_by: Sequence[str] | None = None - ) -> list[Price]: - qs = ( + def get_prices_for(self, customer: Customer) -> PriceQuerySet: + return ( Price.objects.filter( product__counters=self, product__product_type__isnull=False ) @@ -784,9 +779,6 @@ class Counter(models.Model): .select_related("product", "product__product_type") .prefetch_related("groups") ) - if order_by: - qs = qs.order_by(*order_by) - return list(qs) class CounterSellers(models.Model): diff --git a/counter/tests/test_counter.py b/counter/tests/test_counter.py index b5a34e22..c63fb76b 100644 --- a/counter/tests/test_counter.py +++ b/counter/tests/test_counter.py @@ -596,7 +596,7 @@ class TestCounterClick(TestFullClickBase): product=iter(_product_recipe.make(archived=False, _quantity=2)), groups=[group], ) - customer_prices = counter.get_prices_for(customer) + customer_prices = list(counter.get_prices_for(customer)) assert unarchived_prices == customer_prices diff --git a/counter/views/click.py b/counter/views/click.py index b23e65c8..5ec11b57 100644 --- a/counter/views/click.py +++ b/counter/views/click.py @@ -103,7 +103,7 @@ class CounterClick( ): return redirect(obj) # Redirect to counter - self.prices = obj.get_prices_for(self.customer) + self.prices = list(obj.get_prices_for(self.customer)) return super().dispatch(request, *args, **kwargs) diff --git a/eboutic/templates/eboutic/eboutic_main.jinja b/eboutic/templates/eboutic/eboutic_main.jinja index 51945259..869c79b1 100644 --- a/eboutic/templates/eboutic/eboutic_main.jinja +++ b/eboutic/templates/eboutic/eboutic_main.jinja @@ -187,9 +187,10 @@ {% for price in prices %} diff --git a/eboutic/views.py b/eboutic/views.py index c892a104..c2d6a08c 100644 --- a/eboutic/views.py +++ b/eboutic/views.py @@ -33,7 +33,7 @@ from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.messages.views import SuccessMessageMixin from django.core.exceptions import SuspiciousOperation, ValidationError from django.db import DatabaseError, transaction -from django.db.models import Subquery +from django.db.models import Exists, OuterRef, Subquery from django.db.models.fields import forms from django.db.utils import cached_property from django.http import HttpResponse @@ -92,7 +92,9 @@ class EbouticMainView(LoginRequiredMixin, FormView): kwargs["form_kwargs"] = { "customer": self.customer, "counter": get_eboutic(), - "allowed_prices": {price.id: price for price in self.prices}, + "allowed_prices": { + price.id: price for price in self.prices if not price.sold_out + }, } return kwargs @@ -118,9 +120,14 @@ class EbouticMainView(LoginRequiredMixin, FormView): @cached_property def prices(self) -> list[Price]: - return get_eboutic().get_prices_for( - self.customer, - order_by=["product__product_type__order", "product_id", "amount"], + eboutic = get_eboutic() + sold_out_subquery = ~Exists( + eboutic.products.under_clic_limit().filter(id=OuterRef("product_id")) + ) + return list( + eboutic.get_prices_for(self.customer) + .annotate(sold_out=sold_out_subquery) + .order_by("product__product_type__order", "product_id", "amount") ) @cached_property