From ff475c2b87a933746fcdb759b21d3ff9ac875ebd Mon Sep 17 00:00:00 2001 From: Sli Date: Sun, 22 Dec 2024 01:56:57 +0100 Subject: [PATCH] Pre-filter allowed products in backend for counter click --- counter/models.py | 7 --- counter/views/click.py | 102 ++++++++++++++++++++++------------------- 2 files changed, 55 insertions(+), 54 deletions(-) diff --git a/counter/models.py b/counter/models.py index b290d0a0..d5f9c9c7 100644 --- a/counter/models.py +++ b/counter/models.py @@ -428,13 +428,6 @@ class Product(models.Model): def profit(self): return self.selling_price - self.purchase_price - def get_actual_price(self, counter: Counter, customer: Customer): - """Return the price of the article taking into account if the customer has a special price - or not in the counter it's being purchased on""" - if counter.customer_is_barman(customer): - return self.special_selling_price - return self.selling_price - class CounterQuerySet(models.QuerySet): def annotate_has_barman(self, user: User) -> Self: diff --git a/counter/views/click.py b/counter/views/click.py index 75f0fcd9..38d4ce57 100644 --- a/counter/views/click.py +++ b/counter/views/click.py @@ -53,13 +53,15 @@ class ProductForm(Form): def __init__( self, + customer: Customer, + counter: Counter, + allowed_products: list[Product], *args, - customer: Customer | None = None, - counter: Counter | None = None, **kwargs, ): - self.customer = customer - self.counter = counter + self.customer = customer # Used by formset + self.counter = counter # Used by formset + self.allowed_products = allowed_products super().__init__(*args, **kwargs) def clean(self): @@ -67,45 +69,27 @@ class ProductForm(Form): if len(self.errors) > 0: return - if self.customer is None or self.counter is None: - raise RuntimeError( - f"{self} has been initialized without customer or counter" - ) - - user = self.customer.user - # We store self.product so we can use it later on the formset validation - self.product = self.counter.products.filter(id=cleaned_data["id"]).first() + self.product = next( + ( + product + for product in self.allowed_products + if product.id == cleaned_data["id"] + ), + None, + ) if self.product is None: raise ValidationError( - _( - "Product %(product)s doesn't exist or isn't available on this counter" - ) - % {"product": cleaned_data["id"]} + _("The selected product isn't available for this user") ) - # Test alcohoolic products - if self.product.limit_age >= 18: - if not user.date_of_birth: - raise ValidationError(_("Too young for that product")) - if user.is_banned_alcohol: - raise ValidationError(_("Not allowed for that product")) - if user.date_of_birth and self.customer.user.get_age() < self.product.limit_age: - raise ValidationError(_("Too young for that product")) - - if user.is_banned_counter: - raise ValidationError(_("Not allowed for that product")) - # Compute prices cleaned_data["bonus_quantity"] = 0 if self.product.tray: cleaned_data["bonus_quantity"] = math.floor( cleaned_data["quantity"] / Product.QUANTITY_FOR_TRAY_PRICE ) - cleaned_data["unit_price"] = self.product.get_actual_price( - self.counter, self.customer - ) - cleaned_data["total_price"] = cleaned_data["unit_price"] * ( + cleaned_data["total_price"] = self.product.price * ( cleaned_data["quantity"] - cleaned_data["bonus_quantity"] ) @@ -164,27 +148,62 @@ class CounterClick(CounterTabsMixin, CanViewMixin, SingleObjectMixin, FormView): pk_url_kwarg = "counter_id" current_tab = "counter" + def get_products(self) -> list[Product]: + """Get all allowed products for the current customer on the current counter""" + + if hasattr(self, "_products"): + return self._products + + products = self.object.products.select_related("product_type").prefetch_related( + "buying_groups" + ) + + # Only include allowed products + if not self.customer.user.date_of_birth or self.customer.user.is_banned_alcohol: + products = products.filter(limit_age__lt=18) + else: + products = products.filter(limit_age__lte=self.customer.user.get_age()) + + # Compute special price for customer if he is a barmen on that bar + if self.object.customer_is_barman(self.customer): + products = products.annotate(price=F("special_selling_price")) + else: + products = products.annotate(price=F("selling_price")) + + self._products = [ + product + for product in products.all() + if product.can_be_sold_to(self.customer.user) + ] + + return self._products + def get_form_kwargs(self): kwargs = super().get_form_kwargs() kwargs["form_kwargs"] = { "customer": self.customer, "counter": self.object, + "allowed_products": self.get_products(), } return kwargs def dispatch(self, request, *args, **kwargs): self.customer = get_object_or_404(Customer, user__id=self.kwargs["user_id"]) obj: Counter = self.get_object() - if not self.customer.can_buy: - raise Http404 + + if not self.customer.can_buy or self.customer.user.is_banned_counter: + return redirect(obj) # Redirect to counter + if obj.type != "BAR" and not request.user.is_authenticated: raise PermissionDenied + if obj.type == "BAR" and ( "counter_token" not in request.session or request.session["counter_token"] != obj.token or len(obj.barmen_list) == 0 ): return redirect(obj) # Redirect to counter + return super().dispatch(request, *args, **kwargs) def form_valid(self, formset): @@ -207,7 +226,7 @@ class CounterClick(CounterTabsMixin, CanViewMixin, SingleObjectMixin, FormView): product=form.product, club=form.product.club, counter=self.object, - unit_price=form.cleaned_data["unit_price"], + unit_price=form.product.price, quantity=form.cleaned_data["quantity"] - form.cleaned_data["bonus_quantity"], seller=operator, @@ -238,21 +257,10 @@ class CounterClick(CounterTabsMixin, CanViewMixin, SingleObjectMixin, FormView): def get_success_url(self): return resolve_url(self.object) - def get_product(self, pid): - return Product.objects.filter(pk=int(pid)).first() - def get_context_data(self, **kwargs): """Add customer to the context.""" kwargs = super().get_context_data(**kwargs) - products = self.object.products.select_related("product_type") - - # Optimisation to bulk edit prices instead of `calling get_actual_price` on everything - if self.object.customer_is_barman(self.customer): - products = products.annotate(price=F("special_selling_price")) - else: - products = products.annotate(price=F("selling_price")) - - kwargs["products"] = products + kwargs["products"] = self.get_products() kwargs["categories"] = {} for product in kwargs["products"]: if product.product_type: