Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/5 product verbruiksobjecten #36

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions requirements/base.in
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,7 @@ open-api-framework
django-markdownx
django-localflavor

django-json-schema-model

# waiting for > 2.0.1
commonground-api-common==1.13.4
7 changes: 6 additions & 1 deletion requirements/base.txt
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ django==4.2.17
# django-csp
# django-filter
# django-formtools
# django-json-schema-model
# django-jsonform
# django-localflavor
# django-log-outgoing-requests
Expand Down Expand Up @@ -127,6 +128,8 @@ django-filter==24.3
# open-api-framework
django-formtools==2.5.1
# via django-two-factor-auth
django-json-schema-model==0.1.0
# via -r requirements/base.in
django-jsonform==2.23.1
# via
# mozilla-django-oidc-db
Expand Down Expand Up @@ -233,7 +236,9 @@ jinja2==3.1.4
josepy==1.14.0
# via mozilla-django-oidc
jsonschema==4.23.0
# via drf-spectacular
# via
# django-json-schema-model
# drf-spectacular
jsonschema-specifications==2024.10.1
# via jsonschema
kombu==5.4.2
Expand Down
6 changes: 6 additions & 0 deletions requirements/ci.txt
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ django==4.2.17
# django-csp
# django-filter
# django-formtools
# django-json-schema-model
# django-jsonform
# django-localflavor
# django-log-outgoing-requests
Expand Down Expand Up @@ -214,6 +215,10 @@ django-formtools==2.5.1
# -c requirements/base.txt
# -r requirements/base.txt
# django-two-factor-auth
django-json-schema-model==0.1.0
# via
# -c requirements/base.txt
# -r requirements/base.txt
django-jsonform==2.23.1
# via
# -c requirements/base.txt
Expand Down Expand Up @@ -455,6 +460,7 @@ jsonschema==4.23.0
# via
# -c requirements/base.txt
# -r requirements/base.txt
# django-json-schema-model
# drf-spectacular
jsonschema-specifications==2024.10.1
# via
Expand Down
6 changes: 6 additions & 0 deletions requirements/dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ django==4.2.17
# django-extensions
# django-filter
# django-formtools
# django-json-schema-model
# django-jsonform
# django-localflavor
# django-log-outgoing-requests
Expand Down Expand Up @@ -245,6 +246,10 @@ django-formtools==2.5.1
# -c requirements/ci.txt
# -r requirements/ci.txt
# django-two-factor-auth
django-json-schema-model==0.1.0
# via
# -c requirements/ci.txt
# -r requirements/ci.txt
django-jsonform==2.23.1
# via
# -c requirements/ci.txt
Expand Down Expand Up @@ -509,6 +514,7 @@ jsonschema==4.23.0
# via
# -c requirements/ci.txt
# -r requirements/ci.txt
# django-json-schema-model
# drf-spectacular
jsonschema-specifications==2024.10.1
# via
Expand Down
1 change: 1 addition & 0 deletions src/open_producten/conf/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"rest_framework.authtoken",
"localflavor",
"markdownx",
"django_json_schema_model",
"open_producten.accounts",
"open_producten.utils",
"open_producten.producttypen",
Expand Down
6 changes: 3 additions & 3 deletions src/open_producten/locaties/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
description="Deze lijst kan gefilterd wordt met query-string parameters.",
),
retrieve=extend_schema(
summary="Een specifiek LOCATIE opvragen.",
summary="Een specifieke LOCATIE opvragen.",
),
create=extend_schema(
summary="Maak een LOCATIE aan.",
Expand Down Expand Up @@ -73,7 +73,7 @@ class LocatieViewSet(OrderedModelViewSet):
"rol": "medewerker",
},
request_only=True,
)
),
],
),
update=extend_schema(
Expand All @@ -98,7 +98,7 @@ class ContactViewSet(OrderedModelViewSet):
description="Deze lijst kan gefilterd wordt met query-string parameters.",
),
retrieve=extend_schema(
summary="Een specifiek ORGANISATIE opvragen.",
summary="Een specifieke ORGANISATIE opvragen.",
),
create=extend_schema(
summary="Maak een ORGANISATIE aan.",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Generated by Django 4.2.17 on 2025-01-27 15:42

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("producten", "0002_alter_product_eind_datum_alter_product_start_datum"),
]

operations = [
migrations.AddField(
model_name="product",
name="verbruiksobject",
field=models.JSONField(
blank=True,
help_text="Verbruiksobject van dit product. Wordt gevalideerd met het `verbruiksobject_schema` uit het product type.",
null=True,
verbose_name="verbruiksobject",
),
),
]
27 changes: 27 additions & 0 deletions src/open_producten/producten/models/product.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,23 @@ class Product(BasePublishableModel):
blank=True,
)

verbruiksobject = models.JSONField(
_("verbruiksobject"),
null=True,
blank=True,
help_text=_(
"Verbruiksobject van dit product. Wordt gevalideerd met het `verbruiksobject_schema` uit het product type."
),
)

class Meta:
verbose_name = _("Product")
verbose_name_plural = _("Producten")

def clean(self):
validate_bsn_or_kvk(self.bsn, self.kvk)
validate_dates(self.start_datum, self.eind_datum)
validate_verbruiksobject(self.verbruiksobject, self.product_type)

def __str__(self):
return f"{self.bsn if self.bsn else self.kvk} {self.product_type.naam}"
Expand All @@ -77,3 +87,20 @@ def validate_dates(start_datum, eind_datum):
)
}
)


