diff --git a/src/verzoeken/api/serializers.py b/src/verzoeken/api/serializers.py index 3410679..9dbdb0f 100644 --- a/src/verzoeken/api/serializers.py +++ b/src/verzoeken/api/serializers.py @@ -32,7 +32,10 @@ ) from verzoeken.sync.signals import SyncError -from .validators import ObjectVerzoekCreateValidator +from .validators import ( + KlantProductUniqueTogetherValidator, + ObjectVerzoekCreateValidator, +) logger = logging.getLogger(__name__) @@ -227,6 +230,16 @@ class Meta: ], }, } + validators = [ + KlantProductUniqueTogetherValidator( + queryset=VerzoekProduct.objects.all(), + fields=["product", "verzoek"], + ), + KlantProductUniqueTogetherValidator( + queryset=VerzoekProduct.objects.all(), + fields=["product_code", "verzoek"], + ), + ] def validate(self, attrs): validated_attrs = super().validate(attrs) diff --git a/src/verzoeken/api/tests/test_verzoekproduct.py b/src/verzoeken/api/tests/test_verzoekproduct.py index bc3555c..117a8e5 100644 --- a/src/verzoeken/api/tests/test_verzoekproduct.py +++ b/src/verzoeken/api/tests/test_verzoekproduct.py @@ -114,6 +114,22 @@ def test_create_verzoekproduct_with_invalid_product_url(self): error = get_validation_errors(response, "product") self.assertEqual(error["code"], "bad-url") + def test_create_verzoekproduct_with_product_url_not_unique(self): + verzoek = VerzoekFactory.create() + verzoek_url = reverse(verzoek) + + VerzoekProductFactory.create(verzoek=verzoek, product="https://example.com/") + + list_url = reverse(VerzoekProduct) + data = {"verzoek": verzoek_url, "product": "https://example.com/"} + + response = self.client.post(list_url, data) + + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + + error = get_validation_errors(response, "nonFieldErrors") + self.assertEqual(error["code"], "unique") + def test_create_verzoekproduct_with_product_id(self): verzoek = VerzoekFactory.create() verzoek_url = reverse(verzoek) @@ -133,6 +149,22 @@ def test_create_verzoekproduct_with_product_id(self): verzoekproduct.product_code, data["productIdentificatie"]["code"] ) + def test_create_verzoekproduct_with_product_id_not_unique(self): + verzoek = VerzoekFactory.create() + verzoek_url = reverse(verzoek) + + VerzoekProductFactory.create(verzoek=verzoek, product_code="test") + + list_url = reverse(VerzoekProduct) + data = {"verzoek": verzoek_url, "productIdentificatie": {"code": "test"}} + + response = self.client.post(list_url, data) + + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + + error = get_validation_errors(response, "nonFieldErrors") + self.assertEqual(error["code"], "unique") + def test_create_verzoekproduct_without_product(self): verzoek = VerzoekFactory.create() verzoek_url = reverse(verzoek) diff --git a/src/verzoeken/api/validators.py b/src/verzoeken/api/validators.py index 70deb67..a231cc2 100644 --- a/src/verzoeken/api/validators.py +++ b/src/verzoeken/api/validators.py @@ -5,6 +5,7 @@ from django.utils.translation import ugettext_lazy as _ from rest_framework import exceptions, serializers +from rest_framework.validators import UniqueTogetherValidator from vng_api_common.models import APICredential from vng_api_common.validators import ResourceValidator from zds_client import ClientError @@ -106,3 +107,18 @@ def __call__(self, attrs: OrderedDict): raise serializers.ValidationError( self.message.format(object=object_type), code=self.code ) + + +class KlantProductUniqueTogetherValidator(UniqueTogetherValidator): + def enforce_required_fields(self, attrs): + # These attributes are optional, so this should not be enforced + return + + def __call__(self, attrs): + # To ensure that the validation does not fail in case the attributes + # are missing (since they are not required) + if ("product" in self.fields and "product" not in attrs) or ( + "product_code" in self.fields and "product_code" not in attrs + ): + return + super().__call__(attrs)