Skip to content

Commit

Permalink
[#11] product needs at least 1 eigenaar
Browse files Browse the repository at this point in the history
  • Loading branch information
Floris272 committed Mar 7, 2025
1 parent f5e8e17 commit e1525e4
Show file tree
Hide file tree
Showing 7 changed files with 117 additions and 57 deletions.
18 changes: 17 additions & 1 deletion src/open_producten/producten/admin/eigenaar.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,26 @@
from django.contrib import admin
from django.core.exceptions import ValidationError
from django.utils.translation import gettext_lazy as _

from open_producten.logging.admin_tools import AuditLogInlineformset
from open_producten.producten.models import Eigenaar


class EigenaarInlineFormSet(AuditLogInlineformset):

def clean(self):
"""Check that at least one optie has been added."""
super().clean()
if any(self.errors):
return
if not any(
cleaned_data and not cleaned_data.get("DELETE", False)
for cleaned_data in self.cleaned_data
):
raise ValidationError(_("Er is minimaal één optie vereist."))


class EigenaarInline(admin.TabularInline):
formset = AuditLogInlineformset
formset = EigenaarInlineFormSet
model = Eigenaar
extra = 1
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Generated by Django 4.2.17 on 2025-03-05 16:43
# Generated by Django 4.2.17 on 2025-03-07 14:40

import django.core.validators
from django.db import migrations, models
Expand Down Expand Up @@ -74,10 +74,9 @@ class Migration(migrations.Migration):
"klantnummer",
models.CharField(
blank=True,
help_text="Uniek identificerend nummer dat tijdens communicatie tussen mensen kan worden gebruikt om het specifieke klantcontact aan te duiden.",
max_length=10,
help_text="generiek veld voor de identificatie van een klant.",
max_length=50,
null=True,
validators=[django.core.validators.validate_integer],
verbose_name="Klantnummer",
),
),
Expand Down
10 changes: 3 additions & 7 deletions src/open_producten/producten/models/eigenaar.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from django.core.exceptions import ValidationError
from django.core.validators import MinLengthValidator, RegexValidator, validate_integer
from django.core.validators import MinLengthValidator, RegexValidator
from django.db import models
from django.utils.translation import gettext_lazy as _

Expand Down Expand Up @@ -48,12 +48,8 @@ class Eigenaar(BaseModel):

klantnummer = models.CharField(
_("Klantnummer"),
help_text=_(
"Uniek identificerend nummer dat tijdens communicatie tussen mensen kan "
"worden gebruikt om het specifieke klantcontact aan te duiden."
),
validators=[validate_integer],
max_length=10,
help_text=_("generiek veld voor de identificatie van een klant."),
max_length=50,
null=True,
blank=True,
)
Expand Down
8 changes: 7 additions & 1 deletion src/open_producten/producten/serializers/product.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from django.db import transaction
from django.utils.translation import gettext_lazy as _

from rest_framework import serializers

Expand All @@ -18,7 +19,7 @@ class ProductSerializer(serializers.ModelSerializer):
product_type_id = serializers.PrimaryKeyRelatedField(
write_only=True, queryset=ProductType.objects.all(), source="product_type"
)
eigenaren = EigenaarSerializer(many=True, required=False)
eigenaren = EigenaarSerializer(many=True)

class Meta:
model = Product
Expand All @@ -29,6 +30,11 @@ class Meta:
NestedObjectsValidator("eigenaren", Eigenaar),
]

def validate_eigenaren(self, eigenaren: list[Eigenaar]) -> list[Eigenaar]:
if len(eigenaren) == 0:
raise serializers.ValidationError(_("Er is minimaal één eigenaar vereist."))
return eigenaren

@transaction.atomic()
def create(self, validated_data):
eigenaren = validated_data.pop("eigenaren", [])
Expand Down
110 changes: 76 additions & 34 deletions src/open_producten/producten/tests/api/test_product.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,15 @@ def test_required_fields(self):
"frequentie": [
ErrorDetail(string=_("This field is required."), code="required")
],
"eigenaren": [
ErrorDetail(_("This field is required."), code="required")
],
},
)

