From 7bc980113a669a5fe5a2a89b33410471daa08316 Mon Sep 17 00:00:00 2001 From: imperosol Date: Fri, 30 Oct 2026 14:38:00 +0100 Subject: [PATCH] allow `is_logged_in_counter` to target a specific counter --- counter/tests/test_counter.py | 8 +++-- counter/utils.py | 8 +++-- counter/views/admin.py | 61 ++++++++++++++--------------------- counter/views/click.py | 10 ++---- counter/views/home.py | 17 +++++----- counter/views/mixins.py | 8 +++-- 6 files changed, 53 insertions(+), 59 deletions(-) diff --git a/counter/tests/test_counter.py b/counter/tests/test_counter.py index 83b65021..4492b88d 100644 --- a/counter/tests/test_counter.py +++ b/counter/tests/test_counter.py @@ -21,7 +21,8 @@ import pytest from bs4 import BeautifulSoup from dateutil.relativedelta import relativedelta from django.conf import settings -from django.contrib.auth.models import Permission, make_password +from django.contrib.auth.hashers import make_password +from django.contrib.auth.models import Permission from django.contrib.messages import DEFAULT_LEVELS, get_messages from django.shortcuts import resolve_url from django.test import Client, TestCase @@ -65,8 +66,9 @@ class TestFullClickBase(TestCase): @classmethod def setUpTestData(cls): cls.customer = subscriber_user.make() - cls.barmen = subscriber_user.make(password=make_password("plop")) - cls.board_admin = board_user.make(password=make_password("plop")) + password = make_password("plop") + cls.barmen = subscriber_user.make(password=password) + cls.board_admin = board_user.make(password=password) cls.club_admin = subscriber_user.make() cls.root = baker.make(User, is_superuser=True) cls.subscriber = subscriber_user.make() diff --git a/counter/utils.py b/counter/utils.py index 03bde72f..283fb689 100644 --- a/counter/utils.py +++ b/counter/utils.py @@ -6,8 +6,10 @@ from django.urls import resolve if TYPE_CHECKING: from django.http import HttpRequest + from counter.models import Counter -def is_logged_in_counter(request: HttpRequest) -> bool: + +def is_logged_in_counter(request: HttpRequest, counter: Counter | None = None) -> bool: """Check if the request is sent from a device logged to a counter. The request must also be sent within the frame of a counter's activity. @@ -30,4 +32,6 @@ def is_logged_in_counter(request: HttpRequest) -> bool: if not referer_ok and request.resolver_match.app_name != "counter": return False - return bool(request.barmen) + if counter is None: + return bool(request.barmen) + return bool(request.barmen) and request.barmen.issubset(set(counter.barmen_list)) diff --git a/counter/views/admin.py b/counter/views/admin.py index 50cf194b..6f5053d6 100644 --- a/counter/views/admin.py +++ b/counter/views/admin.py @@ -306,52 +306,41 @@ class ReturnableProductDeleteView( } -class RefillingDeleteView(DeleteView): +class CounterTransactionDeletionView(DeleteView): + template_name = "core/delete_confirm.jinja" + + def get_queryset(self): + return super().get_queryset().select_related("counter", "customer") + + def dispatch(self, request, *args, **kwargs): + self.object: Selling | Refilling = self.get_object() + if timezone.now() - self.object.date <= timedelta( + minutes=settings.SITH_LAST_OPERATIONS_LIMIT + ) and is_logged_in_counter(request, counter=self.object.counter): + self.success_url = reverse( + "counter:details", kwargs={"counter_id": self.object.counter_id} + ) + return super().dispatch(request, *args, **kwargs) + elif self.object.is_owned_by(request.user): + self.success_url = reverse( + "core:user_account", kwargs={"user_id": self.object.customer.user_id} + ) + return super().dispatch(request, *args, **kwargs) + raise PermissionDenied + + +class RefillingDeleteView(CounterTransactionDeletionView): """Delete a refilling (for the admins).""" model = Refilling pk_url_kwarg = "refilling_id" - template_name = "core/delete_confirm.jinja" - - def dispatch(self, request, *args, **kwargs): - self.object = self.get_object() - if timezone.now() - self.object.date <= timedelta( - minutes=settings.SITH_LAST_OPERATIONS_LIMIT - ) and is_logged_in_counter(request): - self.success_url = reverse( - "counter:details", kwargs={"counter_id": self.object.counter.id} - ) - return super().dispatch(request, *args, **kwargs) - elif self.object.is_owned_by(request.user): - self.success_url = reverse( - "core:user_account", kwargs={"user_id": self.object.customer.user.id} - ) - return super().dispatch(request, *args, **kwargs) - raise PermissionDenied -class SellingDeleteView(DeleteView): +class SellingDeleteView(CounterTransactionDeletionView): """Delete a selling (for the admins).""" model = Selling pk_url_kwarg = "selling_id" - template_name = "core/delete_confirm.jinja" - - def dispatch(self, request, *args, **kwargs): - self.object = self.get_object() - if timezone.now() - self.object.date <= timedelta( - minutes=settings.SITH_LAST_OPERATIONS_LIMIT - ) and is_logged_in_counter(request): - self.success_url = reverse( - "counter:details", kwargs={"counter_id": self.object.counter.id} - ) - return super().dispatch(request, *args, **kwargs) - elif self.object.is_owned_by(request.user): - self.success_url = reverse( - "core:user_account", kwargs={"user_id": self.object.customer.user.id} - ) - return super().dispatch(request, *args, **kwargs) - raise PermissionDenied class CounterStatView(PermissionRequiredMixin, DetailView): diff --git a/counter/views/click.py b/counter/views/click.py index 5398eb92..b3046e81 100644 --- a/counter/views/click.py +++ b/counter/views/click.py @@ -242,15 +242,11 @@ class RefillingCreateView(FragmentMixin, CreateView): if not self.customer.can_buy: raise Http404 - if not is_logged_in_counter(request): - raise PermissionDenied - self.counter: Counter = get_object_or_404(Counter, id=self.kwargs["counter_id"]) - if not ( - request.barmen - and request.barmen.issubset(self.counter.barmen_list) - and self.counter.can_refill + if ( + not is_logged_in_counter(request, counter=self.counter) + or not self.counter.can_refill ): raise PermissionDenied diff --git a/counter/views/home.py b/counter/views/home.py index c643cdb1..e4763374 100644 --- a/counter/views/home.py +++ b/counter/views/home.py @@ -16,9 +16,9 @@ from datetime import timedelta from typing import TYPE_CHECKING from django.conf import settings +from django.contrib.auth.mixins import UserPassesTestMixin from django.core.exceptions import PermissionDenied from django.db.models import F -from django.http import HttpRequest, HttpResponseRedirect from django.shortcuts import redirect from django.urls import reverse from django.utils import timezone @@ -36,6 +36,7 @@ from counter.utils import is_logged_in_counter from counter.views.mixins import CounterTabsMixin if TYPE_CHECKING: + from django.http import HttpRequest, HttpResponseRedirect from django.utils.safestring import SafeString @@ -155,7 +156,7 @@ class CounterMain( return super().form_valid(form) -class CounterLastOperationsView(CounterTabsMixin, CanViewMixin, DetailView): +class CounterLastOperationsView(CounterTabsMixin, UserPassesTestMixin, DetailView): """Provide the last operations to allow barmen to delete them.""" model = Counter @@ -163,14 +164,12 @@ class CounterLastOperationsView(CounterTabsMixin, CanViewMixin, DetailView): template_name = "counter/last_ops.jinja" current_tab = "last_ops" - def dispatch(self, request, *args, **kwargs): - """We have here again a very particular right handling.""" + def test_func(self): self.object = self.get_object() - if is_logged_in_counter(request) and self.object.barmen_list: - return super().dispatch(request, *args, **kwargs) - return HttpResponseRedirect( - reverse("counter:details", kwargs={"counter_id": self.object.id}) - + "?bad_location" + return is_logged_in_counter( + self.request, counter=self.object + ) or self.request.user.has_perms( + ["counter.view_selling", "counter.view_refilling"] ) def get_context_data(self, **kwargs): diff --git a/counter/views/mixins.py b/counter/views/mixins.py index 8257833e..2e253dfa 100644 --- a/counter/views/mixins.py +++ b/counter/views/mixins.py @@ -22,7 +22,7 @@ from counter.utils import is_logged_in_counter class CounterTabsMixin(TabedViewMixin): def get_tabs_title(self): - return self.object + return self.object.name def get_list_of_tabs(self): if self.object.type != "BAR": @@ -46,7 +46,11 @@ class CounterTabsMixin(TabedViewMixin): "name": _("Cash summary"), } ) - if is_logged_in_counter(self.request): + if is_logged_in_counter( + self.request, counter=self.object + ) or self.request.user.has_perms( + ["counter.view_selling", "counter.view_refilling"] + ): tab_list.append( { "url": reverse(