diff --git a/tapir/accounts/tests/factories/factories.py b/tapir/accounts/tests/factories/factories.py
index 6db301707..451c52c35 100644
--- a/tapir/accounts/tests/factories/factories.py
+++ b/tapir/accounts/tests/factories/factories.py
@@ -65,6 +65,15 @@ def is_employee(self: TapirUser, create, is_employee):
set_group_membership([self], settings.GROUP_EMPLOYEES, is_employee)
+ @factory.post_generation
+ def is_welcome_desk_account(self: TapirUser, create, is_welcome_desk_account):
+ if not create:
+ return
+
+ set_group_membership(
+ [self], settings.GROUP_WELCOME_DESK, is_welcome_desk_account
+ )
+
@factory.post_generation
def shift_capabilities(self: TapirUser, create, shift_capabilities, **kwargs):
if not create:
diff --git a/tapir/coop/tests/membership_resignation/test_create_view.py b/tapir/coop/tests/membership_resignation/test_create_view.py
index da212489e..60478fda1 100644
--- a/tapir/coop/tests/membership_resignation/test_create_view.py
+++ b/tapir/coop/tests/membership_resignation/test_create_view.py
@@ -44,14 +44,14 @@ def setUp(self) -> None:
self.given_feature_flag_value(feature_flag_membership_resignation, True)
mock_timezone_now(self, self.NOW)
- def get_allowed_groups(self):
+ def permission_test_get_allowed_groups(self):
return [
settings.GROUP_VORSTAND,
settings.GROUP_EMPLOYEES,
settings.GROUP_MEMBER_OFFICE,
]
- def do_request(self):
+ def permission_test_do_request(self):
return self.client.get(reverse("coop:membership_resignation_create"))
def test_membershipResignationCreateView_featureFlagDisabled_accessDenied(self):
diff --git a/tapir/coop/tests/membership_resignation/test_delete_view.py b/tapir/coop/tests/membership_resignation/test_delete_view.py
index d5adf88ce..4e2883144 100644
--- a/tapir/coop/tests/membership_resignation/test_delete_view.py
+++ b/tapir/coop/tests/membership_resignation/test_delete_view.py
@@ -33,14 +33,14 @@ def setUp(self) -> None:
super().setUp()
self.given_feature_flag_value(feature_flag_membership_resignation, True)
- def get_allowed_groups(self):
+ def permission_test_get_allowed_groups(self):
return [
settings.GROUP_VORSTAND,
settings.GROUP_EMPLOYEES,
settings.GROUP_MEMBER_OFFICE,
]
- def do_request(self):
+ def permission_test_do_request(self):
resignation: MembershipResignation = MembershipResignationFactory.create()
return self.client.post(
reverse("coop:membership_resignation_delete", args=[resignation.id]),
diff --git a/tapir/coop/tests/membership_resignation/test_detail_view.py b/tapir/coop/tests/membership_resignation/test_detail_view.py
index be6915449..012608221 100644
--- a/tapir/coop/tests/membership_resignation/test_detail_view.py
+++ b/tapir/coop/tests/membership_resignation/test_detail_view.py
@@ -17,7 +17,7 @@ class TestMembershipResignationDetailView(
PermissionTestMixin, FeatureFlagTestMixin, TapirFactoryTestBase
):
- def get_allowed_groups(self):
+ def permission_test_get_allowed_groups(self):
return [
settings.GROUP_VORSTAND,
settings.GROUP_EMPLOYEES,
@@ -25,7 +25,7 @@ def get_allowed_groups(self):
settings.GROUP_ACCOUNTING,
]
- def do_request(self):
+ def permission_test_do_request(self):
self.given_feature_flag_value(feature_flag_membership_resignation, True)
resignation: MembershipResignation = MembershipResignationFactory.create()
return self.client.get(
diff --git a/tapir/coop/tests/membership_resignation/test_edit_view.py b/tapir/coop/tests/membership_resignation/test_edit_view.py
index c0046488a..dc762966b 100644
--- a/tapir/coop/tests/membership_resignation/test_edit_view.py
+++ b/tapir/coop/tests/membership_resignation/test_edit_view.py
@@ -26,14 +26,14 @@ def setUp(self) -> None:
self.given_feature_flag_value(feature_flag_membership_resignation, True)
mock_timezone_now(self, self.NOW)
- def get_allowed_groups(self):
+ def permission_test_get_allowed_groups(self):
return [
settings.GROUP_VORSTAND,
settings.GROUP_EMPLOYEES,
settings.GROUP_MEMBER_OFFICE,
]
- def do_request(self):
+ def permission_test_do_request(self):
resignation: MembershipResignation = MembershipResignationFactory.create()
return self.client.get(
reverse("coop:membership_resignation_edit", args=[resignation.id])
diff --git a/tapir/coop/tests/membership_resignation/test_list_view.py b/tapir/coop/tests/membership_resignation/test_list_view.py
index a5a668b7c..ee014c989 100644
--- a/tapir/coop/tests/membership_resignation/test_list_view.py
+++ b/tapir/coop/tests/membership_resignation/test_list_view.py
@@ -15,7 +15,7 @@ class TestMembershipResignationListView(
PermissionTestMixin, FeatureFlagTestMixin, TapirFactoryTestBase
):
- def get_allowed_groups(self):
+ def permission_test_get_allowed_groups(self):
return [
settings.GROUP_VORSTAND,
settings.GROUP_EMPLOYEES,
@@ -23,7 +23,7 @@ def get_allowed_groups(self):
settings.GROUP_ACCOUNTING,
]
- def do_request(self):
+ def permission_test_do_request(self):
self.given_feature_flag_value(feature_flag_membership_resignation, True)
return self.client.get(reverse("coop:membership_resignation_list"))
diff --git a/tapir/shifts/models.py b/tapir/shifts/models.py
index 88492151c..b06317e59 100644
--- a/tapir/shifts/models.py
+++ b/tapir/shifts/models.py
@@ -547,7 +547,6 @@ class Shift(models.Model):
)
NB_DAYS_FOR_SELF_UNREGISTER = 7
- NB_DAYS_FOR_SELF_LOOK_FOR_STAND_IN = 2
def __str__(self):
display_name = "%s: %s %s-%s" % (
@@ -716,35 +715,6 @@ def user_can_attend(self, user):
and user.share_owner.is_active(self.shift.start_time)
)
- def user_can_self_unregister(self, user: TapirUser) -> bool:
- user_is_registered_to_slot = (
- self.get_valid_attendance() is not None
- and self.get_valid_attendance().user == user
- )
- user_is_not_registered_to_slot_template = (
- self.slot_template is None
- or not hasattr(self.slot_template, "attendance_template")
- or not self.slot_template.attendance_template.user == user
- )
- early_enough = (
- self.shift.start_time.date() - timezone.now().date()
- ).days >= Shift.NB_DAYS_FOR_SELF_UNREGISTER
- return (
- user_is_registered_to_slot
- and user_is_not_registered_to_slot_template
- and early_enough
- )
-
- def user_can_look_for_standin(self, user: TapirUser) -> bool:
- user_is_registered_to_slot = (
- self.get_valid_attendance() is not None
- and self.get_valid_attendance().user == user
- )
- early_enough = (
- self.shift.start_time - timezone.now()
- ).days >= Shift.NB_DAYS_FOR_SELF_LOOK_FOR_STAND_IN
- return user_is_registered_to_slot and early_enough
-
def update_attendance_from_template(self):
"""Updates the attendance of this slot.
diff --git a/tapir/shifts/services/can_look_for_standin_service.py b/tapir/shifts/services/can_look_for_standin_service.py
new file mode 100644
index 000000000..db318798c
--- /dev/null
+++ b/tapir/shifts/services/can_look_for_standin_service.py
@@ -0,0 +1,9 @@
+from django.utils import timezone
+
+from tapir.shifts.models import ShiftSlot
+
+
+class CanLookForStandinService:
+ @staticmethod
+ def can_look_for_a_standin(slot: ShiftSlot):
+ return slot.shift.start_time > timezone.now()
diff --git a/tapir/shifts/services/self_unregister_service.py b/tapir/shifts/services/self_unregister_service.py
new file mode 100644
index 000000000..f418a776c
--- /dev/null
+++ b/tapir/shifts/services/self_unregister_service.py
@@ -0,0 +1,64 @@
+from django.utils import timezone
+from django.utils.translation import gettext_lazy as _
+
+from tapir.accounts.models import TapirUser
+from tapir.shifts.models import (
+ ShiftAttendance,
+ ShiftAttendanceTemplate,
+ Shift,
+)
+
+
+class SelfUnregisterService:
+ @classmethod
+ def should_show_is_abcd_attendance_reason(
+ cls, user: TapirUser, attendance: ShiftAttendance
+ ):
+ if not attendance.slot.slot_template:
+ return False
+
+ return ShiftAttendanceTemplate.objects.filter(
+ user=user, slot_template=attendance.slot.slot_template
+ ).exists()
+
+ @classmethod
+ def should_show_not_enough_days_before_shift_reason(
+ cls, attendance: ShiftAttendance, **_
+ ):
+ return (
+ attendance.slot.shift.start_time.date() - timezone.now().date()
+ ).days <= Shift.NB_DAYS_FOR_SELF_UNREGISTER
+
+ @classmethod
+ def build_reasons_why_cant_self_unregister(
+ cls, user: TapirUser, attendance: ShiftAttendance
+ ):
+ return [
+ message
+ for check, message in cls.get_check_to_message_map().items()
+ if check(
+ user=user,
+ attendance=attendance,
+ )
+ ]
+
+ @classmethod
+ def user_can_self_unregister(
+ cls, user: TapirUser, attendance: ShiftAttendance
+ ) -> bool:
+ for check in cls.get_check_to_message_map().keys():
+ if check(user=user, attendance=attendance):
+ return False
+
+ return True
+
+ @classmethod
+ def get_check_to_message_map(cls):
+ return {
+ cls.should_show_is_abcd_attendance_reason: _(
+ "It is not possible to unregister from a shift that comes from your ABCD shift."
+ ),
+ cls.should_show_not_enough_days_before_shift_reason: _(
+ "It is only possible to unregister from a shift at least 7 days before the shift."
+ ),
+ }
diff --git a/tapir/shifts/templates/shifts/cant_attend.html b/tapir/shifts/templates/shifts/cant_attend.html
new file mode 100644
index 000000000..0142e7a35
--- /dev/null
+++ b/tapir/shifts/templates/shifts/cant_attend.html
@@ -0,0 +1,102 @@
+{% extends "core/base.html" %}
+{% load django_bootstrap5 %}
+{% load i18n %}
+{% load static %}
+{% load core %}
+{% block title %}
+ {% translate "Can't attend:" %} {{ attendance }}
+{% endblock title %}
+{% block content %}
+
+
+
+
+
+
+
+
+ {% if can_unregister %}
+ {% blocktranslate %}
+ You can unregister from this shift.
+ You will be removed from the attendance list and won't get any negative
+ point.
+ {% endblocktranslate %}
+ {% else %}
+ {% blocktranslate %}
+ You cannot unregister from this shift.
+ The reasons are:
+ {% endblocktranslate %}
+
+ {% for reason in reasons_why_cant_self_unregister %}- {{ reason }}
{% endfor %}
+
+ {% endif %}
+
+
+
+
+
+
+
+
+
+ {% blocktranslate %}
+
+ If you cannot unregister from the shift, your other option is to look for a replacement ("stand-in").
+ This will notify the team that you can't come, and will allow other members to take over your slot.
+ Please enable this as soon as you know you won't be able to come to your shift.
+
+
+ This is always possible up to the time of the shift.
+
+
+ After enabling this, it is still absolutely necessary that you contact the member office
+ ({{ email_address_member_office }})
+ as soon as possible.
+
+
+ If you have a valid reason for not coming (see the
+ Member Manual),
+ the member office will unregister you from the shift and you won't get any negative points.
+
+
+ If you don't have a valid reason for not coming, you may still get lucky:
+ someone may take over your shift. The shift calendar shows where someone is looking for a stand-in.
+ If that happens, you will be notified by email and will not get any negative points.
+ You are still obliged to sign up for an alternative shift in this shift cycle.
+ There is no guarantee that this happens. If your slot is not taken over, you will get negative points.
+
+ {% endblocktranslate %}
+
+
+
+
+
+
+{% endblock content %}
diff --git a/tapir/shifts/templates/shifts/shift_detail.html b/tapir/shifts/templates/shifts/shift_detail.html
index e57369279..5e6bdcc7f 100644
--- a/tapir/shifts/templates/shifts/shift_detail.html
+++ b/tapir/shifts/templates/shifts/shift_detail.html
@@ -163,51 +163,12 @@ #{{ forloop.counter }}
{% else %}
-
+
+ event_busy
+ {% translate "I can't attend this shift" %}
+
{% endif %}
-
{% elif not slot.is_occupied %}
{% blocktranslate asvar self_register_tooltip %}You can only register
yourself
diff --git a/tapir/shifts/tests/test_can_look_for_standin_service.py b/tapir/shifts/tests/test_can_look_for_standin_service.py
new file mode 100644
index 000000000..9da1d3039
--- /dev/null
+++ b/tapir/shifts/tests/test_can_look_for_standin_service.py
@@ -0,0 +1,26 @@
+import datetime
+from unittest.mock import Mock
+
+from django.test import SimpleTestCase
+
+from tapir.shifts.services.can_look_for_standin_service import CanLookForStandinService
+from tapir.utils.tests_utils import mock_timezone_now
+
+
+class TestCanLookForStandinService(SimpleTestCase):
+ NOW = datetime.datetime(year=2024, month=7, day=8)
+
+ def setUp(self):
+ self.NOW = mock_timezone_now(self, self.NOW)
+
+ def test_canLookForAStandin_shiftIsInThePast_returnsFalse(self):
+ slot = Mock()
+ slot.shift.start_time = self.NOW - datetime.timedelta(hours=1)
+
+ self.assertFalse(CanLookForStandinService.can_look_for_a_standin(slot))
+
+ def test_canLookForAStandin_shiftIsInTheFuture_returnsTrue(self):
+ slot = Mock()
+ slot.shift.start_time = self.NOW + datetime.timedelta(hours=1)
+
+ self.assertTrue(CanLookForStandinService.can_look_for_a_standin(slot))
diff --git a/tapir/shifts/tests/test_cant_attend_view.py b/tapir/shifts/tests/test_cant_attend_view.py
new file mode 100644
index 000000000..f9a7c2749
--- /dev/null
+++ b/tapir/shifts/tests/test_cant_attend_view.py
@@ -0,0 +1,73 @@
+from unittest.mock import patch, Mock
+
+from django.template.response import TemplateResponse
+from django.urls import reverse
+
+from tapir import settings
+from tapir.accounts.tests.factories.factories import TapirUserFactory
+from tapir.shifts.models import ShiftAttendance, ShiftSlot
+from tapir.shifts.services.can_look_for_standin_service import CanLookForStandinService
+from tapir.shifts.services.self_unregister_service import SelfUnregisterService
+from tapir.shifts.tests.factories import ShiftFactory
+from tapir.utils.tests_utils import TapirFactoryTestBase, PermissionTestMixin
+
+
+class TestCantAttendView(PermissionTestMixin, TapirFactoryTestBase):
+ def permission_test_get_allowed_groups(self):
+ return [
+ settings.GROUP_VORSTAND,
+ settings.GROUP_EMPLOYEES,
+ settings.GROUP_MEMBER_OFFICE,
+ settings.GROUP_SHIFT_MANAGER,
+ ]
+
+ def permission_test_do_request(self):
+ ShiftFactory.create()
+ member_registered_to_the_shift = TapirUserFactory.create()
+ attendance = ShiftAttendance.objects.create(
+ user=member_registered_to_the_shift, slot=ShiftSlot.objects.get()
+ )
+
+ return self.client.get(
+ reverse("shifts:attendance_cant_attend", args=[attendance.id])
+ )
+
+ @patch.object(CanLookForStandinService, "can_look_for_a_standin")
+ @patch.object(SelfUnregisterService, "build_reasons_why_cant_self_unregister")
+ def test_cantAttendView_default_contextDataIsCorrect(
+ self,
+ mock_build_reasons_why_cant_self_unregister: Mock,
+ mock_can_look_for_a_standin: Mock,
+ ):
+ shift = ShiftFactory.create()
+ member_registered_to_the_shift = self.login_as_normal_user()
+ attendance = ShiftAttendance.objects.create(
+ user=member_registered_to_the_shift, slot=shift.slots.get()
+ )
+ mock_build_reasons_why_cant_self_unregister.return_value = (
+ "test reasons why cant self unregister"
+ )
+ mock_can_look_for_a_standin.return_value = "test can look for standin"
+
+ response: TemplateResponse = self.client.get(
+ reverse("shifts:attendance_cant_attend", args=[attendance.id])
+ )
+
+ self.assertStatusCode(response, 200)
+ self.assertEqual(attendance, response.context_data["attendance"])
+ mock_build_reasons_why_cant_self_unregister.assert_called_once_with(
+ member_registered_to_the_shift, attendance
+ )
+ self.assertEqual(
+ "test reasons why cant self unregister",
+ response.context_data["reasons_why_cant_self_unregister"],
+ )
+ self.assertEqual(
+ False,
+ response.context_data["can_unregister"],
+ )
+ mock_can_look_for_a_standin.assert_called_once_with(shift.slots.get())
+ self.assertEqual(
+ "test can look for standin",
+ response.context_data["can_look_for_standin"],
+ )
diff --git a/tapir/shifts/tests/test_member_self_look_for_stand_in.py b/tapir/shifts/tests/test_member_self_look_for_stand_in.py
index 6993a5010..e9211ed33 100644
--- a/tapir/shifts/tests/test_member_self_look_for_stand_in.py
+++ b/tapir/shifts/tests/test_member_self_look_for_stand_in.py
@@ -9,7 +9,6 @@
from tapir.shifts.models import (
ShiftSlot,
ShiftAttendance,
- Shift,
)
from tapir.shifts.tests.factories import ShiftFactory
from tapir.shifts.tests.utils import register_user_to_shift
@@ -21,9 +20,7 @@ class TestMemberSelfLookForStandIn(TapirFactoryTestBase, TapirEmailTestMixin):
def test_member_self_look_for_stand_in(self):
user = self.login_as_normal_user()
- start_time = timezone.now() + datetime.timedelta(
- days=Shift.NB_DAYS_FOR_SELF_LOOK_FOR_STAND_IN, hours=1
- )
+ start_time = timezone.now() + datetime.timedelta(hours=1)
shift = ShiftFactory.create(start_time=start_time)
register_user_to_shift(self.client, user, shift)
@@ -38,17 +35,15 @@ def test_member_self_look_for_stand_in(self):
self.assertEqual(
ShiftAttendance.objects.get(slot__shift=shift, user=user).state,
ShiftAttendance.State.LOOKING_FOR_STAND_IN,
- "The attendance state should have been set to cancelled.",
+ "The attendance state should have been set to looking for a stand in.",
)
def test_member_self_look_for_stand_in_threshold(self):
user = self.login_as_normal_user()
- start_time = timezone.now() + datetime.timedelta(
- days=Shift.NB_DAYS_FOR_SELF_LOOK_FOR_STAND_IN, hours=-1
- )
+ start_time = timezone.now() + datetime.timedelta(hours=-1)
shift = ShiftFactory.create(start_time=start_time)
+ ShiftAttendance.objects.create(user=user, slot=ShiftSlot.objects.get())
- register_user_to_shift(self.client, user, shift)
attendance = ShiftAttendance.objects.get(slot__shift=shift, user=user)
response = self.client.post(
reverse(
diff --git a/tapir/shifts/tests/test_member_self_unregisters.py b/tapir/shifts/tests/test_member_self_unregisters.py
index ef64fb6be..2988de320 100644
--- a/tapir/shifts/tests/test_member_self_unregisters.py
+++ b/tapir/shifts/tests/test_member_self_unregisters.py
@@ -18,19 +18,21 @@ class TestMemberSelfUnregisters(TapirFactoryTestBase):
def test_member_self_unregisters(self):
user = self.login_as_normal_user(share_owner__is_investing=False)
start_time = timezone.now() + datetime.timedelta(
- days=Shift.NB_DAYS_FOR_SELF_UNREGISTER, hours=1
+ days=Shift.NB_DAYS_FOR_SELF_UNREGISTER + 1
)
shift = ShiftFactory.create(start_time=start_time)
register_user_to_shift(self.client, user, shift)
attendance = ShiftAttendance.objects.get(slot__shift=shift, user=user)
- self.client.post(
+ response = self.client.post(
reverse(
"shifts:update_shift_attendance_state",
args=[attendance.id, ShiftAttendance.State.CANCELLED],
- )
+ ),
+ follow=True,
)
+ self.assertStatusCode(response, 200)
self.assertEqual(
ShiftAttendance.objects.get(slot__shift=shift, user=user).state,
ShiftAttendance.State.CANCELLED,
diff --git a/tapir/shifts/tests/test_self_unregister_service.py b/tapir/shifts/tests/test_self_unregister_service.py
new file mode 100644
index 000000000..8555d6e59
--- /dev/null
+++ b/tapir/shifts/tests/test_self_unregister_service.py
@@ -0,0 +1,89 @@
+import datetime
+from unittest.mock import Mock
+
+from tapir.accounts.tests.factories.factories import TapirUserFactory
+from tapir.shifts.models import (
+ ShiftAttendance,
+ ShiftSlot,
+ ShiftAttendanceTemplate,
+ ShiftSlotTemplate,
+)
+from tapir.shifts.services.self_unregister_service import SelfUnregisterService
+from tapir.shifts.tests.factories import ShiftFactory, ShiftTemplateFactory
+from tapir.utils.tests_utils import TapirFactoryTestBase, mock_timezone_now
+
+
+class TestSelfUnregisterService(TapirFactoryTestBase):
+ NOW = datetime.datetime(year=2024, month=3, day=4)
+
+ def setUp(self) -> None:
+ self.NOW = mock_timezone_now(self, self.NOW)
+
+ def test_shouldShowIsAbcdAttendanceReason_shiftIsNotAbcd_returnsFalse(self):
+ tapir_user = TapirUserFactory.create()
+ ShiftFactory.create()
+ attendance = ShiftAttendance.objects.create(
+ user=tapir_user, slot=ShiftSlot.objects.get()
+ )
+
+ self.assertFalse(
+ SelfUnregisterService.should_show_is_abcd_attendance_reason(
+ user=tapir_user, attendance=attendance
+ )
+ )
+
+ def test_shouldShowIsAbcdAttendanceReason_shiftIsAbcdButAttendanceIsNotAbcd_returnsFalse(
+ self,
+ ):
+ shift_template = ShiftTemplateFactory.create()
+ shift_template.create_shift(datetime.date(year=2024, month=10, day=5))
+ tapir_user = TapirUserFactory.create()
+ attendance = ShiftAttendance.objects.create(
+ user=tapir_user, slot=ShiftSlot.objects.get()
+ )
+
+ self.assertFalse(
+ SelfUnregisterService.should_show_is_abcd_attendance_reason(
+ user=tapir_user, attendance=attendance
+ )
+ )
+
+ def test_shouldShowIsAbcdAttendanceReason_shiftIsFromAnAbcdAttendance_returnsTrue(
+ self,
+ ):
+ shift_template = ShiftTemplateFactory.create()
+ tapir_user = TapirUserFactory.create()
+ ShiftAttendanceTemplate.objects.create(
+ user=tapir_user, slot_template=ShiftSlotTemplate.objects.get()
+ )
+ shift_template.create_shift(datetime.date(year=2024, month=10, day=5))
+
+ self.assertTrue(
+ SelfUnregisterService.should_show_is_abcd_attendance_reason(
+ user=tapir_user, attendance=ShiftAttendance.objects.get()
+ )
+ )
+
+ def test_shouldShowNotEnoughDaysBeforeShiftReason_dateIsNotEnoughDaysBeforeShift_returnsTrue(
+ self,
+ ):
+ attendance = Mock()
+ attendance.slot.shift.start_time = self.NOW + datetime.timedelta(days=6)
+
+ self.assertTrue(
+ SelfUnregisterService.should_show_not_enough_days_before_shift_reason(
+ attendance=attendance
+ )
+ )
+
+ def test_shouldShowNotEnoughDaysBeforeShiftReason_dateIsEnoughDaysBeforeShift_returnsFalse(
+ self,
+ ):
+ attendance = Mock()
+ attendance.slot.shift.start_time = self.NOW + datetime.timedelta(days=8)
+
+ self.assertFalse(
+ SelfUnregisterService.should_show_not_enough_days_before_shift_reason(
+ attendance=attendance
+ )
+ )
diff --git a/tapir/shifts/tests/test_shift_delete_view.py b/tapir/shifts/tests/test_shift_delete_view.py
index af0e671dc..ae4721381 100644
--- a/tapir/shifts/tests/test_shift_delete_view.py
+++ b/tapir/shifts/tests/test_shift_delete_view.py
@@ -6,7 +6,7 @@
class TestShiftDeleteForm(PermissionTestMixin, TapirFactoryTestBase):
- def get_allowed_groups(self):
+ def permission_test_get_allowed_groups(self):
return [
settings.GROUP_VORSTAND,
settings.GROUP_EMPLOYEES,
@@ -14,7 +14,7 @@ def get_allowed_groups(self):
settings.GROUP_SHIFT_MANAGER,
]
- def do_request(self):
+ def permission_test_do_request(self):
shift = ShiftFactory.create()
return self.client.get(reverse("shifts:shift_delete", args=[shift.id]))
diff --git a/tapir/shifts/urls.py b/tapir/shifts/urls.py
index 83dfbf859..fd04308f7 100644
--- a/tapir/shifts/urls.py
+++ b/tapir/shifts/urls.py
@@ -212,4 +212,9 @@
views.UpdateShiftAttendanceTemplateCustomTimeView.as_view(),
name="attendance_template_custom_time",
),
+ path(
+ "attendance//cant_attend",
+ views.CantAttendView.as_view(),
+ name="attendance_cant_attend",
+ ),
]
diff --git a/tapir/shifts/views/attendance.py b/tapir/shifts/views/attendance.py
index 2416c0d21..9b8658647 100644
--- a/tapir/shifts/views/attendance.py
+++ b/tapir/shifts/views/attendance.py
@@ -11,8 +11,10 @@
CreateView,
UpdateView,
FormView,
+ TemplateView,
)
+from tapir import settings
from tapir.accounts.models import TapirUser
from tapir.core.services.send_mail_service import SendMailService
from tapir.core.views import TapirFormMixin
@@ -38,6 +40,8 @@
ShiftAttendanceTakenOverLogEntry,
SolidarityShift,
)
+from tapir.shifts.services.can_look_for_standin_service import CanLookForStandinService
+from tapir.shifts.services.self_unregister_service import SelfUnregisterService
from tapir.shifts.views.views import SelectedUserViewMixin
from tapir.utils.shortcuts import safe_redirect, get_html_link
from tapir.utils.user_utils import UserUtils
@@ -119,18 +123,29 @@ class UpdateShiftAttendanceStateBase(
model = ShiftAttendance
get_state_from_kwargs = True
+ def __init__(self):
+ super().__init__()
+ self.attendance = None
+
def get_attendance(self):
- return ShiftAttendance.objects.get(pk=self.kwargs["pk"])
+ if not self.attendance:
+ self.attendance = ShiftAttendance.objects.get(pk=self.kwargs["pk"])
+
+ return self.attendance
def get_permission_required(self):
state = self.kwargs["state"]
self_unregister = (
state == ShiftAttendance.State.CANCELLED
- and self.get_attendance().slot.user_can_self_unregister(self.request.user)
+ and SelfUnregisterService.user_can_self_unregister(
+ self.request.user, self.get_attendance()
+ )
)
look_for_standing = (
state == ShiftAttendance.State.LOOKING_FOR_STAND_IN
- and self.get_attendance().slot.user_can_look_for_standin(self.request.user)
+ and CanLookForStandinService.can_look_for_a_standin(
+ self.get_attendance().slot
+ )
)
cancel_look_for_standing = (
state == ShiftAttendance.State.PENDING
@@ -396,3 +411,46 @@ def get_context_data(self, **kwargs):
}
)
return context
+
+
+class CantAttendView(LoginRequiredMixin, PermissionRequiredMixin, TemplateView):
+ template_name = "shifts/cant_attend.html"
+
+ def __init__(self):
+ super().__init__()
+ self.attendance = None
+
+ def get_attendance(self):
+ if not self.attendance:
+ self.attendance = get_object_or_404(ShiftAttendance, pk=self.kwargs["pk"])
+
+ return self.attendance
+
+ def get_permission_required(self):
+ attendance = self.get_attendance()
+ if attendance.user == self.request.user:
+ return []
+ return [PERMISSION_SHIFTS_MANAGE]
+
+ def get_context_data(self, **kwargs):
+ context_data = super().get_context_data()
+ context_data["attendance"] = self.get_attendance()
+
+ context_data["reasons_why_cant_self_unregister"] = (
+ SelfUnregisterService.build_reasons_why_cant_self_unregister(
+ self.request.user, self.get_attendance()
+ )
+ )
+ context_data["can_unregister"] = (
+ len(context_data["reasons_why_cant_self_unregister"]) == 0
+ )
+
+ context_data["email_address_member_office"] = (
+ settings.EMAIL_ADDRESS_MEMBER_OFFICE
+ )
+
+ context_data["can_look_for_standin"] = (
+ CanLookForStandinService.can_look_for_a_standin(self.get_attendance().slot)
+ )
+
+ return context_data
diff --git a/tapir/shifts/views/views.py b/tapir/shifts/views/views.py
index 94fbd3de1..a1c6f3384 100644
--- a/tapir/shifts/views/views.py
+++ b/tapir/shifts/views/views.py
@@ -236,10 +236,6 @@ def get_context_data(self, **kwargs):
for slot in slots:
slot.can_self_register = slot.user_can_attend(self.request.user)
- slot.can_self_unregister = slot.user_can_self_unregister(self.request.user)
- slot.can_look_for_stand_in = slot.user_can_look_for_standin(
- self.request.user
- )
slot.previous_attendances = ShiftAttendance.objects.filter(slot=slot)
if slot.get_valid_attendance() is not None:
@@ -250,9 +246,6 @@ def get_context_data(self, **kwargs):
context["slots"] = slots
context["attendance_states"] = ShiftAttendance.State
context["NB_DAYS_FOR_SELF_UNREGISTER"] = Shift.NB_DAYS_FOR_SELF_UNREGISTER
- context["NB_DAYS_FOR_SELF_LOOK_FOR_STAND_IN"] = (
- Shift.NB_DAYS_FOR_SELF_LOOK_FOR_STAND_IN
- )
context["SHIFT_ATTENDANCE_STATES"] = SHIFT_ATTENDANCE_STATES
return context
diff --git a/tapir/translations/locale/de/LC_MESSAGES/django.po b/tapir/translations/locale/de/LC_MESSAGES/django.po
index b60b8759e..f14f62831 100644
--- a/tapir/translations/locale/de/LC_MESSAGES/django.po
+++ b/tapir/translations/locale/de/LC_MESSAGES/django.po
@@ -2,12 +2,12 @@
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR , YEAR.
-#
+#
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2025-03-20 19:43+0100\n"
+"POT-Creation-Date: 2025-03-21 10:39+0100\n"
"PO-Revision-Date: 2024-12-17 14:15+0100\n"
"Last-Translator: \n"
"Language-Team: \n"
@@ -1034,7 +1034,7 @@ msgstr "Bewerber*in erzeugen"
#: coop/templates/coop/draftuser_register_form.html:29
#: shifts/templates/shifts/register_user_to_shift_slot.html:27
#: shifts/templates/shifts/register_user_to_shift_slot_template.html:26
-#: shifts/templates/shifts/shift_detail.html:226
+#: shifts/templates/shifts/shift_detail.html:187
#: shifts/templates/shifts/shift_template_detail.html:84
msgid "Register"
msgstr "Anmelden"
@@ -2309,14 +2309,14 @@ msgid ""
msgstr ""
#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:56
-#: shifts/models.py:949 shifts/templates/shifts/shift_day_printable.html:56
-#: shifts/templates/shifts/shift_detail.html:281
+#: shifts/models.py:919 shifts/templates/shifts/shift_day_printable.html:56
+#: shifts/templates/shifts/shift_detail.html:242
#: shifts/templates/shifts/shift_detail_printable.html:51
msgid "Attended"
msgstr "Teilgenommen"
#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:58
-#: shifts/models.py:948
+#: shifts/models.py:918
msgid "Pending"
msgstr "Ausstehend"
@@ -2648,7 +2648,7 @@ msgid "List of all emails"
msgstr "Liste alle E-Mails"
#: core/templates/core/email_list.html:39 shifts/models.py:513
-#: shifts/models.py:1087 shifts/templates/shifts/user_shift_account_log.html:29
+#: shifts/models.py:1057 shifts/templates/shifts/user_shift_account_log.html:29
msgid "Description"
msgstr "Beschreibung"
@@ -3101,7 +3101,7 @@ msgstr "Zeit flexibel"
msgid "If enabled, members who register for that shift can choose themselves the time where they come do their shift."
msgstr ""
-#: shifts/models.py:417 shifts/models.py:840
+#: shifts/models.py:417 shifts/models.py:810
#: shifts/templates/shifts/shift_detail.html:95
#: shifts/templates/shifts/shift_template_detail.html:42
msgid "Chosen time"
@@ -3127,53 +3127,61 @@ msgstr ""
msgid "If 'flexible time' is enabled, then the time component is ignored"
msgstr ""
-#: shifts/models.py:842
+#: shifts/models.py:812
msgid "This shift lets you choose at what time you come during the day of the shift. In order to help organising the attendance, please specify when you expect to come."
msgstr ""
-#: shifts/models.py:950 shifts/templates/shifts/shift_day_printable.html:57
-#: shifts/templates/shifts/shift_detail.html:291
+#: shifts/models.py:920 shifts/templates/shifts/shift_day_printable.html:57
+#: shifts/templates/shifts/shift_detail.html:252
#: shifts/templates/shifts/shift_detail_printable.html:52
msgid "Missed"
msgstr "Nicht erschienen"
-#: shifts/models.py:951 shifts/templates/shifts/shift_day_printable.html:58
-#: shifts/templates/shifts/shift_detail.html:321
+#: shifts/models.py:921 shifts/templates/shifts/shift_day_printable.html:58
+#: shifts/templates/shifts/shift_detail.html:282
#: shifts/templates/shifts/shift_detail_printable.html:53
msgid "Excused"
msgstr "Entschuldigt"
-#: shifts/models.py:952 shifts/templates/shifts/shift_detail.html:329
+#: shifts/models.py:922 shifts/templates/shifts/shift_detail.html:290
msgid "Cancelled"
msgstr "Abgesagt"
-#: shifts/models.py:953 shifts/templates/shifts/shift_day_printable.html:97
-#: shifts/templates/shifts/shift_detail.html:313
+#: shifts/models.py:923 shifts/templates/shifts/shift_day_printable.html:97
+#: shifts/templates/shifts/shift_detail.html:274
#: shifts/templates/shifts/shift_detail_printable.html:94
#: shifts/templates/shifts/shift_filters.html:83
msgid "Looking for a stand-in"
msgstr "Sucht Vertretung"
-#: shifts/models.py:986
+#: shifts/models.py:956
msgid "🏠 ABCD"
msgstr "🏠 ABCD"
-#: shifts/models.py:987
+#: shifts/models.py:957
msgid "✈ Flying"
msgstr "✈ Fliegend"
-#: shifts/models.py:988
+#: shifts/models.py:958
msgid "❄ Frozen"
msgstr ""
-#: shifts/models.py:1019
+#: shifts/models.py:989
msgid "Is frozen"
msgstr ""
-#: shifts/models.py:1193
+#: shifts/models.py:1163
msgid "Cycle start date"
msgstr "Anfangsdatum"
+#: shifts/services/self_unregister_service.py:59
+msgid "It is not possible to unregister from a shift that comes from your ABCD shift."
+msgstr "Es ist nicht möglich, sich von einer Schicht abzumelden, die aus einer ABCD-Schicht stammt."
+
+#: shifts/services/self_unregister_service.py:62
+msgid "It is only possible to unregister from a shift at least 7 days before the shift."
+msgstr "Es ist nur möglich, sich von einer Schicht abzumelden, wenn dies mindestens 7 Tage vor der Schicht geschieht."
+
#: shifts/templates/shifts/cancel_shift.html:7
msgid "Cancel shift:"
msgstr "Schicht absagen:"
@@ -3208,6 +3216,109 @@ msgstr ""
msgid "Confirm cancellation"
msgstr "Absage bestätigen"
+#: shifts/templates/shifts/cant_attend.html:7
+msgid "Can't attend:"
+msgstr ""
+
+#: shifts/templates/shifts/cant_attend.html:13
+msgid "If you can't attend"
+msgstr "Wenn du an diese Schicht nicht teilnehmen kannst"
+
+#: shifts/templates/shifts/cant_attend.html:21
+msgid "Option 1: unregister"
+msgstr "Option 1: Abmelden"
+
+#: shifts/templates/shifts/cant_attend.html:25
+msgid ""
+"\n"
+" You can unregister from this shift.\n"
+" You will be removed from the attendance list and won't get any negative\n"
+" point.\n"
+" "
+msgstr ""
+"\n"
+" Du kannst dich von dieser Schicht abmelden.\n"
+" Du wirst aus der Anwesenheitsliste entfernt und bekommst keine negative Punkte.\n"
+" "
+
+#: shifts/templates/shifts/cant_attend.html:31
+msgid ""
+"\n"
+" You cannot unregister from this shift.
\n"
+" The reasons are:\n"
+" "
+msgstr ""
+"\n"
+" Du kannst dich nicht von dieser Schicht abmelden.
\n"
+" Die Gründe dafür sind:\n"
+" "
+
+#: shifts/templates/shifts/cant_attend.html:48
+#: shifts/templates/shifts/shift_template_detail.html:73
+msgid "Unregister"
+msgstr "Abmelden"
+
+#: shifts/templates/shifts/cant_attend.html:57
+msgid "Option 2: Enable looking for a stand-in"
+msgstr "Option 2: Suche nach einem Ersatz aktivieren"
+
+#: shifts/templates/shifts/cant_attend.html:59
+#, python-format
+msgid ""
+"\n"
+" \n"
+" If you cannot unregister from the shift, your other option is to look for a replacement (\"stand-in\").\n"
+" This will notify the team that you can't come, and will allow other members to take over your slot.\n"
+" Please enable this as soon as you know you won't be able to come to your shift.\n"
+"
\n"
+" \n"
+" This is always possible up to the time of the shift.\n"
+"
\n"
+" \n"
+" After enabling this, it is still absolutely necessary that you contact the member office\n"
+" (%(email_address_member_office)s)\n"
+" as soon as possible.\n"
+"
\n"
+" \n"
+" If you have a valid reason for not coming (see the\n"
+" Member Manual),\n"
+" the member office will unregister you from the shift and you won't get any negative points.\n"
+"
\n"
+" \n"
+" If you don't have a valid reason for not coming, you may still get lucky:\n"
+" someone may take over your shift. The shift calendar shows where someone is looking for a stand-in.
\n"
+" If that happens, you will be notified by email and will not get any negative points.\n"
+" You are still obliged to sign up for an alternative shift in this shift cycle.
\n"
+" There is no guarantee that this happens. If your slot is not taken over, you will get negative points.\n"
+"
\n"
+" "
+msgstr ""
+"\n"
+" \n"
+" Wenn du dich nicht von der Schicht abmelden kannst, besteht eine andere Möglichkeit darin, einen Ersatz zu suchen („stand-in“). Damit teilst du dem Team mit, dass du nicht kommen kannst und ermöglichst es anderen Mitgliedern, deinen Platz einzunehmen. Bitte aktiviere dies, sobald du weißt, dass du nicht zu deiner Schicht kommen kannst.
\n"
+" \n"
+" Dies ist immer bis zum Zeitpunkt der Schicht möglich.\n"
+"
\n"
+" \n"
+" Auch nach der Aktivierung ist es unbedingt erforderlich, dass du das Mitgliederbüro \n"
+" (%(email_address_member_office)s)\n"
+" so schnell wie möglich kontaktierst.\n"
+"
\n"
+" \n"
+" Wenn du einen triftigen Grund für Ihr Nichterscheinen hast (siehe \n"
+" Mitgliederhandbuch),\n"
+" wird das Mitgliederbüro dich von der Schicht abmelden und du erhältst keine Minuspunkte.\n"
+"
\n"
+" \n"
+" Wenn du keinen triftigen Grund für dein Nichterscheinen hast, kannst du trotzdem Glück haben: Vielleicht übernimmt jemand deine Schicht. Der Schichtkalender zeigt an, wo jemand eine Vertretung sucht. Wenn das passiert, wirst du per E-Mail benachrichtigt und erhältst keine Minuspunkte. Du bist dann trotzdem verpflichtet, dich für eine alternative Schicht in diesem Schichtzyklus anzumelden.
\n"
+" Es gibt keine Garantie, dass ein Mitglied deine Schicht übernimmt. Wenn dies nicht geschieht, bekommst du Minuspunkte.\n"
+"
\n"
+" "
+
+#: shifts/templates/shifts/cant_attend.html:93
+msgid "Look for a stand-in"
+msgstr "Suche nach Vertretung"
+
#: shifts/templates/shifts/convert_exemption_to_pause_form.html:12
#: shifts/templates/shifts/convert_exemption_to_pause_form.html:18
msgid "Convert exemption to pause"
@@ -3894,61 +4005,15 @@ msgid "Cancels the search for a stand-in. Use this if you want to attend the shi
msgstr "Beendet die Suche nach Vertretung. Benutze dies, wenn du die Schicht wahrnehmen möchtest."
#: shifts/templates/shifts/shift_detail.html:162
-#: shifts/templates/shifts/shift_detail.html:302
+#: shifts/templates/shifts/shift_detail.html:263
msgid "Cancel looking for a stand-in"
msgstr "Beende die Suche nach Vertretung"
-#: shifts/templates/shifts/shift_detail.html:170
-#, fuzzy, python-format
-#| msgid ""
-#| "You can only look for a\n"
-#| " stand-in\n"
-#| " %(NB_DAYS_FOR_SELF_LOOK_FOR_STAND_IN)s days before the shift. If\n"
-#| " you can't\n"
-#| " attend, contact your shift leader as soon as\n"
-#| " possible."
-msgid ""
-"You can only look for\n"
-" a\n"
-" stand-in\n"
-" %(NB_DAYS_FOR_SELF_LOOK_FOR_STAND_IN)s days before the\n"
-" shift. If\n"
-" you can't\n"
-" attend, contact your shift leader as soon as\n"
-" possible."
-msgstr "Du kannst nur bis %(NB_DAYS_FOR_SELF_LOOK_FOR_STAND_IN)s Tage vor der Schicht nach einer Vertretung suchen. Wenn du die Schicht nicht wahrnehmen kannst, wende dich bitte schnellstmöglich an die Teamleitung."
-
-#: shifts/templates/shifts/shift_detail.html:183
-msgid "Look for a stand-in"
-msgstr "Suche nach Vertretung"
+#: shifts/templates/shifts/shift_detail.html:169
+msgid "I can't attend this shift"
+msgstr "Ich kann nicht teilnehmen"
-#: shifts/templates/shifts/shift_detail.html:192
-#, fuzzy, python-format
-#| msgid ""
-#| "You can only unregister\n"
-#| " yourself\n"
-#| " %(NB_DAYS_FOR_SELF_UNREGISTER)s days before the shift. Also,\n"
-#| " ABCD-Shifts\n"
-#| " can't be unregistered from. If you can't\n"
-#| " attend, look for a stand-in or contact your shift leader as soon as\n"
-#| " possible."
-msgid ""
-"You can only\n"
-" unregister\n"
-" yourself\n"
-" %(NB_DAYS_FOR_SELF_UNREGISTER)s days before the shift. Also,\n"
-" ABCD-Shifts\n"
-" can't be unregistered from. If you can't\n"
-" attend, look for a stand-in or contact your shift leader as soon\n"
-" as\n"
-" possible."
-msgstr "Du kannst dich nur bis %(NB_DAYS_FOR_SELF_UNREGISTER)s Tage vor deiner Schicht abmelden. Von ABCD-Schichten kannst du dich überhaupt nicht abmelden. Wenn du die Schicht nicht wahrnehmen kannst, suche bitte eine Vertretung oder melde dich schnellstmöglich bei deiner Teamleitung."
-
-#: shifts/templates/shifts/shift_detail.html:207
-msgid "Unregister myself"
-msgstr "Melde mich ab"
-
-#: shifts/templates/shifts/shift_detail.html:212
+#: shifts/templates/shifts/shift_detail.html:173
#, fuzzy
#| msgid ""
#| "You can only register\n"
@@ -3977,12 +4042,12 @@ msgstr ""
"- die Schicht in der Zukunft liegt\n"
" "
-#: shifts/templates/shifts/shift_detail.html:254
+#: shifts/templates/shifts/shift_detail.html:215
#: shifts/templates/shifts/shift_template_detail.html:95
msgid "Not specified"
msgstr ""
-#: shifts/templates/shifts/shift_detail.html:337
+#: shifts/templates/shifts/shift_detail.html:298
msgid "Edit slot"
msgstr "Slot bearbeiten"
@@ -4106,10 +4171,6 @@ msgstr "Liste des Slots für diese ABCD-Schicht"
msgid "Requirements"
msgstr ""
-#: shifts/templates/shifts/shift_template_detail.html:73
-msgid "Unregister"
-msgstr "Abmelden"
-
#: shifts/templates/shifts/shift_template_detail.html:122
msgid "Future generated Shifts"
msgstr "Zukünftige erstellte Schichten"
@@ -4455,23 +4516,23 @@ msgstr "Allgemein"
msgid "Unknown mode {attendance_mode}"
msgstr ""
-#: shifts/views/attendance.py:197 shifts/views/attendance.py:341
+#: shifts/views/attendance.py:212 shifts/views/attendance.py:356
#, python-format
msgid "Shift attendance: %(name)s"
msgstr "Schicht-Anwesenheit: %(name)s"
-#: shifts/views/attendance.py:203 shifts/views/attendance.py:347
+#: shifts/views/attendance.py:218 shifts/views/attendance.py:362
#, python-format
msgid "Updating shift attendance: %(member_link)s, %(slot_link)s"
msgstr "Schichtanwesenheit aktualisieren: %(member_link)s, %(slot_link)s"
-#: shifts/views/attendance.py:381
+#: shifts/views/attendance.py:396
#, fuzzy, python-format
#| msgid "Shift attendance: %(name)s"
msgid "ABCD attendance: %(name)s"
msgstr "Schicht-Anwesenheit: %(name)s"
-#: shifts/views/attendance.py:388
+#: shifts/views/attendance.py:403
#, fuzzy, python-format
#| msgid "Updating shift attendance: %(member_link)s, %(slot_link)s"
msgid "Updating ABCD attendance: %(member_link)s, %(slot_link)s"
@@ -4558,7 +4619,7 @@ msgstr "Schichtkonto-Protokoll für: %(name)s"
msgid "Create manual shift account entry for: %(link)s"
msgstr "Erzeuge Schicht-Kontoeintrag für: %(link)s"
-#: shifts/views/views.py:354
+#: shifts/views/views.py:347
msgid "Frozen statuses updated."
msgstr ""
@@ -5980,6 +6041,49 @@ msgstr "%(name)s ist kein Mitglied der Genossenschaft. Vielleicht haben sie dere
msgid "%(name)s has not attended a welcome session yet. Make sure they plan to do it!"
msgstr "%(name)s hat an dem Willkommenstreffen noch nicht teilgenommen. Stelle sicher, dass er*sie es entsprechend einplant!"
+#, fuzzy, python-format
+#~| msgid ""
+#~| "You can only look for a\n"
+#~| " stand-in\n"
+#~| " %(NB_DAYS_FOR_SELF_LOOK_FOR_STAND_IN)s days before the shift. If\n"
+#~| " you can't\n"
+#~| " attend, contact your shift leader as soon as\n"
+#~| " possible."
+#~ msgid ""
+#~ "You can only look for\n"
+#~ " a\n"
+#~ " stand-in\n"
+#~ " %(NB_DAYS_FOR_SELF_LOOK_FOR_STAND_IN)s days before the\n"
+#~ " shift. If\n"
+#~ " you can't\n"
+#~ " attend, contact your shift leader as soon as\n"
+#~ " possible."
+#~ msgstr "Du kannst nur bis %(NB_DAYS_FOR_SELF_LOOK_FOR_STAND_IN)s Tage vor der Schicht nach einer Vertretung suchen. Wenn du die Schicht nicht wahrnehmen kannst, wende dich bitte schnellstmöglich an die Teamleitung."
+
+#, fuzzy, python-format
+#~| msgid ""
+#~| "You can only unregister\n"
+#~| " yourself\n"
+#~| " %(NB_DAYS_FOR_SELF_UNREGISTER)s days before the shift. Also,\n"
+#~| " ABCD-Shifts\n"
+#~| " can't be unregistered from. If you can't\n"
+#~| " attend, look for a stand-in or contact your shift leader as soon as\n"
+#~| " possible."
+#~ msgid ""
+#~ "You can only\n"
+#~ " unregister\n"
+#~ " yourself\n"
+#~ " %(NB_DAYS_FOR_SELF_UNREGISTER)s days before the shift. Also,\n"
+#~ " ABCD-Shifts\n"
+#~ " can't be unregistered from. If you can't\n"
+#~ " attend, look for a stand-in or contact your shift leader as soon\n"
+#~ " as\n"
+#~ " possible."
+#~ msgstr "Du kannst dich nur bis %(NB_DAYS_FOR_SELF_UNREGISTER)s Tage vor deiner Schicht abmelden. Von ABCD-Schichten kannst du dich überhaupt nicht abmelden. Wenn du die Schicht nicht wahrnehmen kannst, suche bitte eine Vertretung oder melde dich schnellstmöglich bei deiner Teamleitung."
+
+#~ msgid "Unregister myself"
+#~ msgstr "Melde mich ab"
+
#~ msgid "Paid Shares"
#~ msgstr "Anteil(e) bezahlt"
diff --git a/tapir/translations/locale/de/LC_MESSAGES/djangojs.po b/tapir/translations/locale/de/LC_MESSAGES/djangojs.po
index df5d21902..a97409c2f 100644
--- a/tapir/translations/locale/de/LC_MESSAGES/djangojs.po
+++ b/tapir/translations/locale/de/LC_MESSAGES/djangojs.po
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2025-02-10 12:35+0100\n"
+"POT-Creation-Date: 2025-03-21 10:35+0100\n"
"PO-Revision-Date: 2024-11-05 11:14+0100\n"
"Last-Translator: \n"
"Language-Team: \n"
diff --git a/tapir/utils/tests_utils.py b/tapir/utils/tests_utils.py
index ca779d208..e483184fa 100644
--- a/tapir/utils/tests_utils.py
+++ b/tapir/utils/tests_utils.py
@@ -270,14 +270,14 @@ def given_feature_flag_value(self, flag_name: str, flag_value: bool):
class PermissionTestMixin:
- def get_allowed_groups(self):
+ def permission_test_get_allowed_groups(self):
raise NotImplementedError(
- "Children of PermissionTestMixin must implement get_allowed_groups to say which groups should have access to the view."
+ "Children of PermissionTestMixin must implement permission_test_get_allowed_groups to say which groups should have access to the view."
)
- def do_request(self):
+ def permission_test_do_request(self):
raise NotImplementedError(
- "Children of PermissionTestMixin must implement do_request, a function that visits the target view and returns the response."
+ "Children of PermissionTestMixin must implement permission_test_do_request, a function that visits the target view and returns the response."
)
@parameterized.expand(settings.LDAP_GROUPS)
@@ -290,12 +290,12 @@ def test_accessView_loggedInAsMemberOfGroup_accessAsExpected(self, group):
self.assertEqual(set([group]), user.get_ldap_user().group_names)
self.login_as_user(user)
- response = self.do_request()
+ response = self.permission_test_do_request()
self.assertStatusCode(
response,
(
HTTPStatus.OK
- if group in self.get_allowed_groups()
+ if group in self.permission_test_get_allowed_groups()
else HTTPStatus.FORBIDDEN
),
)