def test_create_product(self):
response = self.client.post(self.path, self.data)
data = self.data | {"eigenaren": [{"kvk_nummer": "12345678"}]}
response = self.client.post(self.path, data)

self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(Product.objects.count(), 1)
Expand All @@ -73,7 +77,15 @@ def test_create_product(self):
"frequentie": product.frequentie,
"aanmaak_datum": product.aanmaak_datum.astimezone().isoformat(),
"update_datum": product.update_datum.astimezone().isoformat(),
"eigenaren": [],
"eigenaren": [
{
"bsn_nummer": None,
"kvk_nummer": "12345678",
"vestigingsnummer": None,
"klantnummer": None,
"id": str(product.eigenaren.first().id),
}
],
"product_type": {
"id": str(product_type.id),
"code": product_type.code,
Expand All @@ -88,7 +100,11 @@ def test_create_product(self):
self.assertEqual(response.data, expected_data)

def test_create_product_with_not_allowed_state(self):
response = self.client.post(self.path, self.data | {"status": "actief"})
data = self.data | {
"status": "actief",
"eigenaren": [{"kvk_nummer": "12345678"}],
}
response = self.client.post(self.path, data)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(
response.data,
Expand All @@ -105,7 +121,11 @@ def test_create_product_with_not_allowed_state(self):
)

def test_create_product_with_allowed_state(self):
response = self.client.post(self.path, self.data | {"status": "gereed"})
data = self.data | {
"status": "gereed",
"eigenaren": [{"kvk_nummer": "12345678"}],
}
response = self.client.post(self.path, data)

self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(Product.objects.count(), 1)
Expand Down Expand Up @@ -165,6 +185,22 @@ def test_create_product_with_vestigingsnummer_only(self):
},
)

def test_create_product_without_eigenaren(self):
response = self.client.post(self.path, self.data | {"eigenaren": []})

self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(
response.data,
{
"eigenaren": [
ErrorDetail(
string=_("Er is minimaal één eigenaar vereist."),
code="invalid",
)
]
},
)

