Skip to content

Commit

Permalink
fix updates to product when toegestane statussen have been changed on…
Browse files Browse the repository at this point in the history
… the product type
  • Loading branch information
Floris272 committed Jan 28, 2025
1 parent 04fb936 commit 245cc2c
Show file tree
Hide file tree
Showing 12 changed files with 357 additions and 109 deletions.
13 changes: 0 additions & 13 deletions src/open_producten/locaties/tests/api/test_organisatie.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,19 +66,6 @@ def test_create_organisatie(self):
}
self.assertEqual(response.data, expected_data)

def test_create_organisatie_without_code_returns_error(self):
data = self.data.copy()
data.pop("code")
response = self.client.post(self.path, data)

self.assertEqual(response.status_code, 400)
self.assertEqual(
response.data,
{
"code": [ErrorDetail(string="Dit veld is vereist.", code="required")],
},
)

def test_update_organisatie(self):
data = self.data | {"naam": "update"}
response = self.client.put(self.detail_path, data)
Expand Down
31 changes: 30 additions & 1 deletion src/open_producten/producten/admin/product.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@

from open_producten.logging.service import AdminAuditLogMixin, get_logs_link
from open_producten.producten.models import Product
from open_producten.producten.models.product import (
validate_eind_datum,
validate_start_datum,
validate_status,
)


class ProductAdminForm(forms.ModelForm):
Expand All @@ -22,6 +27,30 @@ def __init__(self, *args, **kwargs):
widget=forms.Select,
)

def clean(self):
"""
The toegestane_statussen on the product type should be changeable without it affecting existing products.
This means that the status & dates should only be validated when they (or the product type is changed) otherwise it will raise an exception.
"""
super().clean()

product_type_changed = "product_type" in self.changed_data

if "status" in self.changed_data or product_type_changed:
validate_status(
self.cleaned_data["status"], self.cleaned_data["product_type"]
)

if "start_datum" in self.changed_data or product_type_changed:
validate_start_datum(
self.cleaned_data["start_datum"], self.cleaned_data["product_type"]
)

if "eind_datum" in self.changed_data or product_type_changed:
validate_eind_datum(
self.cleaned_data["eind_datum"], self.cleaned_data["product_type"]
)


@admin.register(Product)
class ProductAdmin(AdminAuditLogMixin, admin.ModelAdmin):
Expand Down Expand Up @@ -51,7 +80,7 @@ def product_type_name(self, obj):
def get_queryset(self, request):
return super().get_queryset(request).select_related("product_type")

@admin.display(description=_("actions"))
@admin.display(description=_("acties"))
def show_actions(self, obj: Product) -> str:
actions = [
get_logs_link(obj),
Expand Down
43 changes: 25 additions & 18 deletions src/open_producten/producten/models/product.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ class Product(BasePublishableModel):
help_text=_(
"De status opties worden bepaald door het veld 'toegestane statussen' van het gerelateerde product type."
),
default=ProductStateChoices.INITIEEL.value,
default=ProductStateChoices.INITIEEL,
)