def validate_verbruiksobject(verbruiksobject, product_type):
try:
if (
verbruiksobject is not None
and product_type.verbruiksobject_schema is not None
):
product_type.verbruiksobject_schema.validate(verbruiksobject)
except ValidationError:
raise ValidationError(
{
"verbruiksobject": _(
"Het verbruiksobject komt niet overeen met het schema gedefinieerd op het product type."
)
},
)
6 changes: 6 additions & 0 deletions src/open_producten/producten/serializers/product.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from open_producten.producten.serializers.validators import (
BsnOrKvkValidator,
DateValidator,
VerbruiksObjectValidator,
)
from open_producten.producttypen.models import ProductType
from open_producten.producttypen.serializers.thema import NestedProductTypeSerializer
Expand All @@ -19,3 +20,8 @@ class Meta:
model = Product
fields = "__all__"
validators = [BsnOrKvkValidator(), DateValidator()]
validators = [
BsnOrKvkValidator(),
DateValidator(),
VerbruiksObjectValidator(),
]
22 changes: 21 additions & 1 deletion src/open_producten/producten/serializers/validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@

from rest_framework import serializers

from open_producten.producten.models.product import validate_bsn_or_kvk, validate_dates
from open_producten.producten.models.product import (
validate_bsn_or_kvk,
validate_dates,
validate_verbruiksobject,
)
from open_producten.utils.serializers import get_from_serializer_data_or_instance


Expand Down Expand Up @@ -32,3 +36,19 @@ def __call__(self, value, serializer):
validate_dates(start_datum, eind_datum)
except ValidationError as e:
raise serializers.ValidationError(e.message_dict)


class VerbruiksObjectValidator:
requires_context = True

def __call__(self, value, serializer):
verbruiksobject = get_from_serializer_data_or_instance(
"verbruiksobject", value, serializer
)
product_type = get_from_serializer_data_or_instance(
"product_type", value, serializer
)
try:
validate_verbruiksobject(verbruiksobject, product_type)
except ValidationError as e:
raise serializers.ValidationError(e.message_dict)
80 changes: 80 additions & 0 deletions src/open_producten/producten/tests/api/test_product.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from django.urls import reverse
from django.utils.translation import gettext as _

from django_json_schema_model.models import JsonSchema
from freezegun import freeze_time
from rest_framework import status
from rest_framework.exceptions import ErrorDetail
Expand Down Expand Up @@ -60,6 +61,7 @@ def test_create_product(self):
"id": str(product.id),
"bsn": product.bsn,
"kvk": product.kvk,
"verbruiksobject": None,
"gepubliceerd": False,
"start_datum": str(product.start_datum),
"eind_datum": str(product.eind_datum),
Expand All @@ -79,6 +81,81 @@ def test_create_product(self):
}
self.assertEqual(response.data, expected_data)

def test_create_product_with_verbruiksobject(self):
json_schema = JsonSchema.objects.create(
name="json-schema",
schema={
"type": "object",
"properties": {"naam": {"type": "string"}},
"required": ["naam"],
},
)

self.product_type.verbruiksobject_schema = json_schema
self.product_type.save()

data = self.data | {"verbruiksobject": {"naam": "Test"}}
response = self.client.post(self.path, data)

self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(Product.objects.count(), 1)
product = Product.objects.first()
product_type = product.product_type
expected_data = {
"id": str(product.id),
"bsn": product.bsn,
"kvk": product.kvk,
"verbruiksobject": {"naam": "Test"},
"gepubliceerd": False,
"start_datum": str(product.start_datum),
"eind_datum": str(product.eind_datum),
"aanmaak_datum": product.aanmaak_datum.astimezone().isoformat(),
"update_datum": product.update_datum.astimezone().isoformat(),
"product_type": {
"id": str(product_type.id),
"naam": product_type.naam,
"samenvatting": product_type.samenvatting,
"beschrijving": product_type.beschrijving,
"uniforme_product_naam": product_type.uniforme_product_naam.uri,
"gepubliceerd": True,
"aanmaak_datum": product_type.aanmaak_datum.astimezone().isoformat(),
"update_datum": product_type.update_datum.astimezone().isoformat(),
"keywords": [],
},
}
self.assertEqual(response.data, expected_data)

def test_create_product_with_invalid_verbruiksobject(self):
json_schema = JsonSchema.objects.create(
name="json-schema",
schema={
"type": "object",
"properties": {"naam": {"type": "string"}},
"required": ["naam"],
},
)

self.product_type.verbruiksobject_schema = json_schema
self.product_type.save()

data = self.data | {"verbruiksobject": {"naam": 123}}
response = self.client.post(self.path, data)

self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(
response.data,
{
"verbruiksobject": [
ErrorDetail(
string=_(
"Het verbruiksobject komt niet overeen met het schema gedefinieerd op het product type."
),
code="invalid",
)
]
},
)

def test_create_product_without_bsn_or_kvk_returns_error(self):
data = self.data.copy()
data.pop("bsn")
Expand Down Expand Up @@ -157,6 +234,7 @@ def test_read_producten(self):
"id": str(product1.id),
"bsn": product1.bsn,
"kvk": product1.kvk,
"verbruiksobject": None,
"gepubliceerd": False,
"start_datum": None,
"eind_datum": None,
Expand All @@ -178,6 +256,7 @@ def test_read_producten(self):
"id": str(product2.id),
"bsn": product2.bsn,
"kvk": product2.kvk,
"verbruiksobject": None,
"gepubliceerd": False,
"start_datum": None,
"eind_datum": None,
Expand Down Expand Up @@ -210,6 +289,7 @@ def test_read_product(self):
"id": str(product.id),
"bsn": "111222333",
"kvk": None,
"verbruiksobject": None,
"gepubliceerd": False,
"start_datum": None,
"eind_datum": None,
Expand Down
Loading
Loading