def test_create_product_with_vestigingsnummer_and_kvk(self):
data = self.data | {
"eigenaren": [{"vestingsnummer": "123", "kvk_nummer": "12345678"}]
Expand All @@ -181,6 +217,7 @@ def test_update_product(self):
data = self.data | {
"eind_datum": datetime.date(2025, 12, 31),
"product_type_id": product_type.id,
"eigenaren": [{"kvk_nummer": "12345678"}],
}
response = self.client.put(self.detail_path(product), data)

Expand All @@ -190,7 +227,10 @@ def test_update_product(self):

def test_update_product_with_not_allowed_state(self):
product = ProductFactory.create()
data = self.data.copy() | {"status": "actief"}
data = self.data.copy() | {
"status": "actief",
"eigenaren": [{"kvk_nummer": "12345678"}],
}
response = self.client.put(self.detail_path(product), data)

self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
Expand All @@ -210,46 +250,40 @@ def test_update_product_with_not_allowed_state(self):

def test_update_product_removing_eigenaren(self):
product = ProductFactory.create()
EigenaarFactory.create(product_id=product.id, bsn_nummer="111222333")
EigenaarFactory.create(product_id=product.id, kvk_nummer="12345678")
EigenaarFactory.create(product=product, bsn_nummer="111222333")
EigenaarFactory.create(product=product, kvk_nummer="12345678")

expected_error = {
"eigenaren": [
ErrorDetail(
string=_("Er is minimaal één eigenaar vereist."),
code="invalid",
)
]
}

with self.subTest("PUT"):
response = self.client.put(
self.detail_path(product), self.data | {"eigenaren": []}
)

self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(Eigenaar.objects.count(), 0)

with self.subTest("PATCH"):
response = self.client.patch(self.detail_path(product), {"eigenaren": []})

self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(Eigenaar.objects.count(), 0)

def test_update_keeps_existing_eigenaren(self):
product = ProductFactory.create()
EigenaarFactory.create(product_id=product.id, bsn_nummer="111222333")
EigenaarFactory.create(product_id=product.id, kvk_nummer="12345678")

with self.subTest("PUT"):
response = self.client.put(self.detail_path(product), self.data)

self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(Eigenaar.objects.count(), 2)
self.assertEqual(response.data, expected_error)

with self.subTest("PATCH"):
response = self.client.patch(self.detail_path(product), {})
response = self.client.patch(self.detail_path(product), {"eigenaren": []})

self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(Eigenaar.objects.count(), 2)
self.assertEqual(response.data, expected_error)

def test_update_updating_and_removing_eigenaren(self):
product = ProductFactory.create()
eigenaar_to_be_updated = EigenaarFactory.create(
product_id=product.id, bsn_nummer="111222333"
product=product, bsn_nummer="111222333"
)
EigenaarFactory.create(product_id=product.id, kvk_nummer="12345678")
EigenaarFactory.create(product=product, kvk_nummer="12345678")

self.maxDiff = None

Expand Down Expand Up @@ -290,7 +324,7 @@ def test_update_updating_and_removing_eigenaren(self):

def test_update_creating_and_removing_eigenaren(self):
product = ProductFactory.create()
EigenaarFactory.create(product_id=product.id, kvk_nummer="12345678")
EigenaarFactory.create(product=product, kvk_nummer="12345678")

data = {"eigenaren": [{"klantnummer": "1234"}]}

Expand Down Expand Up @@ -372,7 +406,7 @@ def test_update_product_with_eigenaar_id_not_existing(self):
def test_update_product_with_duplicate_eigenaren_ids(self):
product = ProductFactory.create()
eigenaar_to_be_updated = EigenaarFactory.create(
product_id=product.id, bsn_nummer="111222333"
product=product, bsn_nummer="111222333"
)

expected_error = {
Expand Down Expand Up @@ -525,7 +559,10 @@ def test_update_state_and_dates_are_not_checked_when_not_changed(self):

product = ProductFactory.create(**data)

response = self.client.put(self.detail_path(product), data)
response = self.client.put(
self.detail_path(product),
data | {"eigenaren": [{"kvk_nummer": "12345678"}]},
)
self.assertEqual(response.status_code, status.HTTP_200_OK)

@freeze_time("2025-11-30")
Expand Down Expand Up @@ -602,10 +639,15 @@ def test_update_state_and_dates_are_checked_when_product_type_is_changed(self):
} | test["field"]

product = ProductFactory.create(**data)
EigenaarFactory(product=product)

response = self.client.put(
self.detail_path(product),
data | {"product_type_id": new_product_type.id},
data
| {
"product_type_id": new_product_type.id,
"eigenaren": [{"kvk_nummer": "12345678"}],
},
)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

Expand Down Expand Up @@ -672,7 +714,7 @@ def test_update_state_and_dates_are_checked_when_changed(self):

response = self.client.put(
self.detail_path(product),
data | test["field"],
data | test["field"] | {"eigenaren": [{"kvk_nummer": "12345678"}]},
)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

Expand Down
5 changes: 3 additions & 2 deletions src/open_producten/producten/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,12 @@
"eind_datum": "2026-12-01",
"product_type_id": "95792000-d57f-4d3a-b14c-c4c7aa964907",
"gepubliceerd": False,
"bsn": "111222333",
"status": "gereed",
"prijs": "20.20",
"frequentie": "eenmalig",
"eigenaren": [
{"bsn_nummer": "111222333"},
],
},
request_only=True,
)
Expand All @@ -46,7 +48,6 @@
"gepubliceerd": True,
"start_datum": "2019-08-24",
"eind_datum": "2019-08-24",
"bsn": "string",
"status": "gereed",
},
request_only=True,
Expand Down
Loading

0 comments on commit e1525e4

Please sign in to comment.