kvk = models.CharField(
Expand All @@ -70,6 +70,7 @@ class Meta:
verbose_name = _("Product")
verbose_name_plural = _("Producten")

# TODO move to product admin & try to update based on changed_data?
@property
def status_choices(self):
"""
Expand All @@ -80,16 +81,19 @@ def status_choices(self):
]
if not hasattr(self, "product_type"):
return choices
return choices + [
choice
for choice in ProductStateChoices.choices
if choice[0] in self.product_type.toegestane_statussen
]
return set(
choices
+ [
choice
for choice in ProductStateChoices.choices
if choice[0] in self.product_type.toegestane_statussen
or choice[0] == self.status
]
)

def clean(self):
validate_bsn_or_kvk(self.bsn, self.kvk)
validate_status(self.status, self.product_type)
validate_dates(self.start_datum, self.eind_datum, self.product_type)
validate_dates(self.start_datum, self.eind_datum)

def save(self, *args, **kwargs):
self.handle_start_datum()
Expand Down Expand Up @@ -137,7 +141,7 @@ def validate_bsn_or_kvk(bsn, kvk):

def validate_status(status, product_type):
if (
status != ProductStateChoices.INITIEEL.value
status != ProductStateChoices.INITIEEL
and status not in product_type.toegestane_statussen
):
raise ValidationError(
Expand All @@ -152,19 +156,20 @@ def validate_status(status, product_type):
)


def validate_dates(start_datum, eind_datum, product_type):
if (start_datum == eind_datum) and start_datum is not None:
def validate_dates(start_datum, eind_datum):
if start_datum and eind_datum and (start_datum >= eind_datum):
raise ValidationError(
{
_(
"De start datum en eind_datum van een product mogen niet op dezelfde dag vallen."
)
}
_(
"De eind datum van een product mag niet op een eerdere of dezelfde dag vallen als de start datum."
)
)


def validate_start_datum(start_datum, product_type):

if (
start_datum
and ProductStateChoices.ACTIEF.value not in product_type.toegestane_statussen
and ProductStateChoices.ACTIEF not in product_type.toegestane_statussen
):
raise ValidationError(
{
Expand All @@ -174,9 +179,11 @@ def validate_dates(start_datum, eind_datum, product_type):
}
)


def validate_eind_datum(eind_datum, product_type):
if (
eind_datum
and ProductStateChoices.VERLOPEN.value not in product_type.toegestane_statussen
and ProductStateChoices.VERLOPEN not in product_type.toegestane_statussen
):
raise ValidationError(
{
Expand Down
52 changes: 41 additions & 11 deletions src/open_producten/producten/serializers/validators.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
from django.core.exceptions import ValidationError

from rest_framework import serializers
from rest_framework.serializers import Serializer

from open_producten.producten.models.product import (
validate_bsn_or_kvk,
validate_dates,
validate_eind_datum,
validate_start_datum,
validate_status,
)
from open_producten.utils.serializers import get_from_serializer_data_or_instance
Expand All @@ -22,16 +25,33 @@ def __call__(self, value, serializer):
raise serializers.ValidationError({"bsn_or_kvk": e.message})


def get_from_serializer_data_or_instance_and_changed(
field: str, data: dict, serializer: Serializer
):
value = get_from_serializer_data_or_instance(field, data, serializer)
changed = data.get(field) is not None and (
serializer.instance is None
or data.get(field) != getattr(serializer.instance, field)
)
return value, changed


class StatusValidator:
requires_context = True

def __call__(self, value, serializer):
status = get_from_serializer_data_or_instance("status", value, serializer)
product_type = get_from_serializer_data_or_instance(
"product_type", value, serializer
status, status_changed = get_from_serializer_data_or_instance_and_changed(
"status", value, serializer
)
product_type, product_type_changed = (
get_from_serializer_data_or_instance_and_changed(
"product_type", value, serializer
)
)

try:
validate_status(status, product_type)
if status_changed or product_type_changed:
validate_status(status, product_type)
except ValidationError as e:
raise serializers.ValidationError(e.message_dict)

Expand All @@ -40,16 +60,26 @@ class DateValidator:
requires_context = True

def __call__(self, value, serializer):
start_datum = get_from_serializer_data_or_instance(
"start_datum", value, serializer
start_datum, start_datum_changed = (
get_from_serializer_data_or_instance_and_changed(
"start_datum", value, serializer
)
)
eind_datum = get_from_serializer_data_or_instance(
"eind_datum", value, serializer
eind_datum, eind_datum_changed = (
get_from_serializer_data_or_instance_and_changed(
"eind_datum", value, serializer
)
)
product_type = get_from_serializer_data_or_instance(
"product_type", value, serializer
product_type, product_type_changed = (
get_from_serializer_data_or_instance_and_changed(
"product_type", value, serializer
)
)
try:
validate_dates(start_datum, eind_datum, product_type)
if start_datum_changed or product_type_changed:
validate_start_datum(start_datum, product_type)
if eind_datum_changed or product_type_changed:
validate_eind_datum(eind_datum, product_type)
validate_dates(start_datum, eind_datum)
except ValidationError as e:
raise serializers.ValidationError(e.message_dict)
Loading

0 comments on commit 245cc2c

Please sign in to comment.