diff --git a/src/openforms/forms/api/serializers/form_step.py b/src/openforms/forms/api/serializers/form_step.py index d98de0d739..56061c0a75 100644 --- a/src/openforms/forms/api/serializers/form_step.py +++ b/src/openforms/forms/api/serializers/form_step.py @@ -12,6 +12,7 @@ from ...models import FormDefinition, FormStep from ...validators import validate_no_duplicate_keys_across_steps from .button_text import ButtonTextSerializer +from ..validators import FormStepIsApplicableIfFirstValidator class FormStepLiteralsSerializer(serializers.Serializer): @@ -54,6 +55,7 @@ class Meta: "index", "literals", "url", + "is_applicable", ) extra_kwargs = { "uuid": { @@ -140,6 +142,7 @@ class Meta: "read_only": True, }, } + validators = [FormStepIsApplicableIfFirstValidator()] def create(self, validated_data): validated_data["form"] = self.context["form"] diff --git a/src/openforms/forms/api/validators.py b/src/openforms/forms/api/validators.py index 3f18fa2bb4..350d71e896 100644 --- a/src/openforms/forms/api/validators.py +++ b/src/openforms/forms/api/validators.py @@ -190,6 +190,19 @@ def __call__(self, attrs: dict, serializer: serializers.Serializer): ) +class FormStepIsApplicableIfFirstValidator: + def __call__(self, attrs: dict): + if not attrs.get("is_applicable", True) and attrs.get("order") == 0: + raise serializers.ValidationError( + { + "is_applicable": serializers.ErrorDetail( + _("First form step must be applicable."), + code="invalid", + ), + } + ) + + def validate_template_expressions(configuration: JSONObject) -> None: """ Validate that any template expressions in supported properties are correct. diff --git a/src/openforms/forms/tests/test_api_formsteps.py b/src/openforms/forms/tests/test_api_formsteps.py index 193af20435..59a299f2fd 100644 --- a/src/openforms/forms/tests/test_api_formsteps.py +++ b/src/openforms/forms/tests/test_api_formsteps.py @@ -1092,3 +1092,42 @@ def test_update_with_translations_validate_literals(self, _mock): for error in expected: with self.subTest(field=error["name"], code=error["code"]): self.assertIn(error, invalid_params) + + +class FormStepsAPIApplicabilityTests(APITestCase): + def setUp(self): + super().setUp() + + self.user = UserFactory.create() + self.form = FormFactory.create() + self.form_definition = FormDefinitionFactory.create() + self.client.force_authenticate(user=self.user) + + def test_create_form_step_not_applicable_as_first_unsucessful(self): + self.user.user_permissions.add(Permission.objects.get(codename="change_form")) + self.user.is_staff = True + self.user.save() + url = reverse( + "api:form-steps-list", kwargs={"form_uuid_or_slug": self.form.uuid} + ) + + form_detail_url = reverse( + "api:formdefinition-detail", + kwargs={"uuid": self.form_definition.uuid}, + ) + data = { + "formDefinition": f"http://testserver{form_detail_url}", + "index": 0, + "isApplicable": False, + } + response = self.client.post(url, data=data) + + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual( + response.json()["invalidParams"][0], + { + "name": "isApplicable", + "code": "invalid", + "reason": "First form step must be applicable.", + }, + ) diff --git a/src/openforms/forms/tests/test_models.py b/src/openforms/forms/tests/test_models.py index b9621f259e..da11720415 100644 --- a/src/openforms/forms/tests/test_models.py +++ b/src/openforms/forms/tests/test_models.py @@ -426,10 +426,15 @@ def test_clean(self): order=0, is_applicable=True, ) + step_ok_order_1 = FormStepFactory.create( + order=1, + is_applicable=False, + ) with self.subTest("clean raises"): self.assertRaises(ValidationError, step_raises.clean) - with self.subTest("clean does not raises"): + with self.subTest("clean does not raise"): step_ok.clean() + step_ok_order_1.clean() class FormLogicTests(TestCase): diff --git a/src/openforms/js/components/admin/form_design/FormStep.js b/src/openforms/js/components/admin/form_design/FormStep.js index f77a714fcd..4dd4202c62 100644 --- a/src/openforms/js/components/admin/form_design/FormStep.js +++ b/src/openforms/js/components/admin/form_design/FormStep.js @@ -23,7 +23,6 @@ const FormStep = ({data, onEdit, onComponentMutated, onFieldChange, onReplace}) validationErrors = [], componentTranslations, } = data; - console.log(_generatedId); const previousFormDefinition = usePrevious(formDefinition); let forceBuilderUpdate = false; if (previousFormDefinition && previousFormDefinition != formDefinition) {