1 Commits

Author SHA1 Message Date
imperosol 519a7758c5 manage case where barman is already logged in another device 2026-06-12 10:32:16 +02:00
6 changed files with 73 additions and 29 deletions
+7 -5
View File
@@ -39,6 +39,7 @@ from counter.models import (
Customer,
Eticket,
InvoiceCall,
Permanency,
Price,
Product,
ProductFormula,
@@ -151,12 +152,13 @@ class CounterLoginForm(LoginForm):
raise ValidationError(
message=_("You are not a barman of this counter."), code="not_barman"
)
if Permanency.objects.filter(end=None, user=user).exists():
if user in self.request.barmen:
message = (
_("You are already logged in this counter.")
if user in self.counter.barmen_list
else _("You are already logged in another counter.")
)
message = _("You are already logged in this counter.")
elif user in self.counter.barmen_list:
message = _("You are already logged in another counter.")
else:
message = _("You are already logged on another device")
raise ValidationError(message=message, code="already_logged_in")
+21 -19
View File
@@ -1,8 +1,7 @@
from typing import TYPE_CHECKING, Callable
from django.db.models import Exists, OuterRef
from django.http import HttpRequest, HttpResponse
from django.utils.functional import SimpleLazyObject, empty
from django.utils.functional import SimpleLazyObject
from core.models import User
from counter.models import Permanency
@@ -11,20 +10,31 @@ if TYPE_CHECKING:
from django.contrib.sessions.backends.base import SessionBase
SESSION_BARMEN_KEY = "barmen_ids"
SESSION_PERMANENCES_KEY = "permanence_ids"
def get_cached_barmen(request: HttpRequest) -> set[User]:
if not hasattr(request, "_cached_barmen"):
session: SessionBase = request.session
barmen_ids = session.get(SESSION_BARMEN_KEY, [])
if barmen_ids:
request._cached_barmen = set(
User.objects.filter(
Exists(Permanency.objects.filter(user=OuterRef("pk"), end=None)),
id__in=barmen_ids,
)
if session_ids := session.get(SESSION_PERMANENCES_KEY, None):
# Get ongoing permanences which id is in session.
# Note : we store permanence ids rather than user id to be sure
# not to wrongfully mark someone as logged here,
# even if it logged out then logged in elsewhere.
permanences = (
Permanency.objects.filter(end=None, id__in=session_ids)
.order_by("id")
.select_related("user")
)
# if the list of permanences occurring on this device has changed
# since the last page load, change the ids stored in session
real_ids = [p.id for p in permanences]
if real_ids != session_ids:
session[SESSION_PERMANENCES_KEY] = real_ids
request._cached_barmen = {p.user for p in permanences}
else:
request._cached_barmen = set()
@@ -53,12 +63,4 @@ class BarmenMiddleware:
def __call__(self, request: HttpRequest):
request.barmen = SimpleLazyObject(lambda: get_cached_barmen(request))
response = self.get_response(request)
if request.barmen._wrapped is not empty and {
b.id for b in request.barmen
} != set(request.session.get(SESSION_BARMEN_KEY, [])):
# update the session data only if `session.barmen`
# has been accessed and modified.
request.session[SESSION_BARMEN_KEY] = [b.id for b in request.barmen]
return response
return self.get_response(request)
+1 -1
View File
@@ -1105,7 +1105,7 @@ class Permanency(models.Model):
on_delete=models.CASCADE,
)
start = models.DateTimeField(_("start date"))
end = models.DateTimeField(_("end date"), null=True, db_index=True)
end = models.DateTimeField(_("end date"), null=True, blank=True, db_index=True)
activity = models.DateTimeField(_("last activity date"), auto_now=True)
class Meta:
+36 -1
View File
@@ -760,10 +760,10 @@ class TestBarmanConnection(TestCase):
assert last_perm.counter == self.counter
assert last_perm.user == self.barman
assert last_perm.end is None
assert self.barman in response.wsgi_request.barmen
response = self.client.get(
self.detail_url, {"username": self.barman.username, "password": "plop"}
)
assert self.barman in response.wsgi_request.barmen
assert response.context_data.get("barmen") == [self.barman]
soup = BeautifulSoup(response.text, "lxml")
assert soup.find("form", id="select-user-form") is not None
@@ -804,6 +804,41 @@ class TestBarmanConnection(TestCase):
)
self.assert_counter_login_fails(self.barman)
def test_barman_already_logged_in_another_device(self):
"""Test when the barman is already logged in the current counter on another device."""
other_client = Client()
other_client.post(
self.login_url, {"username": self.barman.username, "password": "plop"}
)
self.assert_counter_login_fails(self.barman)
def test_barman_login_elsewhere(self):
"""Test when the barman log himself out then log in on another device."""
self.client.post(
self.login_url, {"username": self.barman.username, "password": "plop"}
)
other_client = Client()
other_client.post(
reverse("counter:logout", kwargs={"counter_id": self.counter.id}),
data={"user_id": self.barman.id},
)
response = other_client.post(
self.login_url, {"username": self.barman.username, "password": "plop"}
)
assert response.status_code == 200
assert response.headers["HX-Redirect"] == self.detail_url
# the barmen should now be logged in `other_client`...
response = other_client.get(
self.detail_url, {"username": self.barman.username, "password": "plop"}
)
assert self.barman in response.wsgi_request.barmen
# ... but not in `self.client`
response = self.client.get(
self.detail_url, {"username": self.barman.username, "password": "plop"}
)
assert self.barman not in response.wsgi_request.barmen
def test_barman_already_logged_elsewhere(self):
"""Test when the barman is already logged in another counter."""
other_counter = baker.make(Counter, type="BAR")
+3 -2
View File
@@ -30,6 +30,7 @@ from django.views.generic.edit import FormView
from core.auth.mixins import CanViewMixin
from core.views import FragmentMixin, UseFragmentsMixin
from counter.forms import CounterLoginForm, GetUserForm
from counter.middleware import SESSION_PERMANENCES_KEY
from counter.models import Counter, Permanency
from counter.utils import is_logged_in_counter
from counter.views.mixins import CounterTabsMixin
@@ -58,8 +59,8 @@ class CounterLoginFragment(FragmentMixin, SingleObjectMixin, FormView):
def form_valid(self, form: CounterLoginForm):
user = form.get_user()
self.object.permanencies.create(user=user, start=timezone.now())
self.request.barmen.add(user)
perm = self.object.permanencies.create(user=user, start=timezone.now())
self.request.session.setdefault(SESSION_PERMANENCES_KEY, []).append(perm.id)
self.success_url = reverse(
"counter:details", kwargs={"counter_id": self.object.id}
)
+4
View File
@@ -3217,6 +3217,10 @@ msgstr "Vous êtes déjà connecté à ce comptoir."
msgid "You are already logged in another counter."
msgstr "Vous êtes déjà connecté à un autre comptoir."
#: counter/forms.py
msgid "You are already logged on another device"
msgstr "Vous êtes déjà connecté sur un autre appareil"
#: counter/forms.py
msgid "Regular barmen"
msgstr "Barmen réguliers"