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..63528173 --- /dev/null +++ b/src/openklant/components/klantinteracties/migrations/0001_initial.py @@ -0,0 +1,1089 @@ +# Generated by Django 3.2.18 on 2023-10-19 13:54 + +import django.core.validators +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone +import uuid + + +class Migration(migrations.Migration): + initial = True + + dependencies = [] + + operations = [ + migrations.CreateModel( + name="Actor", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "identificator_objecttype", + models.CharField( + help_text="Type van het object, bijvoorbeeld: 'INGESCHREVEN NATUURLIJK PERSOON'.", + max_length=200, + verbose_name="objecttype", + ), + ), + ( + "identificator_soort_object_id", + models.CharField( + help_text="Naam van de eigenschap die het object identificeert, bijvoorbeeld: 'Burgerservicenummer'.", + max_length=200, + verbose_name="soort object id", + ), + ), + ( + "identificator_object_id", + models.CharField( + help_text="Waarde van de eigenschap die het object identificeert, bijvoorbeeld: '123456788'.", + max_length=200, + verbose_name="object id", + ), + ), + ( + "identificator_register", + models.CharField( + help_text="Binnen het landschap van registers unieke omschrijving van het register waarin het object is geregistreerd, bijvoorbeeld: 'BRP'.", + max_length=200, + verbose_name="register", + ), + ), + ( + "uuid", + models.UUIDField( + default=uuid.uuid4, + help_text="Unieke (technische) identificatiecode van de actor.", + unique=True, + ), + ), + ( + "naam", + models.CharField( + help_text="Naam van de actor.", + max_length=200, + verbose_name="naam", + ), + ), + ( + "soort_actor", + models.CharField( + choices=[ + ("medewerker", "Medewerker"), + ("geautomatiseerde_actor", "Geautomatiseerde actor"), + ("organisatorische_eenheid", "Organisatorische eenheid"), + ], + help_text="Geeft aan van welke specifieke soort actor sprake is.", + max_length=24, + verbose_name="soort actor", + ), + ), + ( + "indicatie_actief", + models.BooleanField( + default=True, + help_text="Geeft aan of aan de actor nog betrokken mag worden bij nieuwe klantcontacten. Voor niet-actieve is dit niet toegestaan.", + verbose_name="indicatie actief", + ), + ), + ], + options={ + "verbose_name": "actor", + "verbose_name_plural": "actoren", + }, + ), + migrations.CreateModel( + name="Betrokkene", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "bezoekadres_nummeraanduiding_id", + models.CharField( + blank=True, + help_text="Identificatie van het adres bij de Basisregistratie Adressen en Gebouwen.", + max_length=255, + 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", + ), + ), + ( + "correspondentieadres_nummeraanduiding_id", + models.CharField( + blank=True, + help_text="Identificatie van het adres bij de Basisregistratie Adressen en Gebouwen.", + max_length=255, + 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", + ), + ), + ( + "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", + ), + ), + ( + "uuid", + models.UUIDField( + default=uuid.uuid4, + help_text="Unieke (technische) identificatiecode van de betrokkene bij klantcontact.", + 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", + ), + ), + ("initiator", models.BooleanField(verbose_name="initiator")), + ], + options={ + "verbose_name": "betrokkene bij klantcontact", + }, + ), + migrations.CreateModel( + name="DigitaalAdres", + 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 het digitaal adres.", + 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=255, + 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.CreateModel( + name="Partij", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "bezoekadres_nummeraanduiding_id", + models.CharField( + blank=True, + help_text="Identificatie van het adres bij de Basisregistratie Adressen en Gebouwen.", + max_length=255, + 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", + ), + ), + ( + "correspondentieadres_nummeraanduiding_id", + models.CharField( + blank=True, + help_text="Identificatie van het adres bij de Basisregistratie Adressen en Gebouwen.", + max_length=255, + 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", + ), + ), + ( + "uuid", + models.UUIDField( + default=uuid.uuid4, + help_text="Unieke (technische) identificatiecode van de partij.", + 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.TextField( + 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", + ), + ), + ( + "voorkeurstaal", + models.CharField( + blank=True, + help_text="Taal, in ISO 639-2/B formaat, waarin de partij bij voorkeur contact heeft met de gemeente. Voorbeeld: nld. Zie: https://www.iso.org/standard/4767.html", + max_length=3, + 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", + ), + ), + ( + "betrokkene", + models.ForeignKey( + help_text="'Betrokkene bij klantcontact' was 'Partij'", + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="klantinteracties.betrokkene", + verbose_name="betrokkene", + ), + ), + ( + "digitaal_adres", + models.ForeignKey( + help_text="'Digitaal Adres' was 'Partij'", + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="klantinteracties.digitaaladres", + verbose_name="digitaal adres", + ), + ), + ( + "vertegenwoordigde", + models.ManyToManyField( + blank=True, + help_text="'Partij' die een andere 'Partij' vertegenwoordigde.", + null=True, + related_name="_klantinteracties_partij_vertegenwoordigde_+", + to="klantinteracties.Partij", + verbose_name="vertegenwoordigde", + ), + ), + ( + "voorkeurs_digitaal_adres", + models.ForeignKey( + help_text="'Partij' gaf voorkeur aan voor contact via 'Digitaal adres'", + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="voorkeurs_partij", + to="klantinteracties.digitaaladres", + verbose_name="voorkeurs digitaal adres", + ), + ), + ], + 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( + on_delete=django.db.models.deletion.CASCADE, + to="klantinteracties.partij", + unique=True, + verbose_name="partij", + ), + ), + ], + options={ + "verbose_name": "persoon", + "verbose_name_plural": "personen", + }, + ), + migrations.CreateModel( + name="OrganisatorischeEenheid", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "omschrijving", + models.CharField( + blank=True, + help_text="Omschrijving van de geautomatiseerde actor.", + max_length=200, + verbose_name="omschrijving", + ), + ), + ( + "emailadres", + models.EmailField( + blank=True, + help_text="Elektronisch postadres waaronder de MEDEWERKER in de regel bereikbaar is.", + max_length=254, + verbose_name="e-mailadres", + ), + ), + ( + "faxnummer", + models.CharField( + help_text="Faxnummer waaronder de organisatorische eenheid in de regel bereikbaar is.", + max_length=20, + verbose_name="faxnummer", + ), + ), + ( + "telefoonnummer", + models.CharField( + help_text="Telefoonnummer waaronder de MEDEWERKER in de regel bereikbaar is.", + max_length=20, + verbose_name="telefoonnummer", + ), + ), + ( + "actor", + models.ForeignKey( + help_text="'GeautomatiseerdeActor' was 'Actor'", + on_delete=django.db.models.deletion.CASCADE, + to="klantinteracties.actor", + unique=True, + verbose_name="actor", + ), + ), + ], + options={ + "verbose_name": "organisatorische eenheid", + }, + ), + 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( + on_delete=django.db.models.deletion.CASCADE, + to="klantinteracties.partij", + unique=True, + verbose_name="partij", + ), + ), + ], + options={ + "verbose_name": "organisatie", + "verbose_name_plural": "organisaties", + }, + ), + migrations.CreateModel( + name="Medewerker", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "functie", + models.CharField( + help_text="Functie van de geautomatiseerde actor of beschrijving van de werkzaamheden die deze uitvoert.", + max_length=40, + verbose_name="functie", + ), + ), + ( + "emailadres", + models.EmailField( + blank=True, + help_text="Elektronisch postadres waaronder de MEDEWERKER in de regel bereikbaar is.", + max_length=254, + verbose_name="e-mailadres", + ), + ), + ( + "telefoonnummer", + models.CharField( + help_text="Telefoonnummer waaronder de MEDEWERKER in de regel bereikbaar is.", + max_length=20, + verbose_name="telefoonnummer", + ), + ), + ( + "actor", + models.ForeignKey( + help_text="'GeautomatiseerdeActor' was 'Actor'", + on_delete=django.db.models.deletion.CASCADE, + to="klantinteracties.actor", + verbose_name="actor", + ), + ), + ], + options={ + "verbose_name": "medewerker", + "verbose_name_plural": "mederwerkers", + }, + ), + migrations.CreateModel( + name="Klantcontact", + 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 betrokkene bij klantcontact.", + 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.TextField( + 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", + ), + ), + ( + "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( + default=django.utils.timezone.now, + 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", + ), + ), + ( + "actoren", + models.ManyToManyField( + help_text="De actoren die tijdens het klantcontant contact had met klanten of hun vertegenwoordigers.", + related_name="klantcontacten", + to="klantinteracties.Actor", + verbose_name="actoren", + ), + ), + ], + options={ + "verbose_name": "klantcontact", + "verbose_name_plural": "klantcontacten", + }, + ), + migrations.CreateModel( + name="InterneTaak", + 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 interne taak.", + unique=True, + ), + ), + ( + "nummer", + models.CharField( + help_text="Uniek identificerend nummer dat tijdens communicatie tussen mensen kan worden gebruikt om de specifieke interne taak aan te duiden.", + max_length=10, + validators=[django.core.validators.validate_integer], + verbose_name="nummer", + ), + ), + ( + "gevraagde_handeling", + models.CharField( + help_text="Handeling die moet worden uitgevoerd om de taak af te ronden.", + max_length=200, + verbose_name="gevraagde handeling", + ), + ), + ( + "toelichting", + models.TextField( + help_text="Toelichting die, aanvullend bij de inhoud van het klantcontact dat aanleiding gaf tot de taak en de gevraagde handeling, bijdraagt aan het kunnen afhandelen van de taak.", + max_length=400, + verbose_name="toelichting", + ), + ), + ( + "status", + models.CharField( + choices=[ + ("te_verwerken", "Het verzoek is afgehandeld."), + ("verwerkt", "Het verzoek id buiten behandeling gesteld."), + ], + help_text="Aanduiding van de vordering bij afhandeling van de interne taak.", + max_length=12, + verbose_name="status", + ), + ), + ( + "toegewezen_op", + models.DateTimeField( + auto_now_add=True, + help_text="Datum en tijdstip waarop de interne taak aan een actor werd toegewezen.", + verbose_name="toegewezen op", + ), + ), + ( + "actor", + models.ForeignKey( + help_text="De actor aan wie de interne taak werd toegewezen.", + on_delete=django.db.models.deletion.CASCADE, + to="klantinteracties.actor", + verbose_name="actor", + ), + ), + ( + "klantcontact", + models.ForeignKey( + help_text="Het klantcontact dat aanleiding gaf tot het ontstaan van een interne taak.", + on_delete=django.db.models.deletion.CASCADE, + to="klantinteracties.klantcontact", + verbose_name="klantcontact", + ), + ), + ], + options={ + "verbose_name": "interne taak", + "verbose_name_plural": "interne taken", + }, + ), + migrations.CreateModel( + name="GeautomatiseerdeActor", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "functie", + models.CharField( + help_text="Functie van de geautomatiseerde actor of beschrijving van de werkzaamheden die deze uitvoert.", + max_length=40, + verbose_name="functie", + ), + ), + ( + "omschrijving", + models.CharField( + blank=True, + help_text="Omschrijving van de geautomatiseerde actor.", + max_length=200, + verbose_name="omschrijving", + ), + ), + ( + "actor", + models.ForeignKey( + help_text="'GeautomatiseerdeActor' was 'Actor'", + on_delete=django.db.models.deletion.CASCADE, + to="klantinteracties.actor", + verbose_name="Actor", + ), + ), + ], + options={ + "verbose_name": "geautomatiseerde actor", + "verbose_name_plural": "geautomatiseerde actoren", + }, + ), + 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, + to="klantinteracties.organisatie", + verbose_name="organistatie", + ), + ), + ( + "partij", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="klantinteracties.partij", + unique=True, + verbose_name="partij", + ), + ), + ], + options={ + "verbose_name": "contact persoon", + "verbose_name_plural": "contact personen", + }, + ), + 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, + to="klantinteracties.digitaaladres", + verbose_name="digitaal adres", + ), + ), + migrations.AddField( + model_name="betrokkene", + name="klantcontact", + field=models.ForeignKey( + help_text="'Klantcontact' had 'Betrokkene bij klantcontact'", + on_delete=django.db.models.deletion.CASCADE, + to="klantinteracties.klantcontact", + verbose_name="klantcontact", + ), + ), + ] 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..90a38d8c --- /dev/null +++ b/src/openklant/components/klantinteracties/models/__init__.py @@ -0,0 +1,5 @@ +from .actoren import * # noqa +from .digitaal_adres import * # noqa +from .internetaken import * # noqa +from .klantcontacten import * # noqa +from .partijen import * # noqa diff --git a/src/openklant/components/klantinteracties/models/actoren.py b/src/openklant/components/klantinteracties/models/actoren.py new file mode 100644 index 00000000..2c7858af --- /dev/null +++ b/src/openklant/components/klantinteracties/models/actoren.py @@ -0,0 +1,150 @@ +import uuid + +from django.db import models +from django.utils.translation import gettext_lazy as _ + +from .constants import SoortActor +from .mixins import IdentificatorMixin + + +class Actor(IdentificatorMixin): + uuid = models.UUIDField( + unique=True, + default=uuid.uuid4, + help_text=_("Unieke (technische) identificatiecode van de actor."), + ) + naam = models.CharField( + _("naam"), + help_text=_("Naam van de actor."), + max_length=200, + ) + soort_actor = models.CharField( + _("soort actor"), + help_text=_("Geeft aan van welke specifieke soort actor sprake is."), + choices=SoortActor.choices, + max_length=24, + ) + indicatie_actief = models.BooleanField( + _("indicatie actief"), + help_text=_( + "Geeft aan of aan de actor nog betrokken mag worden bij nieuwe klantcontacten. " + "Voor niet-actieve is dit niet toegestaan." + ), + default=True, + ) + + class Meta: + verbose_name = _("actor") + verbose_name_plural = _("actoren") + + def __str__(self): + return self.naam + + +class GeautomatiseerdeActor(models.Model): + actor = models.ForeignKey( + Actor, + on_delete=models.CASCADE, + verbose_name=_("Actor"), + help_text=_("'GeautomatiseerdeActor' was 'Actor'"), + ) + functie = models.CharField( + _("functie"), + help_text=_( + "Functie van de geautomatiseerde actor of beschrijving van de werkzaamheden die deze uitvoert." + ), + max_length=40, + ) + omschrijving = models.CharField( + _("omschrijving"), + help_text=_("Omschrijving van de geautomatiseerde actor."), + max_length=200, + blank=True, + ) + + class Meta: + verbose_name = _("geautomatiseerde actor") + verbose_name_plural = _("geautomatiseerde actoren") + + def __str__(self): + return self.functie + + +class Medewerker(models.Model): + actor = models.ForeignKey( + Actor, + on_delete=models.CASCADE, + verbose_name=_("actor"), + help_text=_("'GeautomatiseerdeActor' was 'Actor'"), + ) + functie = models.CharField( + _("functie"), + help_text=_( + "Functie van de geautomatiseerde actor of beschrijving van de werkzaamheden die deze uitvoert." + ), + max_length=40, + ) + emailadres = models.EmailField( + _("e-mailadres"), + help_text=_( + "Elektronisch postadres waaronder de MEDEWERKER in de regel bereikbaar is." + ), + blank=True, + ) + telefoonnummer = models.CharField( + _("telefoonnummer"), + help_text=_( + "Telefoonnummer waaronder de MEDEWERKER in de regel bereikbaar is." + ), + max_length=20, + ) + + class Meta: + verbose_name = _("medewerker") + verbose_name_plural = _("mederwerkers") + + def __str__(self): + return self.functie + + +class OrganisatorischeEenheid(models.Model): + actor = models.ForeignKey( + Actor, + on_delete=models.CASCADE, + verbose_name=_("actor"), + help_text=_("'GeautomatiseerdeActor' was 'Actor'"), + unique=True, + ) + omschrijving = models.CharField( + _("omschrijving"), + help_text=_("Omschrijving van de geautomatiseerde actor."), + max_length=200, + blank=True, + ) + emailadres = models.EmailField( + _("e-mailadres"), + help_text=_( + "Elektronisch postadres waaronder de MEDEWERKER in de regel bereikbaar is." + ), + blank=True, + ) + faxnummer = models.CharField( + _("faxnummer"), + help_text=_( + "Faxnummer waaronder de organisatorische eenheid in de regel bereikbaar is." + ), + max_length=20, + ) + telefoonnummer = models.CharField( + _("telefoonnummer"), + help_text=_( + "Telefoonnummer waaronder de MEDEWERKER in de regel bereikbaar is." + ), + max_length=20, + ) + + class Meta: + verbose_name = _("organisatorische eenheid") + + def __str__(self): + return self.omschrijving diff --git a/src/openklant/components/klantinteracties/models/constants.py b/src/openklant/components/klantinteracties/models/constants.py new file mode 100644 index 00000000..c9daac7f --- /dev/null +++ b/src/openklant/components/klantinteracties/models/constants.py @@ -0,0 +1,52 @@ +from django.utils.translation import gettext_lazy as _ + +from djchoices import ChoiceItem, DjangoChoices + + +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..aecaa247 --- /dev/null +++ b/src/openklant/components/klantinteracties/models/digitaal_adres.py @@ -0,0 +1,38 @@ +import uuid + +from django.db import models +from django.utils.translation import gettext_lazy as _ + + +class DigitaalAdres(models.Model): + uuid = models.UUIDField( + 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=255, + ) + 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/internetaken.py b/src/openklant/components/klantinteracties/models/internetaken.py new file mode 100644 index 00000000..0509dd8a --- /dev/null +++ b/src/openklant/components/klantinteracties/models/internetaken.py @@ -0,0 +1,72 @@ +import uuid + +from django.core.validators import validate_integer +from django.db import models +from django.utils.translation import gettext_lazy as _ + +from .actoren import Actor +from .constants import Taakstatus +from .klantcontacten import Klantcontact + + +class InterneTaak(models.Model): + uuid = models.UUIDField( + unique=True, + default=uuid.uuid4, + help_text=_("Unieke (technische) identificatiecode van de interne taak."), + ) + actor = models.ForeignKey( + Actor, + on_delete=models.CASCADE, + verbose_name=_("actor"), + help_text=_("De actor aan wie de interne taak werd toegewezen."), + ) + klantcontact = models.ForeignKey( + Klantcontact, + on_delete=models.CASCADE, + verbose_name=_("klantcontact"), + help_text=_( + "Het klantcontact dat aanleiding gaf tot het ontstaan van een interne taak." + ), + ) + nummer = models.CharField( + _("nummer"), + help_text=_( + "Uniek identificerend nummer dat tijdens communicatie tussen mensen kan " + "worden gebruikt om de specifieke interne taak aan te duiden." + ), + validators=[validate_integer], + max_length=10, + ) + gevraagde_handeling = models.CharField( + _("gevraagde handeling"), + help_text=_("Handeling die moet worden uitgevoerd om de taak af te ronden."), + max_length=200, + ) + toelichting = models.TextField( + _("toelichting"), + help_text=_( + "Toelichting die, aanvullend bij de inhoud van het klantcontact dat " + "aanleiding gaf tot de taak en de gevraagde handeling, " + "bijdraagt aan het kunnen afhandelen van de taak." + ), + max_length=400, + ) + status = models.CharField( + _("status"), + help_text=_("Aanduiding van de vordering bij afhandeling van de interne taak."), + choices=Taakstatus.choices, + max_length=12, + ) + toegewezen_op = models.DateTimeField( + _("toegewezen op"), + help_text=_( + "Datum en tijdstip waarop de interne taak aan een actor werd toegewezen." + ), + auto_now_add=True, + blank=False, + ) + + class Meta: + verbose_name = _("interne taak") + verbose_name_plural = _("interne taken") diff --git a/src/openklant/components/klantinteracties/models/klantcontacten.py b/src/openklant/components/klantinteracties/models/klantcontacten.py new file mode 100644 index 00000000..9867cbf6 --- /dev/null +++ b/src/openklant/components/klantinteracties/models/klantcontacten.py @@ -0,0 +1,141 @@ +import uuid + +from django.core.validators import validate_integer +from django.db import models +from django.utils import timezone +from django.utils.translation import gettext_lazy as _ + +from .actoren import Actor +from .constants import Klantcontrol +from .digitaal_adres import DigitaalAdres +from .mixins import BezoekAdresMixin, ContactNaamMixin, CorrespondentieAdresMixin + + +class Klantcontact(models.Model): + uuid = models.UUIDField( + unique=True, + default=uuid.uuid4, + help_text=_( + "Unieke (technische) identificatiecode van de betrokkene bij klantcontact." + ), + ) + actoren = models.ManyToManyField( + Actor, + verbose_name=_("actoren"), + related_name="klantcontacten", + help_text=_( + "De actoren die tijdens het klantcontant contact had met klanten of hun vertegenwoordigers." + ), + blank=False, + ) + # 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.TextField( + _("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, + ) + 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." + ), + default=timezone.now, + blank=False, + ) + + class Meta: + verbose_name = _("klantcontact") + verbose_name_plural = _("klantcontacten") + + +class Betrokkene(BezoekAdresMixin, CorrespondentieAdresMixin, ContactNaamMixin): + uuid = models.UUIDField( + 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"), + help_text=_("'Klantcontact' had 'Betrokkene bij klantcontact'"), + ) + digitaal_adres = models.ForeignKey( + DigitaalAdres, + on_delete=models.CASCADE, + verbose_name=_("digitaal adres"), + 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, + ) + # TODO: add help_text when it is provided + initiator = models.BooleanField( + _("initiator"), + ) + + class Meta: + verbose_name = _("betrokkene bij klantcontact") diff --git a/src/openklant/components/klantinteracties/models/mixins.py b/src/openklant/components/klantinteracties/models/mixins.py new file mode 100644 index 00000000..b75ab273 --- /dev/null +++ b/src/openklant/components/klantinteracties/models/mixins.py @@ -0,0 +1,225 @@ +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 + + +class BezoekAdresMixin(models.Model): + # TODO: Check if this is correct. + bezoekadres_nummeraanduiding_id = models.CharField( + _("nummeraanduiding id"), + help_text=_( + "Identificatie van het adres bij de Basisregistratie Adressen en Gebouwen." + ), + max_length=255, + blank=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, + ) + + bezoekadres = GegevensGroepType( + { + "nummeraanduiding_id": bezoekadres_nummeraanduiding_id, + "adresregel_1": bezoekadres_adresregel1, + "adresregel_2": bezoekadres_adresregel2, + "adresregel_3": bezoekadres_adresregel3, + "land": bezoekadres_land, + } + ) + + class Meta: + abstract = True + + +class CorrespondentieAdresMixin(models.Model): + # TODO: Check if this is correct. + correspondentieadres_nummeraanduiding_id = models.CharField( + _("nummeraanduiding ID"), + help_text=_( + "Identificatie van het adres bij de Basisregistratie Adressen en Gebouwen." + ), + max_length=255, + blank=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, + ) + + correspondentieadres = GegevensGroepType( + { + "nummeraanduiding_id": correspondentieadres_nummeraanduiding_id, + "adresregel_1": correspondentieadres_adresregel1, + "adresregel_2": correspondentieadres_adresregel2, + "adresregel_3": correspondentieadres_adresregel3, + "land": correspondentieadres_land, + }, + ) + + class Meta: + abstract = True + + +class ContactNaamMixin(models.Model): + 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, + ) + + Contactnaam = GegevensGroepType( + { + "voorletters": contactnaam_voorletters, + "voornaam": contactnaam_voornaam, + "voorvoegsel_achternaam": contactnaam_voorvoegsel_achternaam, + "achternaam": contactnaam_achternaam, + } + ) + + class Meta: + abstract = True + + +class IdentificatorMixin(models.Model): + identificator_objecttype = models.CharField( + _("objecttype"), + help_text=_( + "Type van het object, bijvoorbeeld: 'INGESCHREVEN NATUURLIJK PERSOON'." + ), + max_length=200, + blank=False, + ) + identificator_soort_object_id = models.CharField( + _("soort object id"), + help_text=_( + "Naam van de eigenschap die het object identificeert, bijvoorbeeld: 'Burgerservicenummer'." + ), + max_length=200, + blank=False, + ) + identificator_object_id = models.CharField( + _("object id"), + help_text=_( + "Waarde van de eigenschap die het object identificeert, bijvoorbeeld: '123456788'." + ), + max_length=200, + blank=False, + ) + identificator_register = models.CharField( + _("register"), + help_text=_( + "Binnen het landschap van registers unieke omschrijving van het register waarin " + "het object is geregistreerd, bijvoorbeeld: 'BRP'." + ), + max_length=200, + blank=False, + ) + + identificator = GegevensGroepType( + { + "objecttype": identificator_objecttype, + "soort_object_id": identificator_soort_object_id, + "object_id": identificator_object_id, + "register": identificator_register, + } + ) + + class Meta: + abstract = True diff --git a/src/openklant/components/klantinteracties/models/partijen.py b/src/openklant/components/klantinteracties/models/partijen.py new file mode 100644 index 00000000..016c05a2 --- /dev/null +++ b/src/openklant/components/klantinteracties/models/partijen.py @@ -0,0 +1,158 @@ +import uuid + +from django.core.validators import validate_integer +from django.db import models +from django.utils.translation import gettext_lazy as _ + +from .constants import SoortPartij +from .digitaal_adres import DigitaalAdres +from .klantcontacten import Betrokkene +from .mixins import BezoekAdresMixin, ContactNaamMixin, CorrespondentieAdresMixin + + +class Partij(BezoekAdresMixin, CorrespondentieAdresMixin): + uuid = models.UUIDField( + unique=True, + default=uuid.uuid4, + help_text=_("Unieke (technische) identificatiecode van de partij."), + ) + betrokkene = models.ForeignKey( + Betrokkene, + on_delete=models.CASCADE, + verbose_name=_("betrokkene"), + help_text=_("'Betrokkene bij klantcontact' was 'Partij'"), + null=True, + ) + digitaal_adres = models.ForeignKey( + DigitaalAdres, + on_delete=models.CASCADE, + verbose_name=_("digitaal adres"), + help_text=_("'Digitaal Adres' was 'Partij'"), + null=True, + ) + voorkeurs_digitaal_adres = models.ForeignKey( + DigitaalAdres, + on_delete=models.CASCADE, + related_name="voorkeurs_partij", + verbose_name=_("voorkeurs digitaal adres"), + help_text=_("'Partij' gaf voorkeur aan voor contact via 'Digitaal adres'"), + null=True, + ) + vertegenwoordigde = models.ManyToManyField( + "self", + verbose_name=_("vertegenwoordigde"), + help_text=_("'Partij' die een andere 'Partij' vertegenwoordigde."), + blank=True, + 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.TextField( + _("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." + ), + ) + voorkeurstaal = models.CharField( + _("voorkeurstaal"), + help_text=_( + "Taal, in ISO 639-2/B formaat, waarin de partij bij voorkeur contact heeft " + "met de gemeente. Voorbeeld: nld. Zie: https://www.iso.org/standard/4767.html" + ), + max_length=3, + 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." + ), + ) + + class Meta: + verbose_name = _("partij") + verbose_name_plural = _("partijen") + + +class Organisatie(models.Model): + partij = models.ForeignKey( + Partij, + on_delete=models.CASCADE, + verbose_name=_("partij"), + unique=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(ContactNaamMixin): + partij = models.ForeignKey( + Partij, + on_delete=models.CASCADE, + verbose_name=_("partij"), + unique=True, + ) + + class Meta: + verbose_name = _("persoon") + verbose_name_plural = _("personen") + + def __str__(self) -> str: + return self.contactnaam_voorletters + + +class Contactpersoon(ContactNaamMixin): + partij = models.ForeignKey( + Partij, + on_delete=models.CASCADE, + verbose_name=_("partij"), + unique=True, + ) + organisatie = models.ForeignKey( + Organisatie, + on_delete=models.CASCADE, + verbose_name=_("organistatie"), + help_text=_("De organisatie waar een contactpersoon voor werkt."), + null=True, + ) + + 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 = [