Skip to content

Commit

Permalink
Merge pull request #269 from maykinmedia/feature/246-default-address
Browse files Browse the repository at this point in the history
 ✨ [#246] Add is_standaard_adres for DigitaalAdres
  • Loading branch information
stevenbal authored Nov 22, 2024
2 parents 8d7f179 + 8076b6a commit f37d5a2
Show file tree
Hide file tree
Showing 5 changed files with 222 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from django.db import transaction
from django.utils.translation import gettext_lazy as _

from rest_framework import serializers
from rest_framework import serializers, validators

from openklant.components.klantinteracties.api.serializers.constants import (
SERIALIZER_PATH,
Expand Down Expand Up @@ -79,6 +79,7 @@ class Meta:
"verstrekt_door_partij",
"adres",
"soort_digitaal_adres",
"is_standaard_adres",
"omschrijving",
)
extra_kwargs = {
Expand All @@ -90,6 +91,20 @@ class Meta:
},
}

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

if "soort_digitaal_adres" in self.fields:
# Avoid validating the UniqueConstraint for `soort_digitaal_adres` with
# `is_standaard_adres=True`. We want to enforce the constraint at the database
# level, but not via the API, because setting a new default sets all other
# `is_standaard_adres=False` (via DigitaalAdres.save)
self.fields["soort_digitaal_adres"].validators = [
validator
for validator in self.fields["soort_digitaal_adres"].validators
if not isinstance(validator, validators.UniqueValidator)
]

