diff --git a/tests/test_graphql_api/test_reservation/test_cancel.py b/tests/test_graphql_api/test_reservation/test_cancel.py index 9411dbddd..6687ddf17 100644 --- a/tests/test_graphql_api/test_reservation/test_cancel.py +++ b/tests/test_graphql_api/test_reservation/test_cancel.py @@ -8,7 +8,7 @@ from tests.factories import PaymentOrderFactory, ReservationFactory from tests.helpers import patch_method -from tilavarauspalvelu.enums import OrderStatus, PaymentType, ReservationStateChoice +from tilavarauspalvelu.enums import OrderStatus, PaymentType, ReservationStateChoice, ReservationTypeChoice from tilavarauspalvelu.models import ReservationCancelReason from tilavarauspalvelu.utils.verkkokauppa.verkkokauppa_api_client import VerkkokauppaAPIClient from utils.date_utils import local_datetime @@ -20,8 +20,9 @@ ] -def test_reservation__cancel__success(graphql): - reservation = ReservationFactory.create_for_cancellation() +@pytest.mark.parametrize("reservation_type", [ReservationTypeChoice.NORMAL, ReservationTypeChoice.SEASONAL]) +def test_reservation__cancel__success(graphql, reservation_type): + reservation = ReservationFactory.create_for_cancellation(type=reservation_type) graphql.login_with_superuser() data = get_cancel_data(reservation) @@ -50,6 +51,36 @@ def test_reservation__cancel__adds_cancel_details(graphql): assert reservation.cancel_details == "foo" +@pytest.mark.parametrize( + "reservation_type", + [ + ReservationTypeChoice.BLOCKED, + ReservationTypeChoice.STAFF, + ReservationTypeChoice.BEHALF, + ], +) +def test_reservation__cancel__fails_type_wrong(graphql, reservation_type): + reservation = ReservationFactory.create_for_cancellation(type=reservation_type) + + graphql.login_with_superuser() + data = get_cancel_data(reservation) + response = graphql(CANCEL_MUTATION, input_data=data) + + assert response.error_message() == "Mutation was unsuccessful." + assert response.field_error_messages() == ["Only reservations with type ['NORMAL', 'SEASONAL'] can be cancelled."] + + +def test_reservation__cancel__fails_when_type_is_seasonal_and_reservation_is_paid(graphql): + reservation = ReservationFactory.create_for_cancellation(type=ReservationTypeChoice.SEASONAL, price=10) + + graphql.login_with_superuser() + data = get_cancel_data(reservation) + response = graphql(CANCEL_MUTATION, input_data=data) + + assert response.error_message() == "Mutation was unsuccessful." + assert response.field_error_messages() == ["Paid seasonal reservations cannot be cancelled."] + + def test_reservation__cancel__fails_if_state_is_not_confirmed(graphql): reservation = ReservationFactory.create_for_cancellation(state=ReservationStateChoice.CREATED) diff --git a/tilavarauspalvelu/api/graphql/types/reservation/serializers/cancellation_serializers.py b/tilavarauspalvelu/api/graphql/types/reservation/serializers/cancellation_serializers.py index 583fbff71..265f8e5cf 100644 --- a/tilavarauspalvelu/api/graphql/types/reservation/serializers/cancellation_serializers.py +++ b/tilavarauspalvelu/api/graphql/types/reservation/serializers/cancellation_serializers.py @@ -5,7 +5,7 @@ from rest_framework.exceptions import ValidationError from tilavarauspalvelu.api.graphql.extensions import error_codes -from tilavarauspalvelu.enums import OrderStatus, ReservationStateChoice +from tilavarauspalvelu.enums import OrderStatus, ReservationStateChoice, ReservationTypeChoice from tilavarauspalvelu.integrations.email.main import EmailService from tilavarauspalvelu.models import Reservation from tilavarauspalvelu.tasks import refund_paid_reservation_task @@ -46,6 +46,14 @@ def validate(self, data: dict[str, Any]) -> dict[str, Any]: msg = "Only reservations with state 'CONFIRMED' can be cancelled." raise ValidationError(msg, code=error_codes.RESERVATION_CANCELLATION_NOT_ALLOWED) + if self.instance.type not in ReservationTypeChoice.types_that_can_be_cancelled: + msg = f"Only reservations with type {ReservationTypeChoice.types_that_can_be_cancelled} can be cancelled." + raise ValidationError(msg, code=error_codes.RESERVATION_CANCELLATION_NOT_ALLOWED) + + if self.instance.type == ReservationTypeChoice.SEASONAL.value and self.instance.price > 0: + msg = "Paid seasonal reservations cannot be cancelled." + raise ValidationError(msg, code=error_codes.RESERVATION_CANCELLATION_NOT_ALLOWED) + now = local_datetime() if self.instance.begin < now: msg = "Reservation cannot be cancelled after it has begun." diff --git a/tilavarauspalvelu/enums.py b/tilavarauspalvelu/enums.py index 9e21e38a8..fcb76a009 100644 --- a/tilavarauspalvelu/enums.py +++ b/tilavarauspalvelu/enums.py @@ -436,6 +436,13 @@ def should_not_anonymize(cls) -> list[str]: ReservationTypeChoice.STAFF.value, ] + @classproperty + def types_that_can_be_cancelled(cls) -> list[str]: + return [ # type: ignore[return-type] + ReservationTypeChoice.NORMAL.value, + ReservationTypeChoice.SEASONAL.value, + ] + class ReservationTypeStaffChoice(models.TextChoices): # These are the same as the ones above, but for the staff create endpoint