diff --git a/src/openklant/components/klantinteracties/__init__.py b/src/openklant/components/klantinteracties/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/openklant/components/klantinteracties/apps.py b/src/openklant/components/klantinteracties/apps.py new file mode 100644 index 00000000..6de18f55 --- /dev/null +++ b/src/openklant/components/klantinteracties/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class KlantinteractiesConfig(AppConfig): + name = "openklant.components.klantinteracties" diff --git a/src/openklant/components/klantinteracties/migrations/0001_initial.py b/src/openklant/components/klantinteracties/migrations/0001_initial.py new file mode 100644 index 00000000..2d333eb1 --- /dev/null +++ b/src/openklant/components/klantinteracties/migrations/0001_initial.py @@ -0,0 +1,371 @@ +# Generated by Django 3.2.18 on 2023-10-10 15:54 + +import django.core.validators +from django.db import migrations, models +import django.db.models.deletion +import uuid + + +class Migration(migrations.Migration): + initial = True + + dependencies = [] + + operations = [ + migrations.CreateModel( + name="Partij", + fields=[ + ( + "id", + models.UUIDField( + default=uuid.uuid4, + help_text="Unieke (technische) identificatiecode van de partij.", + primary_key=True, + serialize=False, + unique=True, + ), + ), + ( + "nummer", + models.CharField( + help_text="Uniek identificerend nummer dat tijdens communicatie tussen mensen kan worden gebruikt om de specifieke partij aan te duiden.", + max_length=10, + validators=[django.core.validators.validate_integer], + verbose_name="Nummer", + ), + ), + ( + "interne_notitie", + models.CharField( + blank=True, + help_text="Mededelingen, aantekeningen of bijzonderheden over de partij, bedoeld voor intern gebruik.", + max_length=1000, + verbose_name="Interne notitie", + ), + ), + ( + "soort_partij", + models.CharField( + choices=[ + ("persoon", "Persoon"), + ("organisatie", "Organisatie"), + ("contactpersoon", "Contactpersoon"), + ], + help_text="Geeft aan van welke specifieke soort partij sprake is.", + max_length=14, + verbose_name="Soort partij", + ), + ), + ( + "indicatie_geheimhouding", + models.BooleanField( + help_text="Geeft aan of de verstrekker van partijgegevens heeft aangegeven dat deze gegevens als geheim beschouwd moeten worden.", + verbose_name="Indicatie geheimhouding", + ), + ), + ( + "voorkeurskanaal", + models.CharField( + blank=True, + help_text="Kanaal dat de partij bij voorkeur gebruikt voor contact met de gemeente.", + max_length=50, + verbose_name="Voorkeurskanaal", + ), + ), + ( + "voorkeurstaal", + models.CharField( + blank=True, + help_text="Taal waarin de partij bij voorkeur contact heeft met de gemeente.", + max_length=255, + verbose_name="Voorkeurstaal", + ), + ), + ( + "indicatie_actief", + models.BooleanField( + help_text="Geeft aan of de contactgegevens van de partij nog gebruikt morgen worden om contact op te nemen. Gegevens van niet-actieve partijen mogen hiervoor niet worden gebruikt.", + verbose_name="Indicatie actief", + ), + ), + ( + "correspondentieadres_nummeraanduiding_id", + models.UUIDField( + blank=True, + help_text="Identificatie van het adres bij de Basisregistratie Adressen en Gebouwen.", + null=True, + unique=True, + verbose_name="Nummeraanduiding ID", + ), + ), + ( + "correspondentieadres_adresregel1", + models.CharField( + blank=True, + help_text="Eerste deel van het adres dat niet voorkomt in de Basisregistratie Adressen en Gebouwen.", + max_length=80, + verbose_name="Adresregel 1", + ), + ), + ( + "correspondentieadres_adresregel2", + models.CharField( + blank=True, + help_text="Tweede deel van het adres dat niet voorkomt in de Basisregistratie Adressen en Gebouwen.", + max_length=80, + verbose_name="Adresregel 2", + ), + ), + ( + "correspondentieadres_adresregel3", + models.CharField( + blank=True, + help_text="Derde deel van het adres dat niet voorkomt in de Basisregistratie Adressen en Gebouwen.", + max_length=80, + verbose_name="Adresregel 3", + ), + ), + ( + "correspondentieadres_land", + models.CharField( + blank=True, + help_text="Een code, opgenomen in Tabel 34, Landentabel, die het land (buiten Nederland) aangeeft alwaar de ingeschrevene verblijft.", + max_length=4, + validators=[ + django.core.validators.MinLengthValidator(limit_value=4), + django.core.validators.validate_integer, + ], + verbose_name="Land", + ), + ), + ( + "bezoekadres_nummeraanduiding_id", + models.UUIDField( + blank=True, + help_text="Identificatie van het adres bij de Basisregistratie Adressen en Gebouwen.", + null=True, + unique=True, + verbose_name="Nummeraanduiding ID", + ), + ), + ( + "bezoekadres_adresregel1", + models.CharField( + blank=True, + help_text="Eerste deel van het adres dat niet voorkomt in de Basisregistratie Adressen en Gebouwen.", + max_length=80, + verbose_name="Adresregel 1", + ), + ), + ( + "bezoekadres_adresregel2", + models.CharField( + blank=True, + help_text="Tweede deel van het adres dat niet voorkomt in de Basisregistratie Adressen en Gebouwen.", + max_length=80, + verbose_name="Adresregel 2", + ), + ), + ( + "bezoekadres_adresregel3", + models.CharField( + blank=True, + help_text="Derde deel van het adres dat niet voorkomt in de Basisregistratie Adressen en Gebouwen.", + max_length=80, + verbose_name="Adresregel 3", + ), + ), + ( + "bezoekadres_land", + models.CharField( + blank=True, + help_text="Een code, opgenomen in Tabel 34, Landentabel, die het land (buiten Nederland) aangeeft alwaar de ingeschrevene verblijft.", + max_length=4, + validators=[ + django.core.validators.MinLengthValidator(limit_value=4), + django.core.validators.validate_integer, + ], + verbose_name="Land", + ), + ), + ], + options={ + "verbose_name": "partij", + "verbose_name_plural": "partijen", + }, + ), + migrations.CreateModel( + name="Persoon", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "contactnaam_voorletters", + models.CharField( + help_text="Een afkorting van de voornamen. Meestal de beginletter, maar in sommige gevallen de beginletter gecombineerd met de tweede letter van een voornaam.", + max_length=10, + verbose_name="Voorletters", + ), + ), + ( + "contactnaam_voornaam", + models.CharField( + blank=True, + help_text="De voornaam die de persoon wil gebruiken tijdens communicatie met de gemeente.", + max_length=200, + verbose_name="Voornaam", + ), + ), + ( + "contactnaam_voorvoegsel_achternaam", + models.CharField( + blank=True, + help_text="Een eventueel voorvoegsel dat hoort bij de achternaam die de persoon wil gebruiken tijdens communicatie met de gemeente.", + max_length=10, + verbose_name="Voorvoegsel achternaam", + ), + ), + ( + "contactnaam_achternaam", + models.CharField( + blank=True, + help_text="Een achternaam die de persoon wil gebruiken tijdens communicatie met de gemeente.", + max_length=200, + verbose_name="Achternaam", + ), + ), + ( + "partij", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="persoon", + to="klantinteracties.partij", + verbose_name="Partij", + ), + ), + ], + options={ + "verbose_name": "persoon", + "verbose_name_plural": "personen", + }, + ), + migrations.CreateModel( + name="Organisatie", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "naam", + models.CharField( + blank=True, + help_text="Naam van de organisatie.", + max_length=200, + verbose_name="Naam", + ), + ), + ( + "partij", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="organisatie", + to="klantinteracties.partij", + verbose_name="Partij", + ), + ), + ], + options={ + "verbose_name": "organisatie", + "verbose_name_plural": "organisaties", + }, + ), + migrations.CreateModel( + name="Contactpersoon", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "contactnaam_voorletters", + models.CharField( + help_text="Een afkorting van de voornamen. Meestal de beginletter, maar in sommige gevallen de beginletter gecombineerd met de tweede letter van een voornaam.", + max_length=10, + verbose_name="Voorletters", + ), + ), + ( + "contactnaam_voornaam", + models.CharField( + blank=True, + help_text="De voornaam die de persoon wil gebruiken tijdens communicatie met de gemeente.", + max_length=200, + verbose_name="Voornaam", + ), + ), + ( + "contactnaam_voorvoegsel_achternaam", + models.CharField( + blank=True, + help_text="Een eventueel voorvoegsel dat hoort bij de achternaam die de persoon wil gebruiken tijdens communicatie met de gemeente.", + max_length=10, + verbose_name="Voorvoegsel achternaam", + ), + ), + ( + "contactnaam_achternaam", + models.CharField( + blank=True, + help_text="Een achternaam die de persoon wil gebruiken tijdens communicatie met de gemeente.", + max_length=200, + verbose_name="Achternaam", + ), + ), + ( + "organisatie", + models.ForeignKey( + help_text="De organisatie waar een contactpersoon voor werkt.", + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="contact_personen", + to="klantinteracties.organisatie", + verbose_name="Organistatie", + ), + ), + ( + "partij", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="contact_persoon", + to="klantinteracties.partij", + verbose_name="Partij", + ), + ), + ], + options={ + "verbose_name": "contact persoon", + "verbose_name_plural": "contact personen", + }, + ), + ] diff --git a/src/openklant/components/klantinteracties/migrations/0002_klantcontact.py b/src/openklant/components/klantinteracties/migrations/0002_klantcontact.py new file mode 100644 index 00000000..2d61aa75 --- /dev/null +++ b/src/openklant/components/klantinteracties/migrations/0002_klantcontact.py @@ -0,0 +1,110 @@ +# Generated by Django 3.2.18 on 2023-10-10 17:54 + +import django.core.validators +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + dependencies = [ + ("klantinteracties", "0001_initial"), + ] + + operations = [ + migrations.CreateModel( + name="Klantcontact", + fields=[ + ( + "id", + models.UUIDField( + default=uuid.uuid4, + help_text="Unieke (technische) identificatiecode van de betrokkene bij klantcontact.", + primary_key=True, + serialize=False, + unique=True, + ), + ), + ( + "nummer", + models.CharField( + help_text="Uniek identificerend nummer dat tijdens communicatie tussen mensen kan worden gebruikt om het specifieke klantcontact aan te duiden.", + max_length=10, + validators=[django.core.validators.validate_integer], + verbose_name="Nummer", + ), + ), + ( + "kanaal", + models.CharField( + help_text="Communicatiekanaal dat bij het klantcontact werd gebruikt.", + max_length=50, + verbose_name="Kanaal", + ), + ), + ( + "onderwerp", + models.CharField( + help_text="Datgene waarover het klantcontact ging.", + max_length=200, + verbose_name="Onderwerp", + ), + ), + ( + "inhoud", + models.CharField( + blank=True, + help_text="Informatie die tijdens het klantcontact werd overgebracht of uitgewisseld, voor zover die voor betrokkenen of actoren relevant is.", + max_length=1000, + verbose_name="Inhoud", + ), + ), + ( + "initiator", + models.CharField( + choices=[ + ("gemeente", "Gemeente"), + ("klant", "Klant"), + ("vertegenwoordiger", "Vertegenwoordiger"), + ], + help_text="Degene die het klantcontact initieerde.", + max_length=17, + verbose_name="Initiator", + ), + ), + ( + "indicatie_contact_gelukt", + models.BooleanField( + help_text="Geeft, indien bekend, aan of de poging contact tussen de gemeente en inwoner(s) of organisatie(s) tot stand te brengen succesvol was.", + null=True, + verbose_name="Indicatie contact gelukt", + ), + ), + ( + "taal", + models.CharField( + help_text="Taal die bij het klantcontact werd gesproken of geschreven.", + max_length=255, + verbose_name="Taal", + ), + ), + ( + "vertrouwelijk", + models.BooleanField( + help_text="Geeft aan of onderwerp, inhoud en kenmerken van het klantcontact vertrouwelijk moeten worden behandeld.", + verbose_name="Vertrouwelijk", + ), + ), + ( + "plaatsgevonden_op", + models.DateTimeField( + help_text="Datum en tijdstip waarop het klantontact plaatsvond. Als het klantcontact een gesprek betrof, is dit het moment waarop het gesprek begon. Als het klantcontact verzending of ontvangst van informatie betrof, is dit bij benadering het moment waarop informatie door gemeente verzonden of ontvangen werd.", + verbose_name="Plaatsgevonden op", + ), + ), + ], + options={ + "verbose_name": "klantcontact", + "verbose_name_plural": "klantcontacten", + }, + ), + ] diff --git a/src/openklant/components/klantinteracties/migrations/0003_betrokkene.py b/src/openklant/components/klantinteracties/migrations/0003_betrokkene.py new file mode 100644 index 00000000..876e9253 --- /dev/null +++ b/src/openklant/components/klantinteracties/migrations/0003_betrokkene.py @@ -0,0 +1,211 @@ +# Generated by Django 3.2.18 on 2023-10-11 10:26 + +import django.core.validators +from django.db import migrations, models +import django.db.models.deletion +import uuid + + +class Migration(migrations.Migration): + dependencies = [ + ("klantinteracties", "0002_klantcontact"), + ] + + operations = [ + migrations.CreateModel( + name="Betrokkene", + fields=[ + ( + "id", + models.UUIDField( + default=uuid.uuid4, + help_text="Unieke (technische) identificatiecode van de betrokkene bij klantcontact.", + primary_key=True, + serialize=False, + unique=True, + ), + ), + ( + "rol", + models.CharField( + choices=[ + ("vertegenwoordiger", "Vertegenwoordiger"), + ("klant", "Klant"), + ], + help_text="Rol die de betrokkene bij klantcontact tijdens dat contact vervulde.", + max_length=17, + verbose_name="Rol", + ), + ), + ( + "organisatienaam", + models.CharField( + blank=True, + help_text="Naam van de organisatie waarmee de betrokkene bij klantcontact een relatie had.", + max_length=200, + verbose_name="Organisatienaam", + ), + ), + ( + "contactnaam_voorletters", + models.CharField( + help_text="Een afkorting van de voornamen. Meestal de beginletter, maar in sommige gevallen de beginletter gecombineerd met de tweede letter van een voornaam.", + max_length=10, + verbose_name="Voorletters", + ), + ), + ( + "contactnaam_voornaam", + models.CharField( + blank=True, + help_text="De voornaam die de persoon wil gebruiken tijdens communicatie met de gemeente.", + max_length=200, + verbose_name="Voornaam", + ), + ), + ( + "contactnaam_voorvoegsel_achternaam", + models.CharField( + blank=True, + help_text="Een eventueel voorvoegsel dat hoort bij de achternaam die de persoon wil gebruiken tijdens communicatie met de gemeente.", + max_length=10, + verbose_name="Voorvoegsel achternaam", + ), + ), + ( + "contactnaam_achternaam", + models.CharField( + blank=True, + help_text="Een achternaam die de persoon wil gebruiken tijdens communicatie met de gemeente.", + max_length=200, + verbose_name="Achternaam", + ), + ), + ( + "correspondentieadres_nummeraanduiding_id", + models.UUIDField( + blank=True, + help_text="Identificatie van het adres bij de Basisregistratie Adressen en Gebouwen.", + null=True, + unique=True, + verbose_name="Nummeraanduiding ID", + ), + ), + ( + "correspondentieadres_adresregel1", + models.CharField( + blank=True, + help_text="Eerste deel van het adres dat niet voorkomt in de Basisregistratie Adressen en Gebouwen.", + max_length=80, + verbose_name="Adresregel 1", + ), + ), + ( + "correspondentieadres_adresregel2", + models.CharField( + blank=True, + help_text="Tweede deel van het adres dat niet voorkomt in de Basisregistratie Adressen en Gebouwen.", + max_length=80, + verbose_name="Adresregel 2", + ), + ), + ( + "correspondentieadres_adresregel3", + models.CharField( + blank=True, + help_text="Derde deel van het adres dat niet voorkomt in de Basisregistratie Adressen en Gebouwen.", + max_length=80, + verbose_name="Adresregel 3", + ), + ), + ( + "correspondentieadres_land", + models.CharField( + blank=True, + help_text="Een code, opgenomen in Tabel 34, Landentabel, die het land (buiten Nederland) aangeeft alwaar de ingeschrevene verblijft.", + max_length=4, + validators=[ + django.core.validators.MinLengthValidator(limit_value=4), + django.core.validators.validate_integer, + ], + verbose_name="Land", + ), + ), + ( + "bezoekadres_nummeraanduiding_id", + models.UUIDField( + blank=True, + help_text="Identificatie van het adres bij de Basisregistratie Adressen en Gebouwen.", + null=True, + unique=True, + verbose_name="Nummeraanduiding ID", + ), + ), + ( + "bezoekadres_adresregel1", + models.CharField( + blank=True, + help_text="Eerste deel van het adres dat niet voorkomt in de Basisregistratie Adressen en Gebouwen.", + max_length=80, + verbose_name="Adresregel 1", + ), + ), + ( + "bezoekadres_adresregel2", + models.CharField( + blank=True, + help_text="Tweede deel van het adres dat niet voorkomt in de Basisregistratie Adressen en Gebouwen.", + max_length=80, + verbose_name="Adresregel 2", + ), + ), + ( + "bezoekadres_adresregel3", + models.CharField( + blank=True, + help_text="Derde deel van het adres dat niet voorkomt in de Basisregistratie Adressen en Gebouwen.", + max_length=80, + verbose_name="Adresregel 3", + ), + ), + ( + "bezoekadres_land", + models.CharField( + blank=True, + help_text="Een code, opgenomen in Tabel 34, Landentabel, die het land (buiten Nederland) aangeeft alwaar de ingeschrevene verblijft.", + max_length=4, + validators=[ + django.core.validators.MinLengthValidator(limit_value=4), + django.core.validators.validate_integer, + ], + verbose_name="Land", + ), + ), + ( + "klantcontact", + models.ForeignKey( + help_text="'Klantcontact' had 'Betrokkene bij klantcontact'", + on_delete=django.db.models.deletion.CASCADE, + related_name="betrokkene", + to="klantinteracties.klantcontact", + verbose_name="Klantcontact", + ), + ), + ], + options={ + "verbose_name": "betrokkene bij klantcontact", + }, + ), + migrations.AddField( + model_name="partij", + name="betrokkene", + field=models.ForeignKey( + help_text="'Betrokkene bij klantcontact' was 'Partij'", + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="partijen", + to="klantinteracties.betrokkene", + verbose_name="Betrokkene", + ), + ), + ] diff --git a/src/openklant/components/klantinteracties/migrations/0004_digitaaladres.py b/src/openklant/components/klantinteracties/migrations/0004_digitaaladres.py new file mode 100644 index 00000000..886e5bce --- /dev/null +++ b/src/openklant/components/klantinteracties/migrations/0004_digitaaladres.py @@ -0,0 +1,80 @@ +# Generated by Django 3.2.18 on 2023-10-11 15:47 + +from django.db import migrations, models +import django.db.models.deletion +import uuid + + +class Migration(migrations.Migration): + dependencies = [ + ("klantinteracties", "0003_betrokkene"), + ] + + operations = [ + migrations.CreateModel( + name="DigitaalAdres", + fields=[ + ( + "id", + models.UUIDField( + default=uuid.uuid4, + help_text="Unieke (technische) identificatiecode van het digitaal adres.", + primary_key=True, + serialize=False, + unique=True, + ), + ), + ( + "soort_digitaal_adres", + models.CharField( + help_text="Typering van het digitale adres die aangeeft via welk(e) kanaal of kanalen met dit adres contact kan worden opgenomen.", + max_length=254, + verbose_name="Soort digitaal adres", + ), + ), + ( + "adres", + models.CharField( + help_text="Digitaal adres waarmee een persoon of organisatie bereikt kan worden.", + max_length=80, + verbose_name="Adres", + ), + ), + ( + "omschrijving", + models.CharField( + help_text="Omschrijving van het digitaal adres.", + max_length=40, + verbose_name="Omschrijving", + ), + ), + ], + options={ + "verbose_name": "digitaal adres", + }, + ), + migrations.AddField( + model_name="betrokkene", + name="digitaal_adres", + field=models.ForeignKey( + help_text="'Digitaal Adres' had 'Betrokkene bij klantcontact'", + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="partijen", + to="klantinteracties.digitaaladres", + verbose_name="Digitaal adres", + ), + ), + migrations.AddField( + model_name="partij", + name="digitaal_adres", + field=models.ForeignKey( + help_text="'Digitaal Adres' was 'Partij'", + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="digitale_adressen", + to="klantinteracties.digitaaladres", + verbose_name="Digitaal adres", + ), + ), + ] diff --git a/src/openklant/components/klantinteracties/migrations/__init__.py b/src/openklant/components/klantinteracties/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/openklant/components/klantinteracties/models/__init__.py b/src/openklant/components/klantinteracties/models/__init__.py new file mode 100644 index 00000000..7167c60a --- /dev/null +++ b/src/openklant/components/klantinteracties/models/__init__.py @@ -0,0 +1,3 @@ +from .digitaal_adres import * # noqa +from .klantcontacten import * # noqa +from .partijen import * # noqa diff --git a/src/openklant/components/klantinteracties/models/constants.py b/src/openklant/components/klantinteracties/models/constants.py new file mode 100644 index 00000000..88a2ba0b --- /dev/null +++ b/src/openklant/components/klantinteracties/models/constants.py @@ -0,0 +1,60 @@ +from django.utils.translation import gettext_lazy as _ + +from djchoices import ChoiceItem, DjangoChoices + + +# TODO: change value when document gets updated: +# https://vng-realisatie.github.io/klantinteracties/informatiemodel/gegevenswoordenboek#detail_class_Model_Initiator +class Initiator(DjangoChoices): + gemeente = ChoiceItem("gemeente", _("Gemeente")) + klant = ChoiceItem("klant", _("Klant")) + vertegenwoordiger = ChoiceItem("vertegenwoordiger", _("Vertegenwoordiger")) + + +class Taakstatus(DjangoChoices): + te_verwerken = ChoiceItem("te_verwerken", _("Het verzoek is afgehandeld.")) + verwerkt = ChoiceItem("verwerkt", _("Het verzoek id buiten behandeling gesteld.")) + + +class SoortBezoekadres(DjangoChoices): + binnenlands_adres = ChoiceItem("binnenlands_adres", _("Binnenlands adres")) + buitenlands_adres = ChoiceItem("binnenlands_adres", _("Buitenlands adres")) + + +class AanduidingBijHuisnummer(DjangoChoices): + bij = ChoiceItem("bij", _("Bij")) + tegenover = ChoiceItem("tegenover", _("Tegenover")) + + +class SoortCorrespondentieadres(DjangoChoices): + postbusnummer = ChoiceItem("postbusnummer", _("Postbusnummer")) + antwoordnummer = ChoiceItem("antwoordnummer", _("Antwoordnummer")) + binnenlands_adres = ChoiceItem("binnenlands_adres", _("Binnenlands adres")) + buitenlands_adres = ChoiceItem("buitenlands_adres", _("Buitenlands adres")) + + +class SoortActor(DjangoChoices): + medewerker = ChoiceItem("medewerker", _("Medewerker")) + geautomatiseerde_actor = ChoiceItem( + "geautomatiseerde_actor", _("Geautomatiseerde actor") + ) + organisatorische_eenheid = ChoiceItem( + "organisatorische_eenheid", _("Organisatorische eenheid") + ) + + +class SoortInhoudsdeel(DjangoChoices): + informatieobject = ChoiceItem("informatieobject", _("Informatieobject")) + overig_object = ChoiceItem("overig_object", _("Overig object")) + tekst = ChoiceItem("tekst", _("Tekst")) + + +class SoortPartij(DjangoChoices): + persoon = ChoiceItem("persoon", _("Persoon")) + organisatie = ChoiceItem("organisatie", _("Organisatie")) + contactpersoon = ChoiceItem("contactpersoon", _("Contactpersoon")) + + +class Klantcontrol(DjangoChoices): + vertegenwoordiger = ChoiceItem("vertegenwoordiger", _("Vertegenwoordiger")) + klant = ChoiceItem("klant", _("Klant")) diff --git a/src/openklant/components/klantinteracties/models/digitaal_adres.py b/src/openklant/components/klantinteracties/models/digitaal_adres.py new file mode 100644 index 00000000..12f172c6 --- /dev/null +++ b/src/openklant/components/klantinteracties/models/digitaal_adres.py @@ -0,0 +1,39 @@ +import uuid + +from django.db import models +from django.utils.translation import gettext_lazy as _ + + +class DigitaalAdres(models.Model): + id = models.UUIDField( + primary_key=True, + unique=True, + default=uuid.uuid4, + help_text=_("Unieke (technische) identificatiecode van het digitaal adres."), + ) + soort_digitaal_adres = models.CharField( + _("Soort digitaal adres"), + help_text=_( + "Typering van het digitale adres die aangeeft via welk(e) kanaal of kanalen " + "met dit adres contact kan worden opgenomen." + ), + max_length=254, + ) + adres = models.CharField( + _("Adres"), + help_text=_( + "Digitaal adres waarmee een persoon of organisatie bereikt kan worden." + ), + max_length=80, + ) + omschrijving = models.CharField( + _("Omschrijving"), + help_text=_("Omschrijving van het digitaal adres."), + max_length=40, + ) + + class Meta: + verbose_name = _("digitaal adres") + + def __str__(self): + return self.adres diff --git a/src/openklant/components/klantinteracties/models/klantcontacten.py b/src/openklant/components/klantinteracties/models/klantcontacten.py new file mode 100644 index 00000000..d97ab1d9 --- /dev/null +++ b/src/openklant/components/klantinteracties/models/klantcontacten.py @@ -0,0 +1,297 @@ +import uuid + +from django.core.validators import MinLengthValidator, validate_integer +from django.db import models +from django.utils.translation import gettext_lazy as _ + +from vng_api_common.descriptors import GegevensGroepType + +from .constants import Initiator, Klantcontrol +from .digitaal_adres import DigitaalAdres + + +class Klantcontact(models.Model): + id = models.UUIDField( + primary_key=True, + unique=True, + default=uuid.uuid4, + help_text=_( + "Unieke (technische) identificatiecode van de betrokkene bij klantcontact." + ), + ) + # TODO: add fk to Actor + # TODO: add fk to Onderwerpobject + # TODO: add fk to Inhoudsobject + nummer = models.CharField( + _("Nummer"), + 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, + ) + kanaal = models.CharField( + _("Kanaal"), + help_text=_("Communicatiekanaal dat bij het klantcontact werd gebruikt."), + max_length=50, + ) + onderwerp = models.CharField( + _("Onderwerp"), + help_text=_("Datgene waarover het klantcontact ging."), + max_length=200, + ) + inhoud = models.CharField( + _("Inhoud"), + help_text=_( + "Informatie die tijdens het klantcontact werd overgebracht of uitgewisseld, " + "voor zover die voor betrokkenen of actoren relevant is." + ), + max_length=1000, + blank=True, + ) + initiator = models.CharField( + _("Initiator"), + help_text=_("Degene die het klantcontact initieerde."), + choices=Initiator.choices, + max_length=17, + ) + indicatie_contact_gelukt = models.BooleanField( + _("Indicatie contact gelukt"), + help_text=( + "Geeft, indien bekend, aan of de poging contact tussen de gemeente " + "en inwoner(s) of organisatie(s) tot stand te brengen succesvol was." + ), + null=True, + ) + taal = models.CharField( + _("Taal"), + help_text=_("Taal die bij het klantcontact werd gesproken of geschreven."), + max_length=255, + ) + vertrouwelijk = models.BooleanField( + _("Vertrouwelijk"), + help_text=_( + "Geeft aan of onderwerp, inhoud en kenmerken van het klantcontact vertrouwelijk moeten worden behandeld." + ), + ) + # TODO: does this field require auto_now? + plaatsgevonden_op = models.DateTimeField( + _("Plaatsgevonden op"), + help_text=_( + "Datum en tijdstip waarop het klantontact plaatsvond. Als het klantcontact " + "een gesprek betrof, is dit het moment waarop het gesprek begon. " + "Als het klantcontact verzending of ontvangst van informatie betrof, " + "is dit bij benadering het moment waarop informatie door gemeente verzonden of ontvangen werd." + ), + blank=False, + ) + + class Meta: + verbose_name = _("klantcontact") + verbose_name_plural = _("klantcontacten") + + +class Betrokkene(models.Model): + id = models.UUIDField( + primary_key=True, + unique=True, + default=uuid.uuid4, + help_text=_( + "Unieke (technische) identificatiecode van de betrokkene bij klantcontact." + ), + ) + klantcontact = models.ForeignKey( + Klantcontact, + on_delete=models.CASCADE, + verbose_name=_("Klantcontact"), + related_name="betrokkene", + help_text=_("'Klantcontact' had 'Betrokkene bij klantcontact'"), + null=False, + ) + digitaal_adres = models.ForeignKey( + DigitaalAdres, + on_delete=models.CASCADE, + verbose_name=_("Digitaal adres"), + related_name="partijen", + help_text=_("'Digitaal Adres' had 'Betrokkene bij klantcontact'"), + null=True, + ) + rol = models.CharField( + _("Rol"), + help_text=_( + "Rol die de betrokkene bij klantcontact tijdens dat contact vervulde." + ), + choices=Klantcontrol.choices, + max_length=17, + ) + organisatienaam = models.CharField( + _("Organisatienaam"), + help_text=_( + "Naam van de organisatie waarmee de betrokkene bij klantcontact een relatie had." + ), + max_length=200, + blank=True, + ) + + # Contactnaam model fields: + contactnaam_voorletters = models.CharField( + _("Voorletters"), + help_text=_( + "Een afkorting van de voornamen. Meestal de beginletter, maar in sommige gevallen " + "de beginletter gecombineerd met de tweede letter van een voornaam." + ), + max_length=10, + ) + contactnaam_voornaam = models.CharField( + _("Voornaam"), + help_text=_( + "De voornaam die de persoon wil gebruiken tijdens communicatie met de gemeente." + ), + max_length=200, + blank=True, + ) + contactnaam_voorvoegsel_achternaam = models.CharField( + _("Voorvoegsel achternaam"), + help_text=_( + "Een eventueel voorvoegsel dat hoort bij de achternaam die de persoon " + "wil gebruiken tijdens communicatie met de gemeente." + ), + max_length=10, + blank=True, + ) + contactnaam_achternaam = models.CharField( + _("Achternaam"), + help_text=_( + "Een achternaam die de persoon wil gebruiken tijdens communicatie met de gemeente." + ), + max_length=200, + blank=True, + ) + + # Correspondentieadres model fields: + correspondentieadres_nummeraanduiding_id = models.UUIDField( + verbose_name=_("Nummeraanduiding ID"), + help_text=_( + "Identificatie van het adres bij de Basisregistratie Adressen en Gebouwen." + ), + unique=True, + blank=True, + null=True, + ) + correspondentieadres_adresregel1 = models.CharField( + _("Adresregel 1"), + help_text=_( + "Eerste deel van het adres dat niet voorkomt in de Basisregistratie Adressen en Gebouwen." + ), + max_length=80, + blank=True, + ) + correspondentieadres_adresregel2 = models.CharField( + _("Adresregel 2"), + help_text=_( + "Tweede deel van het adres dat niet voorkomt in de Basisregistratie Adressen en Gebouwen." + ), + max_length=80, + blank=True, + ) + correspondentieadres_adresregel3 = models.CharField( + _("Adresregel 3"), + help_text=_( + "Derde deel van het adres dat niet voorkomt in de Basisregistratie Adressen en Gebouwen." + ), + max_length=80, + blank=True, + ) + correspondentieadres_land = models.CharField( + _("Land"), + help_text=_( + "Een code, opgenomen in Tabel 34, Landentabel, die het land (buiten Nederland) " + "aangeeft alwaar de ingeschrevene verblijft." + ), + validators=[ + MinLengthValidator(limit_value=4), + validate_integer, + ], + max_length=4, + blank=True, + ) + + # Bezoekadres model fields: + bezoekadres_nummeraanduiding_id = models.UUIDField( + verbose_name=_("Nummeraanduiding ID"), + help_text=_( + "Identificatie van het adres bij de Basisregistratie Adressen en Gebouwen." + ), + unique=True, + blank=True, + null=True, + ) + bezoekadres_adresregel1 = models.CharField( + _("Adresregel 1"), + help_text=_( + "Eerste deel van het adres dat niet voorkomt in de Basisregistratie Adressen en Gebouwen." + ), + max_length=80, + blank=True, + ) + bezoekadres_adresregel2 = models.CharField( + _("Adresregel 2"), + help_text=_( + "Tweede deel van het adres dat niet voorkomt in de Basisregistratie Adressen en Gebouwen." + ), + max_length=80, + blank=True, + ) + bezoekadres_adresregel3 = models.CharField( + _("Adresregel 3"), + help_text=_( + "Derde deel van het adres dat niet voorkomt in de Basisregistratie Adressen en Gebouwen." + ), + max_length=80, + blank=True, + ) + bezoekadres_land = models.CharField( + _("Land"), + help_text=_( + "Een code, opgenomen in Tabel 34, Landentabel, die het land (buiten Nederland) " + "aangeeft alwaar de ingeschrevene verblijft." + ), + validators=[ + MinLengthValidator(limit_value=4), + validate_integer, + ], + max_length=4, + blank=True, + ) + + # Group types: + Contactnaam = GegevensGroepType( + { + "voorletters": contactnaam_voorletters, + "voornaam": contactnaam_voornaam, + "voorvoegsel achternaam": contactnaam_voorvoegsel_achternaam, + "achternaam": contactnaam_achternaam, + } + ) + correspondentieadres = GegevensGroepType( + { + "nummeraanduiding id": correspondentieadres_nummeraanduiding_id, + "adresregel 1": correspondentieadres_adresregel1, + "adresregel 2": correspondentieadres_adresregel2, + "adresregel 3": correspondentieadres_adresregel3, + "land": correspondentieadres_land, + }, + ) + bezoekadres = GegevensGroepType( + { + "nummeraanduiding id": bezoekadres_nummeraanduiding_id, + "adresregel 1": bezoekadres_adresregel1, + "adresregel 2": bezoekadres_adresregel2, + "adresregel 3": bezoekadres_adresregel3, + "land": bezoekadres_land, + } + ) + + class Meta: + verbose_name = _("betrokkene bij klantcontact") diff --git a/src/openklant/components/klantinteracties/models/partijen.py b/src/openklant/components/klantinteracties/models/partijen.py new file mode 100644 index 00000000..dce84a9d --- /dev/null +++ b/src/openklant/components/klantinteracties/models/partijen.py @@ -0,0 +1,364 @@ +import uuid + +from django.core.validators import MinLengthValidator, validate_integer +from django.db import models +from django.utils.translation import gettext_lazy as _ + +from vng_api_common.descriptors import GegevensGroepType + +from .constants import SoortPartij +from .digitaal_adres import DigitaalAdres +from .klantcontacten import Betrokkene + + +class Partij(models.Model): + id = models.UUIDField( + primary_key=True, + unique=True, + default=uuid.uuid4, + help_text=_("Unieke (technische) identificatiecode van de partij."), + ) + betrokkene = models.ForeignKey( + Betrokkene, + on_delete=models.CASCADE, + verbose_name=_("Betrokkene"), + related_name="partijen", + help_text=_("'Betrokkene bij klantcontact' was 'Partij'"), + null=True, + ) + digitaal_adres = models.ForeignKey( + DigitaalAdres, + on_delete=models.CASCADE, + verbose_name=_("Digitaal adres"), + related_name="digitale_adressen", + help_text=_("'Digitaal Adres' was 'Partij'"), + null=True, + ) + nummer = models.CharField( + _("Nummer"), + help_text=_( + "Uniek identificerend nummer dat tijdens communicatie tussen mensen kan " + "worden gebruikt om de specifieke partij aan te duiden." + ), + validators=[validate_integer], + max_length=10, + ) + interne_notitie = models.CharField( + _("Interne notitie"), + help_text=_( + "Mededelingen, aantekeningen of bijzonderheden over de partij, bedoeld voor intern gebruik." + ), + max_length=1000, + blank=True, + ) + soort_partij = models.CharField( + _("Soort partij"), + help_text=_("Geeft aan van welke specifieke soort partij sprake is."), + max_length=14, + choices=SoortPartij.choices, + ) + indicatie_geheimhouding = models.BooleanField( + _("Indicatie geheimhouding"), + help_text=_( + "Geeft aan of de verstrekker van partijgegevens heeft aangegeven dat " + "deze gegevens als geheim beschouwd moeten worden." + ), + ) + voorkeurskanaal = models.CharField( + _("Voorkeurskanaal"), + help_text=_( + "Kanaal dat de partij bij voorkeur gebruikt voor contact met de gemeente." + ), + max_length=50, + blank=True, + ) + voorkeurstaal = models.CharField( + _("Voorkeurstaal"), + help_text=_( + "Taal waarin de partij bij voorkeur contact heeft met de gemeente." + ), + max_length=255, + blank=True, + ) + indicatie_actief = models.BooleanField( + _("Indicatie actief"), + help_text=_( + "Geeft aan of de contactgegevens van de partij nog gebruikt morgen worden om contact op te nemen. " + "Gegevens van niet-actieve partijen mogen hiervoor niet worden gebruikt." + ), + ) + + # Correspondentieadres model fields: + correspondentieadres_nummeraanduiding_id = models.UUIDField( + verbose_name=_("Nummeraanduiding ID"), + help_text=_( + "Identificatie van het adres bij de Basisregistratie Adressen en Gebouwen." + ), + unique=True, + blank=True, + null=True, + ) + correspondentieadres_adresregel1 = models.CharField( + _("Adresregel 1"), + help_text=_( + "Eerste deel van het adres dat niet voorkomt in de Basisregistratie Adressen en Gebouwen." + ), + max_length=80, + blank=True, + ) + correspondentieadres_adresregel2 = models.CharField( + _("Adresregel 2"), + help_text=_( + "Tweede deel van het adres dat niet voorkomt in de Basisregistratie Adressen en Gebouwen." + ), + max_length=80, + blank=True, + ) + correspondentieadres_adresregel3 = models.CharField( + _("Adresregel 3"), + help_text=_( + "Derde deel van het adres dat niet voorkomt in de Basisregistratie Adressen en Gebouwen." + ), + max_length=80, + blank=True, + ) + correspondentieadres_land = models.CharField( + _("Land"), + help_text=_( + "Een code, opgenomen in Tabel 34, Landentabel, die het land (buiten Nederland) " + "aangeeft alwaar de ingeschrevene verblijft." + ), + validators=[ + MinLengthValidator(limit_value=4), + validate_integer, + ], + max_length=4, + blank=True, + ) + + # Bezoekadres model fields: + bezoekadres_nummeraanduiding_id = models.UUIDField( + verbose_name=_("Nummeraanduiding ID"), + help_text=_( + "Identificatie van het adres bij de Basisregistratie Adressen en Gebouwen." + ), + unique=True, + blank=True, + null=True, + ) + bezoekadres_adresregel1 = models.CharField( + _("Adresregel 1"), + help_text=_( + "Eerste deel van het adres dat niet voorkomt in de Basisregistratie Adressen en Gebouwen." + ), + max_length=80, + blank=True, + ) + bezoekadres_adresregel2 = models.CharField( + _("Adresregel 2"), + help_text=_( + "Tweede deel van het adres dat niet voorkomt in de Basisregistratie Adressen en Gebouwen." + ), + max_length=80, + blank=True, + ) + bezoekadres_adresregel3 = models.CharField( + _("Adresregel 3"), + help_text=_( + "Derde deel van het adres dat niet voorkomt in de Basisregistratie Adressen en Gebouwen." + ), + max_length=80, + blank=True, + ) + bezoekadres_land = models.CharField( + _("Land"), + help_text=_( + "Een code, opgenomen in Tabel 34, Landentabel, die het land (buiten Nederland) " + "aangeeft alwaar de ingeschrevene verblijft." + ), + validators=[ + MinLengthValidator(limit_value=4), + validate_integer, + ], + max_length=4, + blank=True, + ) + + # Group types: + correspondentieadres = GegevensGroepType( + { + "nummeraanduiding id": correspondentieadres_nummeraanduiding_id, + "adresregel 1": correspondentieadres_adresregel1, + "adresregel 2": correspondentieadres_adresregel2, + "adresregel 3": correspondentieadres_adresregel3, + "land": correspondentieadres_land, + }, + ) + bezoekadres = GegevensGroepType( + { + "nummeraanduiding id": bezoekadres_nummeraanduiding_id, + "adresregel 1": bezoekadres_adresregel1, + "adresregel 2": bezoekadres_adresregel2, + "adresregel 3": bezoekadres_adresregel3, + "land": bezoekadres_land, + } + ) + + class Meta: + verbose_name = _("partij") + verbose_name_plural = _("partijen") + + +class Organisatie(models.Model): + partij = models.ForeignKey( + Partij, + on_delete=models.CASCADE, + verbose_name=_("Partij"), + related_name="organisatie", + null=True, + ) + naam = models.CharField( + _("Naam"), + help_text=_("Naam van de organisatie."), + max_length=200, + blank=True, + ) + + class Meta: + verbose_name = _("organisatie") + verbose_name_plural = _("organisaties") + + def __str__(self) -> str: + return self.naam + + +class Persoon(models.Model): + partij = models.ForeignKey( + Partij, + on_delete=models.CASCADE, + verbose_name=_("Partij"), + related_name="persoon", + null=True, + ) + + # Contactnaam model fields: + contactnaam_voorletters = models.CharField( + _("Voorletters"), + help_text=_( + "Een afkorting van de voornamen. Meestal de beginletter, maar in sommige gevallen " + "de beginletter gecombineerd met de tweede letter van een voornaam." + ), + max_length=10, + ) + contactnaam_voornaam = models.CharField( + _("Voornaam"), + help_text=_( + "De voornaam die de persoon wil gebruiken tijdens communicatie met de gemeente." + ), + max_length=200, + blank=True, + ) + contactnaam_voorvoegsel_achternaam = models.CharField( + _("Voorvoegsel achternaam"), + help_text=_( + "Een eventueel voorvoegsel dat hoort bij de achternaam die de persoon " + "wil gebruiken tijdens communicatie met de gemeente." + ), + max_length=10, + blank=True, + ) + contactnaam_achternaam = models.CharField( + _("Achternaam"), + help_text=_( + "Een achternaam die de persoon wil gebruiken tijdens communicatie met de gemeente." + ), + max_length=200, + blank=True, + ) + + # Group types: + Contactnaam = GegevensGroepType( + { + "voorletters": contactnaam_voorletters, + "voornaam": contactnaam_voornaam, + "voorvoegsel achternaam": contactnaam_voorvoegsel_achternaam, + "achternaam": contactnaam_achternaam, + } + ) + + class Meta: + verbose_name = _("persoon") + verbose_name_plural = _("personen") + + def __str__(self) -> str: + return self.contactnaam_voorletters + + +class Contactpersoon(models.Model): + partij = models.ForeignKey( + Partij, + on_delete=models.CASCADE, + verbose_name=_("Partij"), + related_name="contact_persoon", + null=True, + ) + organisatie = models.ForeignKey( + Organisatie, + on_delete=models.CASCADE, + verbose_name=_("Organistatie"), + related_name="contact_personen", + help_text=_("De organisatie waar een contactpersoon voor werkt."), + null=True, + ) + + # Contactnaam model fields: + contactnaam_voorletters = models.CharField( + _("Voorletters"), + help_text=_( + "Een afkorting van de voornamen. Meestal de beginletter, maar in sommige gevallen " + "de beginletter gecombineerd met de tweede letter van een voornaam." + ), + max_length=10, + ) + contactnaam_voornaam = models.CharField( + _("Voornaam"), + help_text=_( + "De voornaam die de persoon wil gebruiken tijdens communicatie met de gemeente." + ), + max_length=200, + blank=True, + ) + contactnaam_voorvoegsel_achternaam = models.CharField( + _("Voorvoegsel achternaam"), + help_text=_( + "Een eventueel voorvoegsel dat hoort bij de achternaam die de persoon " + "wil gebruiken tijdens communicatie met de gemeente." + ), + max_length=10, + blank=True, + ) + contactnaam_achternaam = models.CharField( + _("Achternaam"), + help_text=_( + "Een achternaam die de persoon wil gebruiken tijdens communicatie met de gemeente." + ), + max_length=200, + blank=True, + ) + + # Group types: + Contactnaam = GegevensGroepType( + { + "voorletters": contactnaam_voorletters, + "voornaam": contactnaam_voornaam, + "voorvoegsel achternaam": contactnaam_voorvoegsel_achternaam, + "achternaam": contactnaam_achternaam, + } + ) + + class Meta: + verbose_name = _("contact persoon") + verbose_name_plural = _("contact personen") + + def __str__(self) -> str: + return self.contactnaam_voorletters diff --git a/src/openklant/conf/base.py b/src/openklant/conf/base.py index 1dec9277..ab846f2e 100644 --- a/src/openklant/conf/base.py +++ b/src/openklant/conf/base.py @@ -137,6 +137,7 @@ "openklant.utils", "openklant.components.klanten", "openklant.components.contactmomenten", + "openklant.components.klantinteracties", ] MIDDLEWARE = [