def validate_adres(self, adres):
"""
Define the validator here, to avoid DRF spectacular marking the format for
Expand All @@ -101,6 +116,21 @@ def validate_adres(self, adres):
OptionalEmailValidator()(adres, soort_digitaal_adres)
return adres

def validate(self, attrs):
partij = get_field_value(self, attrs, "partij")
is_standaard_adres = get_field_value(self, attrs, "is_standaard_adres")
if is_standaard_adres and not partij:
raise serializers.ValidationError(
{
"is_standaard_adres": _(
"`is_standaard_adres` kan alleen gezet worden "
"als `verstrekt_door_partij` niet leeg is."
)
}
)

return super().validate(attrs)

@transaction.atomic
def update(self, instance, validated_data):
if "partij" in validated_data:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from vng_api_common.tests import reverse

from openklant.components.klantinteracties.constants import SoortDigitaalAdres
from openklant.components.klantinteracties.models import DigitaalAdres
from openklant.components.klantinteracties.models.tests.factories.digitaal_adres import (
DigitaalAdresFactory,
)
Expand Down Expand Up @@ -73,6 +74,7 @@ def test_create_digitaal_adres(self):
self.assertEqual(data["verstrektDoorPartij"], None)
self.assertEqual(data["adres"], "[email protected]")
self.assertEqual(data["omschrijving"], "omschrijving")
self.assertEqual(data["isStandaardAdres"], False)

with self.subTest("with_betrokkene_and_partij"):
partij = PartijFactory.create()
Expand Down Expand Up @@ -160,6 +162,82 @@ def test_create_digitaal_adres_email_validation(self):
)
self.assertEqual(digitaal_adres.adres, "0612345678")

def test_create_digitaal_adres_is_standaard_adres(self):
"""
Creating a DigitaalAdres with isStandaardAdres=True should make other existing
DigitaalAdressen no longer the default
"""
# Since this has a different Partij, the value of `is_standaard_adres` should stay `True`
partij1, partij2 = PartijFactory.create_batch(2)
existing_adres_different_partij = DigitaalAdresFactory.create(
partij=partij1, is_standaard_adres=True, soort_digitaal_adres="email"
)
# This adres has the same `soort_digitaal_adres` and `partij`, so the value of
# `is_standaard_adres` should be changed to `False` if we change another one to `True`
existing_adres = DigitaalAdresFactory.create(
is_standaard_adres=True, soort_digitaal_adres="email", partij=partij2
)

list_url = reverse("klantinteracties:digitaaladres-list")
data = {
"verstrektDoorBetrokkene": None,
"verstrektDoorPartij": {"uuid": str(partij2.uuid)},
"soortDigitaalAdres": "email",
"adres": "[email protected]",
"omschrijving": "omschrijving",
"isStandaardAdres": True,
}

response = self.client.post(list_url, data)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)

data = response.json()

self.assertEqual(data["isStandaardAdres"], True)

existing_adres_different_partij.refresh_from_db()
existing_adres.refresh_from_db()
new_adres = DigitaalAdres.objects.last()

self.assertEqual(existing_adres_different_partij.is_standaard_adres, True)
self.assertEqual(existing_adres.is_standaard_adres, False)
self.assertEqual(new_adres.is_standaard_adres, True)

def test_create_digitaal_adres_is_standaard_adres_without_partij_not_possible(self):
"""
Creating a DigitaalAdres with isStandaardAdres=True should not be possible with
verstrektDoorPartij=None
"""
betrokkene = BetrokkeneFactory.create()

list_url = reverse("klantinteracties:digitaaladres-list")
data = {
"verstrektDoorBetrokkene": {"uuid": str(betrokkene.uuid)},
"verstrektDoorPartij": None,
"soortDigitaalAdres": "email",
"adres": "[email protected]",
"omschrijving": "omschrijving",
"isStandaardAdres": True,
}

response = self.client.post(list_url, data)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

data = response.json()
self.assertEqual(
data["invalidParams"],
[
{
"name": "isStandaardAdres",
"code": "invalid",
"reason": _(
"`is_standaard_adres` kan alleen gezet worden als `verstrekt_door_partij` niet leeg is."
),
}
],
)
self.assertEqual(DigitaalAdres.objects.count(), 0)

def test_update_digitaal_adres(self):
betrokkene, betrokkene2 = BetrokkeneFactory.create_batch(2)
partij, partij2 = PartijFactory.create_batch(2)
Expand Down Expand Up @@ -226,6 +304,55 @@ def test_update_digitaal_adres(self):
self.assertEqual(data["adres"], "0721434543")
self.assertEqual(data["omschrijving"], "changed")

def test_update_digitaal_adres_is_standaard_adres(self):
"""
Creating a DigitaalAdres with isStandaardAdres=True should make other existing
DigitaalAdressen no longer the default
"""
partij1, partij2 = PartijFactory.create_batch(2)
# Since this has a different Partij, the value of `is_standaard_adres` should stay `True`
existing_adres_different_partij = DigitaalAdresFactory.create(
partij=partij1, is_standaard_adres=True, soort_digitaal_adres="email"
)
# This adres has the same `soort_digitaal_adres` and `partij`, so the value of
# `is_standaard_adres` should be changed to `False` if we change another one to `True`
existing_adres = DigitaalAdresFactory.create(
is_standaard_adres=True, soort_digitaal_adres="email", partij=partij2
)
digitaal_adres = DigitaalAdresFactory.create(
partij=partij2,
soort_digitaal_adres="email",
adres="adres",
omschrijving="omschrijving",
)
detail_url = reverse(
"klantinteracties:digitaaladres-detail",
kwargs={"uuid": str(digitaal_adres.uuid)},
)

data = {
"verstrektDoorBetrokkene": {"uuid": str(digitaal_adres.betrokkene.uuid)},
"verstrektDoorPartij": {"uuid": str(partij2.uuid)},
"soortDigitaalAdres": "email",
"isStandaardAdres": True,
"adres": "[email protected]",
"omschrijving": "changed",
}

response = self.client.put(detail_url, data)

data = response.json()

self.assertEqual(data["isStandaardAdres"], True)

existing_adres_different_partij.refresh_from_db()
existing_adres.refresh_from_db()
digitaal_adres.refresh_from_db()

self.assertEqual(existing_adres_different_partij.is_standaard_adres, True)
self.assertEqual(existing_adres.is_standaard_adres, False)
self.assertEqual(digitaal_adres.is_standaard_adres, True)

def test_partial_update_digitaal_adres(self):
betrokkene = BetrokkeneFactory.create()
partij = PartijFactory.create()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Generated by Django 4.2.15 on 2024-10-31 08:27

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("klantinteracties", "0021_alter_digitaaladres_betrokkene"),
]

operations = [
migrations.AddField(
model_name="digitaaladres",
name="is_standaard_adres",
field=models.BooleanField(
default=False,
help_text="Geeft aan of dit digitaal adres het standaard adres is voor het `soort_digitaal_adres`",
verbose_name="Is standaard adres",
),
),
migrations.AddConstraint(
model_name="digitaaladres",
constraint=models.UniqueConstraint(
condition=models.Q(("is_standaard_adres", True)),
fields=("partij", "soort_digitaal_adres"),
name="unique_default_per_partij_and_soort",
),
),
]
26 changes: 26 additions & 0 deletions src/openklant/components/klantinteracties/models/digitaal_adres.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,13 @@ class DigitaalAdres(APIMixin, models.Model):
max_length=255,
choices=SoortDigitaalAdres.choices,
)
is_standaard_adres = models.BooleanField(
_("Is standaard adres"),
help_text=_(
"Geeft aan of dit digitaal adres het standaard adres is voor het `soort_digitaal_adres`"
),
default=False,
)
adres = models.CharField(
_("adres"),
help_text=_(
Expand All @@ -55,6 +62,25 @@ class DigitaalAdres(APIMixin, models.Model):

class Meta:
verbose_name = _("digitaal adres")
constraints = [
models.UniqueConstraint(
fields=["partij", "soort_digitaal_adres"],
condition=models.Q(is_standaard_adres=True),
name="unique_default_per_partij_and_soort",
)
]

def __str__(self):
return f"{self.betrokkene} - {self.adres}"

def save(self, *args, **kwargs):
if self.is_standaard_adres:
# Because there can only be one default address per `soort_digitaal_adres`
# and `partij`, mark all other addresses as non-default
DigitaalAdres.objects.filter(
soort_digitaal_adres=self.soort_digitaal_adres,
partij=self.partij,
is_standaard_adres=True,
).update(is_standaard_adres=False)

super().save(*args, **kwargs)
8 changes: 8 additions & 0 deletions src/openklant/components/klantinteracties/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3463,6 +3463,10 @@ components:
- $ref: '#/components/schemas/SoortDigitaalAdresEnum'
description: Typering van het digitale adres die aangeeft via welk(e) kanaal
of kanalen met dit adres contact kan worden opgenomen.
isStandaardAdres:
type: boolean
description: Geeft aan of dit digitaal adres het standaard adres is voor
het `soort_digitaal_adres`
omschrijving:
type: string
description: Omschrijving van het digitaal adres.
Expand Down Expand Up @@ -4868,6 +4872,10 @@ components:
- $ref: '#/components/schemas/SoortDigitaalAdresEnum'
description: Typering van het digitale adres die aangeeft via welk(e) kanaal
of kanalen met dit adres contact kan worden opgenomen.
isStandaardAdres:
type: boolean
description: Geeft aan of dit digitaal adres het standaard adres is voor
het `soort_digitaal_adres`
omschrijving:
type: string
description: Omschrijving van het digitaal adres.
Expand Down

0 comments on commit f37d5a2

Please sign in to comment.