diff --git a/maintenance/__init__.py b/maintenance/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/maintenance/admin.py b/maintenance/admin.py new file mode 100644 index 000000000..bee1529fe --- /dev/null +++ b/maintenance/admin.py @@ -0,0 +1,55 @@ +from django import forms +from django.contrib import admin +from django.db.models import Q +from django.contrib.admin import site as admin_site +from django.core.exceptions import ValidationError +from django.utils.translation import gettext_lazy as _ + +from modeltranslation.admin import TranslationAdmin + + +from .models import MaintenanceMessage, MaintenanceMode + + +class MaintenanceModeAdmin(admin.ModelAdmin): + pass + +class MaintenanceModeInline(admin.TabularInline): + model = MaintenanceMode + fields = ('start', 'end', ) + verbose_name = _('maintenance mode') + verbose_name_plural = _('maintenance modes') + extra = 0 + + +class MaintenanceMessageAdminForm(forms.ModelForm): + class Meta: + model = MaintenanceMessage + fields = ('start', 'end', 'message', ) + + def clean(self): + start = self.cleaned_data['start'] + end = self.cleaned_data['end'] + query = Q(end__gt=start, start__lt=end) + if self.instance and self.instance.pk: + query &= ~Q(pk=self.instance.pk) + collision = MaintenanceMessage.objects.filter(query) + if collision.exists(): + raise ValidationError(_('maintenance message already exists.')) + +class MaintenanceMessageAdmin(TranslationAdmin): + form = MaintenanceMessageAdminForm + inlines = ( MaintenanceModeInline, ) + fieldsets = ( + (_('General'), { + 'fields': ( + 'start', + 'end', + 'message' + ), + }), + ) + + +admin_site.register(MaintenanceMessage, MaintenanceMessageAdmin) +admin_site.register(MaintenanceMode, MaintenanceModeAdmin) \ No newline at end of file diff --git a/maintenance/api/__init__.py b/maintenance/api/__init__.py new file mode 100644 index 000000000..5f0a45736 --- /dev/null +++ b/maintenance/api/__init__.py @@ -0,0 +1 @@ +from .announcements import MaintenanceMessageViewSet \ No newline at end of file diff --git a/resources/api/announcements.py b/maintenance/api/announcements.py similarity index 80% rename from resources/api/announcements.py rename to maintenance/api/announcements.py index f84bd974f..5bd1dba34 100644 --- a/resources/api/announcements.py +++ b/maintenance/api/announcements.py @@ -1,6 +1,6 @@ from rest_framework import viewsets -from .base import TranslatedModelSerializer, register_view -from resources.models import MaintenanceMessage +from resources.api.base import TranslatedModelSerializer, register_view +from maintenance.models import MaintenanceMessage diff --git a/maintenance/apps.py b/maintenance/apps.py new file mode 100644 index 000000000..df4a3eb8d --- /dev/null +++ b/maintenance/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class MaintenanceConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'maintenance' diff --git a/maintenance/migrations/0001_create_maintenance_app.py b/maintenance/migrations/0001_create_maintenance_app.py new file mode 100644 index 000000000..1536110d7 --- /dev/null +++ b/maintenance/migrations/0001_create_maintenance_app.py @@ -0,0 +1,57 @@ +# Generated by Django 3.2.19 on 2023-09-04 06:19 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='MaintenanceMessage', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('created_at', models.DateTimeField(default=django.utils.timezone.now, verbose_name='Time of creation')), + ('modified_at', models.DateTimeField(default=django.utils.timezone.now, verbose_name='Time of modification')), + ('message', models.TextField(verbose_name='Message')), + ('message_fi', models.TextField(null=True, verbose_name='Message')), + ('message_en', models.TextField(null=True, verbose_name='Message')), + ('message_sv', models.TextField(null=True, verbose_name='Message')), + ('start', models.DateTimeField(verbose_name='Begin time')), + ('end', models.DateTimeField(verbose_name='End time')), + ('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='maintenancemessage_created', to=settings.AUTH_USER_MODEL, verbose_name='Created by')), + ('modified_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='maintenancemessage_modified', to=settings.AUTH_USER_MODEL, verbose_name='Modified by')), + ], + options={ + 'verbose_name': 'maintenance message', + 'verbose_name_plural': 'maintenance messages', + 'ordering': ('start',), + }, + ), + migrations.CreateModel( + name='MaintenanceMode', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('created_at', models.DateTimeField(default=django.utils.timezone.now, verbose_name='Time of creation')), + ('modified_at', models.DateTimeField(default=django.utils.timezone.now, verbose_name='Time of modification')), + ('start', models.DateTimeField(verbose_name='Begin time')), + ('end', models.DateTimeField(verbose_name='End time')), + ('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='maintenancemode_created', to=settings.AUTH_USER_MODEL, verbose_name='Created by')), + ('maintenance_message', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='maintenance.maintenancemessage')), + ('modified_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='maintenancemode_modified', to=settings.AUTH_USER_MODEL, verbose_name='Modified by')), + ], + options={ + 'verbose_name': 'maintenance mode', + 'verbose_name_plural': 'maintenance modes', + 'ordering': ('start',), + }, + ), + ] diff --git a/maintenance/migrations/__init__.py b/maintenance/migrations/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/maintenance/models.py b/maintenance/models.py new file mode 100644 index 000000000..723515c07 --- /dev/null +++ b/maintenance/models.py @@ -0,0 +1,62 @@ +import datetime + + +from django.db import models +from django.utils import timezone +from django.utils.translation import gettext_lazy as _ +from django.core.exceptions import ValidationError + + +from resources.models.base import ModifiableModel + + +class MaintenanceMessageQuerySet(models.QuerySet): + def active(self): + return self.filter(start__lt=timezone.now(), end__gt=timezone.now()) + +class MaintenanceMessage(ModifiableModel): + message = models.TextField(verbose_name=_('Message'), null=False, blank=False) + start = models.DateTimeField(verbose_name=_('Begin time'), null=False, blank=False) + end = models.DateTimeField(verbose_name=_('End time'), null=False, blank=False) + + + objects = MaintenanceMessageQuerySet.as_manager() + class Meta: + verbose_name = _('maintenance message') + verbose_name_plural = _('maintenance messages') + ordering = ('start', ) + + + def __str__(self): + return f"{_('maintenance message')} \ + {timezone.localtime(self.start).replace(tzinfo=None)} - \ + {timezone.localtime(self.end).replace(tzinfo=None)}" \ + .capitalize() + + + def clean(self): + super().clean() + if self.end <= self.start: + raise ValidationError(_("Invalid start or end time")) + +class MaintenanceModeQuerySet(models.QuerySet): + def active(self): + return self.filter(start__lt=timezone.now(), end__gt=timezone.now()) + + +class MaintenanceMode(ModifiableModel): + start = models.DateTimeField(verbose_name=_('Begin time'), null=False, blank=False) + end = models.DateTimeField(verbose_name=_('End time'), null=False, blank=False) + maintenance_message = models.ForeignKey(MaintenanceMessage, on_delete=models.CASCADE, null=True, blank=True) + + objects = MaintenanceModeQuerySet.as_manager() + + class Meta: + verbose_name = _('maintenance mode') + verbose_name_plural = _('maintenance modes') + ordering = ('start', ) + + def clean(self): + super().clean() + if self.end <= self.start: + raise ValidationError(_("Invalid start or end time")) diff --git a/maintenance/tests.py b/maintenance/tests.py new file mode 100644 index 000000000..7ce503c2d --- /dev/null +++ b/maintenance/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/maintenance/translation.py b/maintenance/translation.py new file mode 100644 index 000000000..1424dcdd0 --- /dev/null +++ b/maintenance/translation.py @@ -0,0 +1,8 @@ +from modeltranslation.translator import TranslationOptions, register +from .models import MaintenanceMessage + + +@register(MaintenanceMessage) +class MaintenanceMessageTranslationOptions(TranslationOptions): + fields = ('message', ) + required_languages = ('fi', 'en', 'sv', ) diff --git a/maintenance/views.py b/maintenance/views.py new file mode 100644 index 000000000..91ea44a21 --- /dev/null +++ b/maintenance/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/resources/admin/__init__.py b/resources/admin/__init__.py index 4790b52ad..ccefd97bc 100644 --- a/resources/admin/__init__.py +++ b/resources/admin/__init__.py @@ -6,6 +6,7 @@ from django.contrib.admin import site as admin_site from django.contrib.admin.utils import unquote from django.contrib.admin.widgets import FilteredSelectMultiple +from django.contrib.admin.options import InlineModelAdmin from django.contrib.auth import get_user_model from django.contrib.auth.models import Group from django.contrib.gis.admin import OSMGeoAdmin @@ -29,7 +30,7 @@ ReservationHomeMunicipalityField, ReservationHomeMunicipalitySet, Resource, ResourceTag, ResourceAccessibility, ResourceEquipment, ResourceGroup, ResourceImage, ResourceType, TermsOfUse, Unit, UnitAuthorization, UnitIdentifier, UnitGroup, UnitGroupAuthorization, - MaintenanceMessage, UniversalFormFieldType, ResourceUniversalField, ResourceUniversalFormOption, + UniversalFormFieldType, ResourceUniversalField, ResourceUniversalFormOption ) from ..models.utils import generate_id from munigeo.models import Municipality @@ -529,32 +530,6 @@ class RespaTokenAdmin(admin.ModelAdmin): raw_id_fields = ('user',) -class MaintenanceMessageAdminForm(forms.ModelForm): - class Meta: - model = MaintenanceMessage - fields = ('start', 'end', 'message', ) - - def clean(self): - start = self.cleaned_data['start'] - end = self.cleaned_data['end'] - query = Q(end__gt=start, start__lt=end) - if self.instance and self.instance.pk: - query &= ~Q(pk=self.instance.pk) - collision = MaintenanceMessage.objects.filter(query) - if collision.exists(): - raise ValidationError(_('maintenance message already exists.')) - -class MaintenanceMessageAdmin(TranslationAdmin): - form = MaintenanceMessageAdminForm - fieldsets = ( - (_('General'), { - 'fields': ( - 'start', - 'end', - 'message' - ), - }), - ) admin_site.register(ResourceImage, ResourceImageAdmin) admin_site.register(Resource, ResourceAdmin) @@ -584,6 +559,5 @@ class MaintenanceMessageAdmin(TranslationAdmin): if admin.site.is_registered(Token): admin.site.unregister(Token) admin_site.register(Token, RespaTokenAdmin) -admin_site.register(MaintenanceMessage, MaintenanceMessageAdmin) admin_site.register(UniversalFormFieldType, UniversalFieldAdmin) admin_site.register(ResourceUniversalFormOption, ResourceUniversalFormOptionAdmin) diff --git a/resources/api/__init__.py b/resources/api/__init__.py index 2decdc46c..205030b88 100644 --- a/resources/api/__init__.py +++ b/resources/api/__init__.py @@ -9,7 +9,6 @@ from .unit import UnitViewSet from .search import TypeaheadViewSet from .equipment import EquipmentViewSet -from .announcements import MaintenanceMessageViewSet from rest_framework import routers diff --git a/resources/api/reservation.py b/resources/api/reservation.py index 8dd66b642..649aed75b 100644 --- a/resources/api/reservation.py +++ b/resources/api/reservation.py @@ -54,6 +54,8 @@ from respa.renderers import ResourcesBrowsableAPIRenderer from payments.utils import is_free, get_price +from maintenance.models import MaintenanceMode + User = get_user_model() # FIXME: Make this configurable? @@ -266,6 +268,11 @@ def validate(self, data): obj_user_is_staff = bool(request_user and request_user.is_staff) + if (not reservation or (reservation and reservation.state != Reservation.WAITING_FOR_PAYMENT)) \ + and MaintenanceMode.objects.active().exists(): + raise ValidationError(_('Reservations are disabled at this moment.')) + + try: resource = data['resource'] except KeyError: diff --git a/resources/api/resource.py b/resources/api/resource.py index 18b212a3b..5aeb8e829 100644 --- a/resources/api/resource.py +++ b/resources/api/resource.py @@ -65,6 +65,7 @@ from rest_framework.settings import api_settings as drf_settings from rest_framework.relations import PrimaryKeyRelatedField from resources.models.utils import log_entry +from maintenance.models import MaintenanceMode from drf_yasg import openapi from drf_yasg.utils import swagger_auto_schema @@ -615,6 +616,7 @@ class ResourceSerializer(ExtraDataMixin, TranslatedModelSerializer, munigeo_api. resource_staff_emails = ResourceStaffEmailsField() universal_field = ResourceUniversalFieldSerializer(many=True, read_only=True, source='resource_universal_field') reservable_by_all_staff = serializers.BooleanField(required=False) + reservable = serializers.SerializerMethodField() def get_max_price_per_hour(self, obj): """Backwards compatibility for 'max_price_per_hour' field that is now deprecated""" @@ -662,7 +664,8 @@ def get_user_permissions(self, obj): user = prefetched_user or request.user can_make_reservations_for_customers = obj.can_create_reservations_for_other_users(user) if request else False - return { + + permissions = { 'can_make_reservations': obj.can_make_reservations(user) if request else False, **({'can_make_reservations_for_customer': can_make_reservations_for_customers} if (request and can_make_reservations_for_customers) else {}), 'can_ignore_opening_hours': obj.can_ignore_opening_hours(user) if request else False, @@ -672,6 +675,12 @@ def get_user_permissions(self, obj): 'can_bypass_payment': obj.can_bypass_payment(user) if request else False, } + + if MaintenanceMode.objects.active().exists(): + return permissions.fromkeys(permissions, False) + + return permissions + def get_is_favorite(self, obj): request = self.context.get('request', None) return request.user in obj.favorited_by.all() @@ -684,6 +693,11 @@ def get_payment_terms(self, obj): data = TermsOfUseSerializer(obj.payment_terms).data return data['text'] + def get_reservable(self, obj): + if MaintenanceMode.objects.active().exists(): + return False + return obj.reservable + def get_reservable_before(self, obj): request = self.context.get('request') prefetched_user = self.context.get('prefetched_user', None) diff --git a/resources/migrations/0148_add_maintenance_mode.py b/resources/migrations/0148_add_maintenance_mode.py new file mode 100644 index 000000000..fcdeaa266 --- /dev/null +++ b/resources/migrations/0148_add_maintenance_mode.py @@ -0,0 +1,35 @@ +# Generated by Django 3.2.19 on 2023-09-01 09:35 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('resources', '0147_reserver_id_only_business'), + ] + + operations = [ + migrations.CreateModel( + name='MaintenanceMode', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('created_at', models.DateTimeField(default=django.utils.timezone.now, verbose_name='Time of creation')), + ('modified_at', models.DateTimeField(default=django.utils.timezone.now, verbose_name='Time of modification')), + ('start', models.DateTimeField(verbose_name='Begin time')), + ('end', models.DateTimeField(verbose_name='End time')), + ('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='maintenancemode_created', to=settings.AUTH_USER_MODEL, verbose_name='Created by')), + ('maintenance_message', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='resources.maintenancemessage')), + ('modified_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='maintenancemode_modified', to=settings.AUTH_USER_MODEL, verbose_name='Modified by')), + ], + options={ + 'verbose_name': 'maintenance mode', + 'verbose_name_plural': 'maintenance modes', + 'ordering': ('start',), + }, + ), + ] diff --git a/resources/migrations/0149_create_maintenance_app.py b/resources/migrations/0149_create_maintenance_app.py new file mode 100644 index 000000000..06cd52146 --- /dev/null +++ b/resources/migrations/0149_create_maintenance_app.py @@ -0,0 +1,31 @@ +# Generated by Django 3.2.19 on 2023-09-04 06:19 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('resources', '0148_add_maintenance_mode'), + ] + + operations = [ + migrations.RemoveField( + model_name='maintenancemode', + name='created_by', + ), + migrations.RemoveField( + model_name='maintenancemode', + name='maintenance_message', + ), + migrations.RemoveField( + model_name='maintenancemode', + name='modified_by', + ), + migrations.DeleteModel( + name='MaintenanceMessage', + ), + migrations.DeleteModel( + name='MaintenanceMode', + ), + ] diff --git a/resources/models/__init__.py b/resources/models/__init__.py index a92dd4103..f1f92984b 100644 --- a/resources/models/__init__.py +++ b/resources/models/__init__.py @@ -7,8 +7,8 @@ ) from .resource import ( Purpose, Resource, ResourceType, ResourceImage, ResourceEquipment, ResourceGroup, - ResourceDailyOpeningHours, TermsOfUse, ResourceTag, MaintenanceMessage, ResourceUniversalField, - ResourceUniversalFormOption, + ResourceDailyOpeningHours, TermsOfUse, ResourceTag, ResourceUniversalField, + ResourceUniversalFormOption ) from .equipment import Equipment, EquipmentAlias, EquipmentCategory from .unit import Unit, UnitAuthorization, UnitIdentifier @@ -52,7 +52,6 @@ 'UnitIdentifier', 'get_opening_hours', 'TimmiPayload', - 'MaintenanceMessage', 'UniversalFormFieldType', 'ResourceUniversalField', 'ResourceUniversalFormOption', diff --git a/resources/models/resource.py b/resources/models/resource.py index 3a99e7b28..ec034f323 100644 --- a/resources/models/resource.py +++ b/resources/models/resource.py @@ -1174,35 +1174,3 @@ def __str__(self): lower = self.open_between.lower upper = self.open_between.upper return "%s: %s -> %s" % (self.resource, lower, upper) - - -class MaintenanceMessageQuerySet(models.QuerySet): - def active(self): - return self.filter(start__lt=timezone.now(), end__gt=timezone.now()) - -class MaintenanceMessage(ModifiableModel): - message = models.TextField(verbose_name=_('Message'), null=False, blank=False) - start = models.DateTimeField(verbose_name=_('Begin time'), null=False, blank=False) - end = models.DateTimeField(verbose_name=_('End time'), null=False, blank=False) - - - objects = MaintenanceMessageQuerySet.as_manager() - class Meta: - verbose_name = _('maintenance message') - verbose_name_plural = _('maintenance messages') - ordering = ('start', ) - - - def __str__(self): - return f"{_('maintenance message')} \ - {timezone.localtime(self.start).replace(tzinfo=None)} - \ - {timezone.localtime(self.end).replace(tzinfo=None)}" \ - .capitalize() - - - def clean(self): - super().clean() - if self.end <= self.start: - raise ValidationError(_("Invalid start or end time")) - - diff --git a/resources/tests/conftest.py b/resources/tests/conftest.py index d9e14773f..de7276868 100644 --- a/resources/tests/conftest.py +++ b/resources/tests/conftest.py @@ -9,11 +9,11 @@ from resources.enums import UnitAuthorizationLevel from resources.models import Resource, ResourceType, Unit, Purpose, Day, Period from resources.models import Equipment, EquipmentAlias, ResourceEquipment, EquipmentCategory, TermsOfUse, ResourceGroup -from resources.models import AccessibilityValue, AccessibilityViewpoint, ResourceAccessibility, UnitAccessibility, MaintenanceMessage +from resources.models import AccessibilityValue, AccessibilityViewpoint, ResourceAccessibility, UnitAccessibility from resources.models import ResourceUniversalFormOption, ResourceUniversalField, UniversalFormFieldType from resources.models import ReservationMetadataSet, ReservationMetadataField from munigeo.models import Municipality - +from maintenance.models import MaintenanceMessage, MaintenanceMode @pytest.fixture def api_client(): @@ -586,6 +586,12 @@ def maintenance_message(): message_sv='Detta är ett meddelande' ) +@pytest.fixture +def maintenance_mode(): + return MaintenanceMode.objects.create( + start=timezone.now(), end=timezone.now() + datetime.timedelta(minutes=20) + ) + @pytest.mark.django_db @pytest.fixture def universal_form_field_type(): diff --git a/resources/tests/test_reservation_api.py b/resources/tests/test_reservation_api.py index 3ef16c687..74d6ec4ee 100644 --- a/resources/tests/test_reservation_api.py +++ b/resources/tests/test_reservation_api.py @@ -15,11 +15,17 @@ from caterings.models import CateringOrder, CateringProvider from resources.enums import UnitAuthorizationLevel -from resources.models import (Period, Day, Reservation, Resource, ResourceGroup, ReservationMetadataField, - ReservationMetadataSet, UnitAuthorization) +from resources.models import ( + Period, Day, Reservation, + Resource, ResourceGroup, ReservationMetadataField, + ReservationMetadataSet, UnitAuthorization +) from notifications.models import NotificationTemplate, NotificationType from notifications.tests.utils import check_received_mail_exists -from .utils import check_disallowed_methods, assert_non_field_errors_contain, assert_response_objects, MAX_QUERIES +from .utils import ( + check_disallowed_methods, assert_non_field_errors_contain, + assert_response_objects, MAX_QUERIES +) DEFAULT_RESERVATION_EXTRA_FIELDS = ('reserver_name', 'reserver_phone_number', 'reserver_address_street', @@ -3258,3 +3264,12 @@ def test_reservations_made_to_unit_with_overlap_restriction_with_different_times assert response.status_code == expected if expected == 400: assert response.data.get('non_field_errors')[0].code == 'conflicting_reservation' + +@pytest.mark.django_db +def test_reservation_not_allowed_during_maintenance_mode( + api_client, user, list_url, reservation_data_extra, maintenance_mode): + api_client.force_authenticate(user=user) + + response = api_client.post(list_url, data=reservation_data_extra) + assert response.status_code == 400 + assert_non_field_errors_contain(response, 'Reservations are disabled at this moment') \ No newline at end of file diff --git a/resources/tests/test_resource_api.py b/resources/tests/test_resource_api.py index c6c24917e..445b880ad 100644 --- a/resources/tests/test_resource_api.py +++ b/resources/tests/test_resource_api.py @@ -67,10 +67,9 @@ def _check_permissions_dict(api_client, resource, is_admin, is_manager, is_viewe url = reverse('resource-detail', kwargs={'pk': resource.pk}) response = api_client.get(url) - print(response.data) assert response.status_code == 200 permissions = response.data['user_permissions'] - + if can_create_reservations_for_other_users: # exists and is True if user is staff for the resource # OR the user is staff and the resource has reservable_by_all_staff set to True @@ -1264,4 +1263,38 @@ def test_user_permissions_external_resources(api_client, resource_in_unit, user, # can_create_reservations_for_other_users should be False for resource_in_unit3 _check_permissions_dict(api_client, resource_in_unit3, is_admin=False, is_manager=False, is_viewer=False, can_make_reservations=True, can_ignore_opening_hours=False, - can_bypass_payment=False, can_create_reservations_for_other_users=False) \ No newline at end of file + can_bypass_payment=False, can_create_reservations_for_other_users=False) + + +@pytest.mark.django_db +def test_user_permissions_in_resource_endpoint_during_maintenance_mode( + maintenance_mode, resource_in_unit, + user, api_client, + staff_user, staff_api_client): + resource_in_unit.unit.create_authorization(staff_user, 'manager') + url = reverse('resource-detail', kwargs={'pk': resource_in_unit.pk}) + + expected = { + 'can_bypass_payment': False, + 'can_ignore_opening_hours': False, + 'can_make_reservations': False, + 'is_admin': False, + 'is_manager': False, + 'is_viewer': False + } + + + api_client.force_authenticate(user=user) + response = api_client.get(url) + assert response.status_code == 200 + assert response.data['user_permissions'] == expected + + + expected.update({ + 'can_make_reservations_for_customer': False + }) + + staff_api_client.force_authenticate(user=staff_user) + response = staff_api_client.get(url) + assert response.status_code == 200 + assert response.data['user_permissions'] == expected diff --git a/resources/translation.py b/resources/translation.py index 26d013024..5ee7424c5 100644 --- a/resources/translation.py +++ b/resources/translation.py @@ -5,7 +5,7 @@ Purpose, Resource, ResourceEquipment, ResourceImage, ResourceType, TermsOfUse, Unit, UnitGroup, Reservation, ReservationHomeMunicipalityField, - MaintenanceMessage, UniversalFormFieldType, ResourceUniversalField, + UniversalFormFieldType, ResourceUniversalField, ResourceUniversalFormOption, ) @@ -75,12 +75,6 @@ class ReservationHomeMunicipalityFieldTranslationOptions(TranslationOptions): fields = ('name',) required_languages = ('fi', 'en', 'sv') -@register(MaintenanceMessage) -class MaintenanceMessageTranslationOptions(TranslationOptions): - fields = ('message', ) - required_languages = ('fi', 'en', 'sv', ) - - @register(UniversalFormFieldType) class UniversalFormFieldTranslationOptions(TranslationOptions): pass diff --git a/respa/settings.py b/respa/settings.py index 9fb94f489..ee91e067d 100644 --- a/respa/settings.py +++ b/respa/settings.py @@ -222,6 +222,7 @@ 'respa_outlook', 'respa_o365', 'respa_admin', + 'maintenance', 'sanitized_dump', 'drf_yasg', diff --git a/respa/urls.py b/respa/urls.py index 3c8d9a152..c0596c840 100644 --- a/respa/urls.py +++ b/respa/urls.py @@ -10,6 +10,7 @@ from resources.views.ical import ICalFeedView import accessibility.api +import maintenance.api if getattr(settings, 'RESPA_COMMENTS_ENABLED', False): import comments.api