From d203145192e04882ed7b321fa410072c0944cf2a Mon Sep 17 00:00:00 2001 From: bart-maykin Date: Wed, 17 Jan 2024 18:19:01 +0100 Subject: [PATCH] :sparkles: [#69] added CategorieRelatie model and api endpoint --- .../klantinteracties/admin/partijen.py | 40 ++- .../api/serializers/partijen.py | 87 +++++- .../api/tests/test_partijen.py | 189 +++++++++--- .../components/klantinteracties/api/urls.py | 2 + .../klantinteracties/api/viewsets/partijen.py | 45 ++- ....py => 0008_categorie_categorierelatie.py} | 54 +++- .../klantinteracties/models/partijen.py | 42 ++- .../models/tests/factories/partijen.py | 9 + .../components/klantinteracties/openapi.yaml | 279 ++++++++++++++++-- 9 files changed, 653 insertions(+), 94 deletions(-) rename src/openklant/components/klantinteracties/migrations/{0008_categorie.py => 0008_categorie_categorierelatie.py} (64%) diff --git a/src/openklant/components/klantinteracties/admin/partijen.py b/src/openklant/components/klantinteracties/admin/partijen.py index e9ae3929..fda769d4 100644 --- a/src/openklant/components/klantinteracties/admin/partijen.py +++ b/src/openklant/components/klantinteracties/admin/partijen.py @@ -4,20 +4,37 @@ from ..models.constants import SoortPartij from ..models.digitaal_adres import DigitaalAdres from ..models.klantcontacten import Betrokkene -from ..models.partijen import Categorie, Contactpersoon, Organisatie, Partij, Persoon - - -class CategorieInlineAdmin(admin.StackedInline): - model = Categorie +from ..models.partijen import ( + Categorie, + CategorieRelatie, + Contactpersoon, + Organisatie, + Partij, + Persoon, +) + + +class CategorieRelatieInlineAdmin(admin.StackedInline): + model = CategorieRelatie + readonly_fields = ("uuid",) + autocomplete_fields = ("categorie",) + fields = ( + "uuid", + "categorie", + "begin_datum", + "eind_datum", + ) extra = 0 class BetrokkeneInlineAdmin(admin.StackedInline): + readonly_fields = ("uuid",) model = Betrokkene extra = 0 class DigitaalAdresInlineAdmin(admin.StackedInline): + readonly_fields = ("uuid",) model = DigitaalAdres extra = 0 @@ -28,6 +45,7 @@ class PersoonInlineAdmin(admin.StackedInline): class ContactpersoonInlineAdmin(admin.StackedInline): + readonly_fields = ("uuid",) model = Contactpersoon fk_name = "partij" raw_id_field = ["partij"] @@ -53,7 +71,7 @@ class PartijAdmin(admin.ModelAdmin): ) inlines = ( PersoonInlineAdmin, - CategorieInlineAdmin, + CategorieRelatieInlineAdmin, ContactpersoonInlineAdmin, OrganisatieInlineAdmin, DigitaalAdresInlineAdmin, @@ -135,3 +153,13 @@ def get_contactpersonen(self, obj): def get_organisaties(self, obj): if organisatie := obj.organisatie: return organisatie.naam + + +@admin.register(Categorie) +class CategorieAdmin(admin.ModelAdmin): + readonly_fields = ("uuid",) + search_fields = ("naam",) + fields = ( + "uuid", + "naam", + ) diff --git a/src/openklant/components/klantinteracties/api/serializers/partijen.py b/src/openklant/components/klantinteracties/api/serializers/partijen.py index 63ffaa56..59387824 100644 --- a/src/openklant/components/klantinteracties/api/serializers/partijen.py +++ b/src/openklant/components/klantinteracties/api/serializers/partijen.py @@ -29,6 +29,7 @@ from openklant.components.klantinteracties.models.digitaal_adres import DigitaalAdres from openklant.components.klantinteracties.models.partijen import ( Categorie, + CategorieRelatie, Contactpersoon, Organisatie, Partij, @@ -67,7 +68,11 @@ class CategorieForeignKeySerializer(serializers.HyperlinkedModelSerializer): class Meta: model = Categorie - fields = ("uuid", "url") + fields = ( + "uuid", + "url", + "naam", + ) extra_kwargs = { "uuid": {"required": True, "validators": [categorie_exists]}, "url": { @@ -75,9 +80,38 @@ class Meta: "lookup_field": "uuid", "help_text": _("De unieke URL van deze categorie binnen deze API."), }, + "naam": {"read_only": True}, } +class CategorieRelatieForeignKeySerializer(serializers.HyperlinkedModelSerializer): + """Let op: Dit attribuut is EXPERIMENTEEL.""" + + categorie_naam = serializers.SerializerMethodField( + help_text=_("De naam van de gelinkte categorie.") + ) + + class Meta: + model = Categorie + fields = ( + "uuid", + "url", + "categorie_naam", + ) + extra_kwargs = { + "uuid": {"required": True, "validators": [categorie_exists]}, + "url": { + "view_name": "klantinteracties:categorie-detail", + "lookup_field": "uuid", + "help_text": _("De unieke URL van deze categorie binnen deze API."), + }, + } + + def get_categorie_naam(self, obj): + if obj.categorie: + return obj.categorie.naam + + class PartijIdentificatorForeignkeySerializer(serializers.HyperlinkedModelSerializer): class Meta: model = PartijIdentificator @@ -131,10 +165,35 @@ class Meta: class CategorieSerializer(serializers.HyperlinkedModelSerializer): """Let op: Dit endpoint is EXPERIMENTEEL.""" + class Meta: + model = Categorie + fields = ( + "uuid", + "url", + "naam", + ) + extra_kwargs = { + "uuid": {"read_only": True}, + "url": { + "view_name": "klantinteracties:categorie-detail", + "lookup_field": "uuid", + "help_text": _("De unieke URL van deze categorie binnen deze API."), + }, + } + + +class CategorieRelatieSerializer(serializers.HyperlinkedModelSerializer): + """Let op: Dit endpoint is EXPERIMENTEEL.""" + partij = PartijForeignkeyBaseSerializer( required=True, allow_null=True, - help_text=_("De partij waar de categorie aan gelinkt is."), + help_text=_("De partij waar de categorie relatie aan gelinkt is."), + ) + categorie = CategorieForeignKeySerializer( + required=True, + allow_null=True, + help_text=_("De categorie waar de categorie relatie aan gelinkt is."), ) begin_datum = serializers.DateField( allow_null=True, @@ -146,12 +205,12 @@ class CategorieSerializer(serializers.HyperlinkedModelSerializer): ) class Meta: - model = Categorie + model = CategorieRelatie fields = ( "uuid", "url", "partij", - "naam", + "categorie", "begin_datum", "eind_datum", ) @@ -172,6 +231,12 @@ def update(self, instance, validated_data): validated_data["partij"] = partij + if "categorie" in validated_data: + if categorie := validated_data.pop("categorie", None): + categorie = Categorie.objects.get(uuid=str(categorie.get("uuid"))) + + validated_data["categorie"] = categorie + return super().update(instance, validated_data) @transaction.atomic @@ -184,7 +249,11 @@ def create(self, validated_data): if partij := validated_data.pop("partij"): partij = Partij.objects.get(uuid=str(partij.get("uuid"))) + if categorie := validated_data.pop("categorie"): + categorie = Categorie.objects.get(uuid=str(categorie.get("uuid"))) + validated_data["partij"] = partij + validated_data["categorie"] = categorie return super().create(validated_data) @@ -355,13 +424,13 @@ class PartijSerializer(NestedGegevensGroepMixin, PolymorphicSerializer): many=True, source="betrokkene_set", ) - categorieen = CategorieForeignKeySerializer( + categorie_relaties = CategorieRelatieForeignKeySerializer( read_only=True, help_text=_( - "De Categorieën van een partij: Let op: Dit attribuut is EXPERIMENTEEL." + "De Categorie relaties van een partij: Let op: Dit attribuut is EXPERIMENTEEL." ), many=True, - source="categorie_set", + source="categorierelatie_set", ) digitale_adressen = DigitaalAdresForeignKeySerializer( required=True, @@ -415,7 +484,7 @@ class PartijSerializer(NestedGegevensGroepMixin, PolymorphicSerializer): # 1 level "digitale_adressen": f"{SERIALIZER_PATH}.digitaal_adres.DigitaalAdresSerializer", "betrokkenen": f"{SERIALIZER_PATH}.klantcontacten.BetrokkeneSerializer", - "categorieen": f"{SERIALIZER_PATH}.partijen.CategorieSerializer", + "categorie_relaties": f"{SERIALIZER_PATH}.partijen.CategorieRelatieSerializer", # 2 levels "betrokkenen.had_klantcontact": f"{SERIALIZER_PATH}.klantcontacten.KlantcontactSerializer", # 3 levels @@ -430,7 +499,7 @@ class Meta: "nummer", "interne_notitie", "betrokkenen", - "categorieen", + "categorie_relaties", "digitale_adressen", "voorkeurs_digitaal_adres", "vertegenwoordigde", diff --git a/src/openklant/components/klantinteracties/api/tests/test_partijen.py b/src/openklant/components/klantinteracties/api/tests/test_partijen.py index 789347cd..145f27f8 100644 --- a/src/openklant/components/klantinteracties/api/tests/test_partijen.py +++ b/src/openklant/components/klantinteracties/api/tests/test_partijen.py @@ -8,6 +8,7 @@ ) from openklant.components.klantinteracties.models.tests.factories.partijen import ( CategorieFactory, + CategorieRelatieFactory, ContactpersoonFactory, OrganisatieFactory, PartijFactory, @@ -39,6 +40,18 @@ def test_read_partij(self): self.assertEqual(response.status_code, status.HTTP_200_OK) + with self.subTest("test_categorie_relatie_with_categorie_names"): + categorie = CategorieFactory.create(naam="test-categorie-naam") + CategorieRelatieFactory.create(partij=partij, categorie=categorie) + + response = self.client.get(detail_url) + self.assertEqual(response.status_code, status.HTTP_200_OK) + data = response.json() + + self.assertEqual( + data["categorieRelaties"][0]["categorieNaam"], "test-categorie-naam" + ) + def test_create_partij(self): vertegenwoordigde = PartijFactory.create() digitaal_adres, digitaal_adres2 = DigitaalAdresFactory.create_batch(2) @@ -1729,10 +1742,10 @@ def test_destroy_partij_identificator(self): self.assertEqual(data["count"], 0) -class CategorieTests(APITestCase): - def test_list_categorie(self): - list_url = reverse("klantinteracties:categorie-list") - CategorieFactory.create_batch(2) +class CategorieRelatieTests(APITestCase): + def test_list_categorie_relatie(self): + list_url = reverse("klantinteracties:categorierelatie-list") + CategorieRelatieFactory.create_batch(2) response = self.client.get(list_url) @@ -1741,23 +1754,24 @@ def test_list_categorie(self): data = response.json() self.assertEqual(len(data["results"]), 2) - def test_read_categorie(self): - partij_identificator = CategorieFactory.create() + def test_read_categorie_relatie(self): + categorie_relatie = CategorieRelatieFactory.create() detail_url = reverse( - "klantinteracties:categorie-detail", - kwargs={"uuid": str(partij_identificator.uuid)}, + "klantinteracties:categorierelatie-detail", + kwargs={"uuid": str(categorie_relatie.uuid)}, ) response = self.client.get(detail_url) self.assertEqual(response.status_code, status.HTTP_200_OK) - def test_create_categorie(self): - list_url = reverse("klantinteracties:categorie-list") + def test_create_categorie_relatie(self): + list_url = reverse("klantinteracties:categorierelatie-list") partij = PartijFactory.create() + categorie = CategorieFactory.create(naam="naam") data = { "partij": {"uuid": str(partij.uuid)}, - "naam": "naam", + "categorie": {"uuid": str(categorie.uuid)}, "beginDatum": "2024-01-11", "eindDatum": "2024-01-12", } @@ -1767,7 +1781,8 @@ def test_create_categorie(self): data = response.json() self.assertEqual(data["partij"]["uuid"], str(partij.uuid)) - self.assertEqual(data["naam"], "naam") + self.assertEqual(data["categorie"]["uuid"], str(categorie.uuid)) + self.assertEqual(data["categorie"]["naam"], "naam") self.assertEqual(data["beginDatum"], "2024-01-11") self.assertEqual(data["eindDatum"], "2024-01-12") @@ -1781,11 +1796,133 @@ def test_create_categorie(self): self.assertEqual(data["beginDatum"], today) - def test_update_categorie(self): + def test_update_categorie_relatie(self): partij, partij2 = PartijFactory.create_batch(2) - categorie = CategorieFactory.create( - partij=partij, naam="naam", begin_datum="2024-01-10", eind_datum=None + categorie, categorie2 = CategorieFactory.create_batch(2) + categorie_relatie = CategorieRelatieFactory.create( + partij=partij, + categorie=categorie, + begin_datum="2024-01-11", + eind_datum=None, + ) + detail_url = reverse( + "klantinteracties:categorierelatie-detail", + kwargs={"uuid": str(categorie_relatie.uuid)}, + ) + response = self.client.get(detail_url) + data = response.json() + + self.assertEqual(data["partij"]["uuid"], str(partij.uuid)) + self.assertEqual(data["categorie"]["uuid"], str(categorie.uuid)) + self.assertEqual(data["categorie"]["naam"], categorie.naam) + self.assertEqual(data["beginDatum"], "2024-01-11") + self.assertEqual(data["eindDatum"], None) + + data = { + "partij": {"uuid": str(partij2.uuid)}, + "categorie": {"uuid": str(categorie2.uuid)}, + "beginDatum": "2024-01-12", + "eindDatum": "2024-01-14", + } + + response = self.client.put(detail_url, data) + self.assertEqual(response.status_code, status.HTTP_200_OK) + data = response.json() + + self.assertEqual(data["partij"]["uuid"], str(partij2.uuid)) + self.assertEqual(data["categorie"]["uuid"], str(categorie2.uuid)) + self.assertEqual(data["categorie"]["naam"], categorie2.naam) + self.assertEqual(data["beginDatum"], "2024-01-12") + self.assertEqual(data["eindDatum"], "2024-01-14") + + def test_update_partial_categorie_relatie(self): + partij = PartijFactory.create() + categorie = CategorieFactory.create(naam="naam") + categorie_relatie = CategorieRelatieFactory.create( + partij=partij, + categorie=categorie, + begin_datum="2024-01-11", + eind_datum=None, + ) + detail_url = reverse( + "klantinteracties:categorierelatie-detail", + kwargs={"uuid": str(categorie_relatie.uuid)}, ) + response = self.client.get(detail_url) + data = response.json() + + self.assertEqual(data["partij"]["uuid"], str(partij.uuid)) + self.assertEqual(data["categorie"]["uuid"], str(categorie.uuid)) + self.assertEqual(data["categorie"]["naam"], categorie.naam) + self.assertEqual(data["beginDatum"], "2024-01-11") + self.assertEqual(data["eindDatum"], None) + + data = { + "eindDatum": "2024-01-14", + } + + response = self.client.patch(detail_url, data) + self.assertEqual(response.status_code, status.HTTP_200_OK) + data = response.json() + + self.assertEqual(data["partij"]["uuid"], str(partij.uuid)) + self.assertEqual(data["categorie"]["uuid"], str(categorie.uuid)) + self.assertEqual(data["categorie"]["naam"], "naam") + self.assertEqual(data["beginDatum"], "2024-01-11") + self.assertEqual(data["eindDatum"], "2024-01-14") + + def test_destroy_categorie_relatie(self): + categorie_relatie = CategorieRelatieFactory.create() + detail_url = reverse( + "klantinteracties:categorierelatie-detail", + kwargs={"uuid": str(categorie_relatie.uuid)}, + ) + response = self.client.delete(detail_url) + self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) + + list_url = reverse("klantinteracties:categorierelatie-list") + response = self.client.get(list_url) + data = response.json() + self.assertEqual(data["count"], 0) + + +class CategorieTests(APITestCase): + def test_list_categorie(self): + list_url = reverse("klantinteracties:categorie-list") + CategorieFactory.create_batch(2) + + response = self.client.get(list_url) + + self.assertEqual(response.status_code, status.HTTP_200_OK) + + data = response.json() + self.assertEqual(len(data["results"]), 2) + + def test_read_categorie(self): + partij_identificator = CategorieFactory.create() + detail_url = reverse( + "klantinteracties:categorie-detail", + kwargs={"uuid": str(partij_identificator.uuid)}, + ) + + response = self.client.get(detail_url) + + self.assertEqual(response.status_code, status.HTTP_200_OK) + + def test_create_categorie(self): + list_url = reverse("klantinteracties:categorie-list") + data = { + "naam": "naam", + } + + response = self.client.post(list_url, data) + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + data = response.json() + + self.assertEqual(data["naam"], "naam") + + def test_update_categorie(self): + categorie = CategorieFactory.create(naam="naam") detail_url = reverse( "klantinteracties:categorie-detail", @@ -1794,34 +1931,21 @@ def test_update_categorie(self): response = self.client.get(detail_url) data = response.json() - self.assertEqual(data["partij"]["uuid"], str(partij.uuid)) self.assertEqual(data["naam"], "naam") - self.assertEqual(data["beginDatum"], "2024-01-10") - self.assertIsNone(data["eindDatum"]) data = { - "partij": {"uuid": str(partij2.uuid)}, "naam": "changed", - "beginDatum": "2024-01-11", - "eindDatum": "2024-01-12", } response = self.client.put(detail_url, data) self.assertEqual(response.status_code, status.HTTP_200_OK) data = response.json() - self.assertEqual(data["partij"]["uuid"], str(partij2.uuid)) self.assertEqual(data["naam"], "changed") - self.assertEqual(data["beginDatum"], "2024-01-11") - self.assertEqual(data["eindDatum"], "2024-01-12") def test_partial_update_categorie(self): - partij = PartijFactory.create() categorie = CategorieFactory.create( - partij=partij, naam="naam", - begin_datum="2024-01-10", - eind_datum=None, ) detail_url = reverse( @@ -1831,23 +1955,16 @@ def test_partial_update_categorie(self): response = self.client.get(detail_url) data = response.json() - self.assertEqual(data["partij"]["uuid"], str(partij.uuid)) self.assertEqual(data["naam"], "naam") - self.assertEqual(data["beginDatum"], "2024-01-10") - self.assertIsNone(data["eindDatum"]) data = {"naam": "changed"} - response = self.client.patch(detail_url, data) self.assertEqual(response.status_code, status.HTTP_200_OK) data = response.json() - self.assertEqual(data["partij"]["uuid"], str(partij.uuid)) self.assertEqual(data["naam"], "changed") - self.assertEqual(data["beginDatum"], "2024-01-10") - self.assertIsNone(data["eindDatum"]) - def test_destroy_partij_identificator(self): + def test_destroy_categorie(self): categorie = CategorieFactory.create() detail_url = reverse( "klantinteracties:categorie-detail", diff --git a/src/openklant/components/klantinteracties/api/urls.py b/src/openklant/components/klantinteracties/api/urls.py index d6d1324e..cc91aa9e 100644 --- a/src/openklant/components/klantinteracties/api/urls.py +++ b/src/openklant/components/klantinteracties/api/urls.py @@ -21,6 +21,7 @@ OnderwerpobjectViewSet, ) from openklant.components.klantinteracties.api.viewsets.partijen import ( + CategorieRelatieViewSet, CategorieViewSet, PartijIdentificatorViewSet, PartijViewSet, @@ -43,6 +44,7 @@ router.register("internetaken", InterneTaakViewSet) router.register("categorieen", CategorieViewSet) +router.register("categorie-relaties", CategorieRelatieViewSet) router.register("partijen", PartijViewSet) router.register("partij-identificatoren", PartijIdentificatorViewSet) diff --git a/src/openklant/components/klantinteracties/api/viewsets/partijen.py b/src/openklant/components/klantinteracties/api/viewsets/partijen.py index b1cf1056..1e0c4302 100644 --- a/src/openklant/components/klantinteracties/api/viewsets/partijen.py +++ b/src/openklant/components/klantinteracties/api/viewsets/partijen.py @@ -4,12 +4,14 @@ from openklant.components.klantinteracties.api.filterset.partijen import PartijFilterSet from openklant.components.klantinteracties.api.serializers.partijen import ( + CategorieRelatieSerializer, CategorieSerializer, PartijIdentificatorSerializer, PartijSerializer, ) from openklant.components.klantinteracties.models.partijen import ( Categorie, + CategorieRelatie, Partij, PartijIdentificator, ) @@ -68,6 +70,47 @@ class PartijViewSet(ExpandMixin, viewsets.ModelViewSet): permission_classes = (TokenPermissions,) +@extend_schema(tags=["categorie relaties"]) +@extend_schema_view( + list=extend_schema( + summary="Alle categorie relaties opvragen.", + description="Alle categorie relaties opvragen, Let op: Dit endpoint is EXPERIMENTEEL.", + ), + retrieve=extend_schema( + summary="Een specifiek categorie relatie opvragen..", + description="Een specifiek categorie relatie opvragen, Let op: Dit endpoint is EXPERIMENTEEL.", + ), + create=extend_schema( + summary="Maak een categorie relatie aan.", + description="Maak een categorie relatie aan, Let op: Dit endpoint is EXPERIMENTEEL.", + ), + update=extend_schema( + summary="Werk een categorie relatie in zijn geheel bij.", + description="Werk een categorie relatie deels bij, Let op: Dit endpoint is EXPERIMENTEEL.", + ), + partial_update=extend_schema( + summary="Werk een categorie relatie deels bij.", + description="Werk een categorie relatie deels bij, Let op: Dit endpoint is EXPERIMENTEEL.", + ), + destroy=extend_schema( + summary="Verwijder een categorie relatie.", + description="Verwijder een categorie relatie, Let op: Dit endpoint is EXPERIMENTEEL.", + ), +) +class CategorieRelatieViewSet(viewsets.ModelViewSet): + """De categorie relatie van een partij, Let op: Dit endpoint is EXPERIMENTEEL.""" + + queryset = CategorieRelatie.objects.order_by("-pk").select_related( + "partij", + "categorie", + ) + serializer_class = CategorieRelatieSerializer + lookup_field = "uuid" + pagination_class = PageNumberPagination + authentication_classes = (TokenAuthentication,) + permission_classes = (TokenPermissions,) + + @extend_schema(tags=["categorieën"]) @extend_schema_view( list=extend_schema( @@ -98,7 +141,7 @@ class PartijViewSet(ExpandMixin, viewsets.ModelViewSet): class CategorieViewSet(viewsets.ModelViewSet): """De categorie van een partij, Let op: Dit endpoint is EXPERIMENTEEL.""" - queryset = Categorie.objects.order_by("-pk").select_related("partij") + queryset = Categorie.objects.order_by("-pk") serializer_class = CategorieSerializer lookup_field = "uuid" pagination_class = PageNumberPagination diff --git a/src/openklant/components/klantinteracties/migrations/0008_categorie.py b/src/openklant/components/klantinteracties/migrations/0008_categorie_categorierelatie.py similarity index 64% rename from src/openklant/components/klantinteracties/migrations/0008_categorie.py rename to src/openklant/components/klantinteracties/migrations/0008_categorie_categorierelatie.py index 65cd6fbc..5b733e83 100644 --- a/src/openklant/components/klantinteracties/migrations/0008_categorie.py +++ b/src/openklant/components/klantinteracties/migrations/0008_categorie_categorierelatie.py @@ -1,4 +1,4 @@ -# Generated by Django 3.2.23 on 2024-01-12 08:02 +# Generated by Django 3.2.23 on 2024-01-17 15:11 from django.db import migrations, models import django.db.models.deletion @@ -11,15 +11,6 @@ class Migration(migrations.Migration): ] operations = [ - migrations.AlterField( - model_name="internetaak", - name="toegewezen_op", - field=models.DateTimeField( - auto_now_add=True, - help_text="Datum en tijdstip waarop de interne taak aan een actor werd toegewezen.", - verbose_name="toegewezen op", - ), - ), migrations.CreateModel( name="Categorie", fields=[ @@ -49,6 +40,32 @@ class Migration(migrations.Migration): verbose_name="naam", ), ), + ], + options={ + "verbose_name": "categorie", + "verbose_name_plural": "categorieën", + }, + ), + migrations.CreateModel( + name="CategorieRelatie", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "uuid", + models.UUIDField( + default=uuid.uuid4, + help_text="Unieke (technische) identificatiecode van de Categorie Relatie.", + unique=True, + ), + ), ( "begin_datum", models.DateField( @@ -67,11 +84,22 @@ class Migration(migrations.Migration): verbose_name="eind datum", ), ), + ( + "categorie", + models.ForeignKey( + blank=True, + help_text="De 'categorie' van deze 'categorie relatie'.", + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="klantinteracties.categorie", + verbose_name="categorie", + ), + ), ( "partij", models.ForeignKey( blank=True, - help_text="De 'Categorie' van een 'Partij'.", + help_text="De 'categorie relatie' van een 'Partij'.", null=True, on_delete=django.db.models.deletion.CASCADE, to="klantinteracties.partij", @@ -80,8 +108,8 @@ class Migration(migrations.Migration): ), ], options={ - "verbose_name": "categorie", - "verbose_name_plural": "categorieën", + "verbose_name": "categorie relatie", + "verbose_name_plural": "categorieën relatie", }, ), ] diff --git a/src/openklant/components/klantinteracties/models/partijen.py b/src/openklant/components/klantinteracties/models/partijen.py index 3f06822b..3856a0ad 100644 --- a/src/openklant/components/klantinteracties/models/partijen.py +++ b/src/openklant/components/klantinteracties/models/partijen.py @@ -106,24 +106,26 @@ def save(self, *args, **kwargs): return super().save(*args, **kwargs) -class Categorie(models.Model): +class CategorieRelatie(models.Model): uuid = models.UUIDField( unique=True, default=uuid.uuid4, - help_text=_("Unieke (technische) identificatiecode van de Categorie."), + help_text=_("Unieke (technische) identificatiecode van de Categorie Relatie."), ) partij = models.ForeignKey( "klantinteracties.Partij", on_delete=models.CASCADE, verbose_name=_("partij"), - help_text=_("De 'Categorie' van een 'Partij'."), + help_text=_("De 'categorie relatie' van een 'Partij'."), null=True, blank=True, ) - naam = models.CharField( - _("naam"), - help_text=_("Naam van de categorie."), - max_length=80, + categorie = models.ForeignKey( + "klantinteracties.Categorie", + on_delete=models.CASCADE, + verbose_name=_("categorie"), + help_text=_("De 'categorie' van deze 'categorie relatie'."), + null=True, blank=True, ) begin_datum = models.DateField( @@ -149,6 +151,32 @@ class Categorie(models.Model): ), ) + class Meta: + verbose_name = _("categorie relatie") + verbose_name_plural = _("categorieën relatie") + + def __str__(self): + if self.categorie and self.partij: + return f"{self.categorie} - ({self.partij.nummer})" + elif self.categorie: + return self.categorie.naam + + return str(self.uuid) + + +class Categorie(models.Model): + uuid = models.UUIDField( + unique=True, + default=uuid.uuid4, + help_text=_("Unieke (technische) identificatiecode van de Categorie."), + ) + naam = models.CharField( + _("naam"), + help_text=_("Naam van de categorie."), + max_length=80, + blank=True, + ) + class Meta: verbose_name = _("categorie") verbose_name_plural = _("categorieën") diff --git a/src/openklant/components/klantinteracties/models/tests/factories/partijen.py b/src/openklant/components/klantinteracties/models/tests/factories/partijen.py index 94feeeeb..d28567e8 100644 --- a/src/openklant/components/klantinteracties/models/tests/factories/partijen.py +++ b/src/openklant/components/klantinteracties/models/tests/factories/partijen.py @@ -5,6 +5,7 @@ from openklant.components.klantinteracties.models.partijen import ( Categorie, + CategorieRelatie, Contactpersoon, Organisatie, Partij, @@ -38,8 +39,16 @@ def vertegenwoordigde(self, create, extracted, **kwargs): self.vertegenwoordigde.add(*extracted) +class CategorieRelatieFactory(factory.django.DjangoModelFactory): + uuid = factory.Faker("uuid4") + + class Meta: + model = CategorieRelatie + + class CategorieFactory(factory.django.DjangoModelFactory): uuid = factory.Faker("uuid4") + naam = factory.Faker("word") class Meta: model = Categorie diff --git a/src/openklant/components/klantinteracties/openapi.yaml b/src/openklant/components/klantinteracties/openapi.yaml index 1c473f18..aaedb919 100644 --- a/src/openklant/components/klantinteracties/openapi.yaml +++ b/src/openklant/components/klantinteracties/openapi.yaml @@ -540,6 +540,153 @@ paths: responses: '204': description: No response body + /categorie-relaties: + get: + operationId: categorieRelatiesList + description: 'Alle categorie relaties opvragen, Let op: Dit endpoint is EXPERIMENTEEL.' + summary: Alle categorie relaties opvragen. + parameters: + - name: page + required: false + in: query + description: Een pagina binnen de gepagineerde set resultaten. + schema: + type: integer + tags: + - categorie relaties + security: + - tokenAuth: [] + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/PaginatedCategorieRelatieList' + description: '' + post: + operationId: categorieRelatiesCreate + description: 'Maak een categorie relatie aan, Let op: Dit endpoint is EXPERIMENTEEL.' + summary: Maak een categorie relatie aan. + tags: + - categorie relaties + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CategorieRelatie' + required: true + security: + - tokenAuth: [] + responses: + '201': + content: + application/json: + schema: + $ref: '#/components/schemas/CategorieRelatie' + description: '' + /categorie-relaties/{uuid}: + get: + operationId: categorieRelatiesRetrieve + description: 'Een specifiek categorie relatie opvragen, Let op: Dit endpoint + is EXPERIMENTEEL.' + summary: Een specifiek categorie relatie opvragen.. + parameters: + - in: path + name: uuid + schema: + type: string + format: uuid + description: Unieke (technische) identificatiecode van de Categorie Relatie. + required: true + tags: + - categorie relaties + security: + - tokenAuth: [] + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/CategorieRelatie' + description: '' + put: + operationId: categorieRelatiesUpdate + description: 'Werk een categorie relatie deels bij, Let op: Dit endpoint is + EXPERIMENTEEL.' + summary: Werk een categorie relatie in zijn geheel bij. + parameters: + - in: path + name: uuid + schema: + type: string + format: uuid + description: Unieke (technische) identificatiecode van de Categorie Relatie. + required: true + tags: + - categorie relaties + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CategorieRelatie' + required: true + security: + - tokenAuth: [] + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/CategorieRelatie' + description: '' + patch: + operationId: categorieRelatiesPartialUpdate + description: 'Werk een categorie relatie deels bij, Let op: Dit endpoint is + EXPERIMENTEEL.' + summary: Werk een categorie relatie deels bij. + parameters: + - in: path + name: uuid + schema: + type: string + format: uuid + description: Unieke (technische) identificatiecode van de Categorie Relatie. + required: true + tags: + - categorie relaties + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/PatchedCategorieRelatie' + security: + - tokenAuth: [] + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/CategorieRelatie' + description: '' + delete: + operationId: categorieRelatiesDestroy + description: 'Verwijder een categorie relatie, Let op: Dit endpoint is EXPERIMENTEEL.' + summary: Verwijder een categorie relatie. + parameters: + - in: path + name: uuid + schema: + type: string + format: uuid + description: Unieke (technische) identificatiecode van de Categorie Relatie. + required: true + tags: + - categorie relaties + security: + - tokenAuth: [] + responses: + '204': + description: No response body /categorieen: get: operationId: categorieenList @@ -574,7 +721,6 @@ paths: application/json: schema: $ref: '#/components/schemas/Categorie' - required: true security: - tokenAuth: [] responses: @@ -627,7 +773,6 @@ paths: application/json: schema: $ref: '#/components/schemas/Categorie' - required: true security: - tokenAuth: [] responses: @@ -1559,14 +1704,14 @@ paths: - betrokkenen - betrokkenen.had_klantcontact - betrokkenen.had_klantcontact.had_betrokken_actoren - - categorieen + - categorie_relaties - digitale_adressen description: |- Sluit de gespecifieerde gerelateerde resources in in het antwoord. * `digitale_adressen` - digitale_adressen * `betrokkenen` - betrokkenen - * `categorieen` - categorieen + * `categorie_relaties` - categorie_relaties * `betrokkenen.had_klantcontact` - betrokkenen.had_klantcontact * `betrokkenen.had_klantcontact.had_betrokken_actoren` - betrokkenen.had_klantcontact.had_betrokken_actoren explode: false @@ -2129,15 +2274,58 @@ components: format: uri readOnly: true description: De unieke URL van deze categorie binnen deze API. - partij: - allOf: - - $ref: '#/components/schemas/PartijForeignkeyBase' - nullable: true - description: De partij waar de categorie aan gelinkt is. naam: type: string description: Naam van de categorie. maxLength: 80 + required: + - url + - uuid + CategorieForeignKey: + type: object + description: 'Let op: Dit attribuut is EXPERIMENTEEL.' + properties: + uuid: + type: string + format: uuid + description: Unieke (technische) identificatiecode van de Categorie. + url: + type: string + format: uri + readOnly: true + description: De unieke URL van deze categorie binnen deze API. + naam: + type: string + readOnly: true + description: Naam van de categorie. + required: + - naam + - url + - uuid + CategorieRelatie: + type: object + description: 'Let op: Dit endpoint is EXPERIMENTEEL.' + properties: + uuid: + type: string + format: uuid + readOnly: true + description: Unieke (technische) identificatiecode van de Categorie Relatie. + url: + type: string + format: uri + readOnly: true + description: De unieke URL van deze categorie binnen deze API. + partij: + allOf: + - $ref: '#/components/schemas/PartijForeignkeyBase' + nullable: true + description: De partij waar de categorie relatie aan gelinkt is. + categorie: + allOf: + - $ref: '#/components/schemas/CategorieForeignKey' + nullable: true + description: De categorie waar de categorie relatie aan gelinkt is. beginDatum: type: string format: date @@ -2154,10 +2342,11 @@ components: Een voorbeeld: 2022-02-21' required: - beginDatum + - categorie - partij - url - uuid - CategorieForeignKey: + CategorieRelatieForeignKey: type: object description: 'Let op: Dit attribuut is EXPERIMENTEEL.' properties: @@ -2170,7 +2359,12 @@ components: format: uri readOnly: true description: De unieke URL van deze categorie binnen deze API. + categorieNaam: + type: string + readOnly: true + description: De naam van de gelinkte categorie. required: + - categorieNaam - url - uuid Contactnaam: @@ -2658,6 +2852,26 @@ components: type: array items: $ref: '#/components/schemas/Categorie' + PaginatedCategorieRelatieList: + type: object + properties: + count: + type: integer + example: 123 + next: + type: string + nullable: true + format: uri + example: http://api.example.org/accounts/?page=4 + previous: + type: string + nullable: true + format: uri + example: http://api.example.org/accounts/?page=2 + results: + type: array + items: + $ref: '#/components/schemas/CategorieRelatie' PaginatedDigitaalAdresList: type: object properties: @@ -2811,12 +3025,13 @@ components: $ref: '#/components/schemas/BetrokkeneForeignKey' readOnly: true description: Betrokkene bij klantcontact die een partij was. - categorieen: + categorieRelaties: type: array items: - $ref: '#/components/schemas/CategorieForeignKey' + $ref: '#/components/schemas/CategorieRelatieForeignKey' readOnly: true - description: 'De Categorieën van een partij: Let op: Dit attribuut is EXPERIMENTEEL.' + description: 'De Categorie relaties van een partij: Let op: Dit attribuut + is EXPERIMENTEEL.' digitaleAdressen: type: array items: @@ -2881,7 +3096,7 @@ components: bekende adressen. required: - betrokkenen - - categorieen + - categorieRelaties - digitaleAdressen - indicatieActief - indicatieGeheimhouding @@ -3247,15 +3462,34 @@ components: format: uri readOnly: true description: De unieke URL van deze categorie binnen deze API. - partij: - allOf: - - $ref: '#/components/schemas/PartijForeignkeyBase' - nullable: true - description: De partij waar de categorie aan gelinkt is. naam: type: string description: Naam van de categorie. maxLength: 80 + PatchedCategorieRelatie: + type: object + description: 'Let op: Dit endpoint is EXPERIMENTEEL.' + properties: + uuid: + type: string + format: uuid + readOnly: true + description: Unieke (technische) identificatiecode van de Categorie Relatie. + url: + type: string + format: uri + readOnly: true + description: De unieke URL van deze categorie binnen deze API. + partij: + allOf: + - $ref: '#/components/schemas/PartijForeignkeyBase' + nullable: true + description: De partij waar de categorie relatie aan gelinkt is. + categorie: + allOf: + - $ref: '#/components/schemas/CategorieForeignKey' + nullable: true + description: De categorie waar de categorie relatie aan gelinkt is. beginDatum: type: string format: date @@ -3508,12 +3742,13 @@ components: $ref: '#/components/schemas/BetrokkeneForeignKey' readOnly: true description: Betrokkene bij klantcontact die een partij was. - categorieen: + categorieRelaties: type: array items: - $ref: '#/components/schemas/CategorieForeignKey' + $ref: '#/components/schemas/CategorieRelatieForeignKey' readOnly: true - description: 'De Categorieën van een partij: Let op: Dit attribuut is EXPERIMENTEEL.' + description: 'De Categorie relaties van een partij: Let op: Dit attribuut + is EXPERIMENTEEL.' digitaleAdressen: type: array items: