class NietNatuurlijkPersoon(models.Model): rol = models.OneToOneField(Rol, on_delete=models.CASCADE) rsin = RSINField( blank=True, help_text='Het door een kamer toegekend uniek nummer voor de INGESCHREVEN NIET-NATUURLIJK PERSOON', ) nummer_ander_nietnatuurlijk_persoon = models.CharField( max_length=17, help_text='Het door de gemeente uitgegeven uniekenummer voor een ANDER NIET-NATUURLIJK PERSOON') statutaire_naam = models.TextField( max_length=500, blank=True, help_text='Naam van de niet-natuurlijke persoon zoals deze is vastgelegd in de statuten (rechtspersoon) of ' 'in de vennootschapsovereenkomst is overeengekomen (Vennootschap onder firma of Commanditaire ' 'vennootschap).') rechtsvorm = models.CharField( max_length=30, choices=SoortRechtsvorm.choices, blank=True, help_text='De juridische vorm van de NIET-NATUURLIJK PERSOON.' ) bezoekadres = models.CharField( max_length=1000, blank=True, help_text='De gegevens over het adres van de NIET-NATUURLIJK PERSOON', ) sub_verblijf_buitenland = models.CharField( max_length=1000, blank=True, help_text='De gegevens over het verblijf in het buitenland' ) class Meta: verbose_name = 'niet-natuurlijk persoon'
class NietNatuurlijkPersoon(AbstractRolZaakobjectZakelijkRechtRelation): inn_nnp_id = RSINField( blank=True, help_text="Het door een kamer toegekend uniek nummer voor de INGESCHREVEN NIET-NATUURLIJK PERSOON", ) ann_identificatie = models.CharField( max_length=17, blank=True, help_text="Het door de gemeente uitgegeven unieke nummer voor een ANDER NIET-NATUURLIJK PERSOON", ) statutaire_naam = models.TextField( max_length=500, blank=True, help_text="Naam van de niet-natuurlijke persoon zoals deze is vastgelegd in de statuten (rechtspersoon) of " "in de vennootschapsovereenkomst is overeengekomen (Vennootschap onder firma of Commanditaire " "vennootschap).", ) inn_rechtsvorm = models.CharField( max_length=50, choices=SoortRechtsvorm.choices, blank=True, help_text="De juridische vorm van de NIET-NATUURLIJK PERSOON.", ) bezoekadres = models.CharField( max_length=1000, blank=True, help_text="De gegevens over het adres van de NIET-NATUURLIJK PERSOON", ) class Meta: verbose_name = "niet-natuurlijk persoon"
class NietNatuurlijkPersoon(models.Model): klant = models.OneToOneField( "datamodel.Klant", on_delete=models.CASCADE, related_name="niet_natuurlijk_persoon", ) inn_nnp_id = RSINField( blank=True, help_text= "Het door een kamer toegekend uniek nummer voor de INGESCHREVEN NIET-NATUURLIJK PERSOON", ) ann_identificatie = models.CharField( max_length=17, blank=True, help_text= "Het door de gemeente uitgegeven unieke nummer voor een ANDER NIET-NATUURLIJK PERSOON", ) statutaire_naam = models.TextField( max_length=500, blank=True, help_text= "Naam van de niet-natuurlijke persoon zoals deze is vastgelegd in de statuten (rechtspersoon) of " "in de vennootschapsovereenkomst is overeengekomen (Vennootschap onder firma of Commanditaire " "vennootschap).", ) inn_rechtsvorm = models.CharField( max_length=50, choices=SoortRechtsvorm.choices, blank=True, help_text="De juridische vorm van de NIET-NATUURLIJK PERSOON.", ) bezoekadres = models.CharField( max_length=1000, blank=True, help_text="De gegevens over het adres van de NIET-NATUURLIJK PERSOON", ) class Meta: verbose_name = "niet-natuurlijk persoon" def unique_representation(self): return f"{self.inn_nnp_id or self.ann_identificatie}"
class KlantInteractie(models.Model): uuid = models.UUIDField(unique=True, default=uuid.uuid4, help_text="Unieke resource identifier (UUID4)") bronorganisatie = RSINField( help_text="Het RSIN van de Niet-natuurlijk persoon zijnde de " "organisatie die de klantinteractie heeft gecreeerd. Dit moet een " "geldig RSIN zijn van 9 nummers en voldoen aan " "https://nl.wikipedia.org/wiki/Burgerservicenummer#11-proef") klant = models.ForeignKey( Klant, on_delete=models.CASCADE, null=True, blank=True, help_text= _("URL-referentie naar een KLANT indien de klantinteractie niet anoniem is." ), ) interactiedatum = models.DateTimeField( default=timezone.now, help_text= _("De datum en het tijdstip waarop de klantinteractie heeft plaatsgevonden." ), ) tekst = models.TextField( blank=True, help_text= _("Een toelichting die inhoudelijk de klantinteractie van de klant beschrijft." ), ) voorkeurskanaal = models.CharField( max_length=50, blank=True, help_text= _("Het communicatiekanaal dat voor opvolging van de klantinteractie de voorkeur heeft van de KLANT." ), ) class Meta: abstract = True
class Zaak(APIMixin, models.Model): """ Modelleer de structuur van een ZAAK. Een samenhangende hoeveelheid werk met een welgedefinieerde aanleiding en een welgedefinieerd eindresultaat, waarvan kwaliteit en doorlooptijd bewaakt moeten worden. """ uuid = models.UUIDField(unique=True, default=uuid.uuid4, help_text="Unieke resource identifier (UUID4)") # Relate 'is_deelzaak_van' # De relatie vanuit een zaak mag niet verwijzen naar # dezelfde zaak d.w.z. moet verwijzen naar een andere # zaak. Die andere zaak mag geen relatie ?is deelzaak # van? hebben (d.w.z. deelzaken van deelzaken worden # niet ondersteund). hoofdzaak = models.ForeignKey( "self", limit_choices_to={"hoofdzaak__isnull": True}, null=True, blank=True, on_delete=models.CASCADE, related_name="deelzaken", verbose_name="is deelzaak van", help_text=_("URL-referentie naar de ZAAK, waarom verzocht is door de " "initiator daarvan, die behandeld wordt in twee of meer " "separate ZAAKen waarvan de onderhavige ZAAK er één is."), ) identificatie = models.CharField( max_length=40, blank=True, help_text="De unieke identificatie van de ZAAK binnen de organisatie " "die verantwoordelijk is voor de behandeling van de ZAAK.", validators=[alphanumeric_excluding_diacritic], ) bronorganisatie = RSINField( help_text="Het RSIN van de Niet-natuurlijk persoon zijnde de " "organisatie die de zaak heeft gecreeerd. Dit moet een geldig " "RSIN zijn van 9 nummers en voldoen aan " "https://nl.wikipedia.org/wiki/Burgerservicenummer#11-proef") omschrijving = models.CharField( max_length=80, blank=True, help_text="Een korte omschrijving van de zaak.") toelichting = models.TextField(max_length=1000, blank=True, help_text="Een toelichting op de zaak.") zaaktype = models.ForeignKey( "catalogi.ZaakType", on_delete=models.CASCADE, help_text= "URL-referentie naar het ZAAKTYPE (in de Catalogi API) in de CATALOGUS waar deze voorkomt", ) registratiedatum = models.DateField( help_text="De datum waarop de zaakbehandelende organisatie de ZAAK " "heeft geregistreerd. Indien deze niet opgegeven wordt, " "wordt de datum van vandaag gebruikt.", default=date.today, ) verantwoordelijke_organisatie = RSINField( help_text= "Het RSIN van de Niet-natuurlijk persoon zijnde de organisatie " "die eindverantwoordelijk is voor de behandeling van de " "zaak. Dit moet een geldig RSIN zijn van 9 nummers en voldoen aan " "https://nl.wikipedia.org/wiki/Burgerservicenummer#11-proef") startdatum = models.DateField( help_text="De datum waarop met de uitvoering van de zaak is gestart") einddatum = models.DateField( blank=True, null=True, help_text="De datum waarop de uitvoering van de zaak afgerond is.", ) einddatum_gepland = models.DateField( blank=True, null=True, help_text="De datum waarop volgens de planning verwacht wordt dat de " "zaak afgerond wordt.", ) uiterlijke_einddatum_afdoening = models.DateField( blank=True, null=True, help_text="De laatste datum waarop volgens wet- en regelgeving de zaak " "afgerond dient te zijn.", ) publicatiedatum = models.DateField( _("publicatiedatum"), null=True, blank=True, help_text=_( "Datum waarop (het starten van) de zaak gepubliceerd is of wordt." ), ) producten_of_diensten = ArrayField( models.URLField(_("URL naar product/dienst"), max_length=1000), default=list, help_text=_( "De producten en/of diensten die door de zaak worden voortgebracht. " "Dit zijn URLs naar de resources zoals die door de producten- " "en dienstencatalogus-API wordt ontsloten. " "De producten/diensten moeten bij het zaaktype vermeld zijn."), blank=True, ) communicatiekanaal = models.URLField( _("communicatiekanaal"), blank=True, max_length=1000, help_text= _("Het medium waarlangs de aanleiding om een zaak te starten is ontvangen. " "URL naar een communicatiekanaal in de VNG-Referentielijst van communicatiekanalen." ), ) vertrouwelijkheidaanduiding = VertrouwelijkheidsAanduidingField( _("vertrouwlijkheidaanduiding"), help_text= _("Aanduiding van de mate waarin het zaakdossier van de ZAAK voor de openbaarheid bestemd is." ), ) betalingsindicatie = models.CharField( _("betalingsindicatie"), max_length=20, blank=True, choices=BetalingsIndicatie.choices, help_text=_("Indicatie of de, met behandeling van de zaak gemoeide, " "kosten betaald zijn door de desbetreffende betrokkene."), ) laatste_betaaldatum = models.DateTimeField( _("laatste betaaldatum"), blank=True, null=True, help_text=_( "De datum waarop de meest recente betaling is verwerkt " "van kosten die gemoeid zijn met behandeling van de zaak."), ) zaakgeometrie = GeometryField( blank=True, null=True, help_text="Punt, lijn of (multi-)vlak geometrie-informatie.", ) verlenging_reden = models.CharField( _("reden verlenging"), max_length=200, blank=True, help_text= _("Omschrijving van de reden voor het verlengen van de behandeling van de zaak." ), ) verlenging_duur = DaysDurationField( _("duur verlenging"), blank=True, null=True, help_text=_( "Het aantal werkbare dagen waarmee de doorlooptijd van de " "behandeling van de ZAAK is verlengd (of verkort) ten opzichte " "van de eerder gecommuniceerde doorlooptijd."), ) verlenging = GegevensGroepType({ "reden": verlenging_reden, "duur": verlenging_duur }) opschorting_indicatie = models.BooleanField( _("indicatie opschorting"), default=False, blank=True, help_text=_( "Aanduiding of de behandeling van de ZAAK tijdelijk is opgeschort." ), ) opschorting_reden = models.CharField( _("reden opschorting"), max_length=200, blank=True, help_text= _("Omschrijving van de reden voor het opschorten van de behandeling van de zaak." ), ) opschorting = GegevensGroepType({ "indicatie": opschorting_indicatie, "reden": opschorting_reden }) selectielijstklasse = models.URLField( _("selectielijstklasse"), blank=True, max_length=1000, help_text= _("URL-referentie naar de categorie in de gehanteerde 'Selectielijst Archiefbescheiden' die, gezien " "het zaaktype en het resultaattype van de zaak, bepalend is voor het archiefregime van de zaak." ), ) # Archiving archiefnominatie = models.CharField( _("archiefnominatie"), max_length=40, null=True, blank=True, choices=Archiefnominatie.choices, help_text= _("Aanduiding of het zaakdossier blijvend bewaard of na een bepaalde termijn vernietigd moet worden." ), ) archiefstatus = models.CharField( _("archiefstatus"), max_length=40, choices=Archiefstatus.choices, default=Archiefstatus.nog_te_archiveren, help_text= _("Aanduiding of het zaakdossier blijvend bewaard of na een bepaalde termijn vernietigd moet worden." ), ) archiefactiedatum = models.DateField( _("archiefactiedatum"), null=True, blank=True, help_text= _("De datum waarop het gearchiveerde zaakdossier vernietigd moet worden dan wel overgebracht moet " "worden naar een archiefbewaarplaats. Wordt automatisch berekend bij het aanmaken of wijzigen van " "een RESULTAAT aan deze ZAAK indien nog leeg."), ) objects = ZaakQuerySet.as_manager() class Meta: verbose_name = "zaak" verbose_name_plural = "zaken" unique_together = ("bronorganisatie", "identificatie") def __str__(self): return self.identificatie def save(self, *args, **kwargs): if not self.identificatie: self.identificatie = generate_unique_identification( self, "registratiedatum") if (self.betalingsindicatie == BetalingsIndicatie.nvt and self.laatste_betaaldatum): self.laatste_betaaldatum = None super().save(*args, **kwargs) @property def current_status_uuid(self): status = self.status_set.order_by("-datum_status_gezet").first() return status.uuid if status else None def unique_representation(self): return f"{self.bronorganisatie} - {self.identificatie}"
class InformatieObject(models.Model): identificatie = models.CharField( max_length=40, validators=[alphanumeric_excluding_diacritic], blank=True, default="", help_text="Een binnen een gegeven context ondubbelzinnige referentie " "naar het INFORMATIEOBJECT.", db_index=True, ) bronorganisatie = RSINField( max_length=9, help_text="Het RSIN van de Niet-natuurlijk persoon zijnde de " "organisatie die het informatieobject heeft gecreëerd of " "heeft ontvangen en als eerste in een samenwerkingsketen " "heeft vastgelegd.", db_index=True, ) # TODO: change to read-only? creatiedatum = models.DateField( help_text="Een datum of een gebeurtenis in de levenscyclus van het " "INFORMATIEOBJECT.") titel = models.CharField( max_length=200, help_text="De naam waaronder het INFORMATIEOBJECT formeel bekend is.", ) vertrouwelijkheidaanduiding = VertrouwelijkheidsAanduidingField( blank=True, help_text="Aanduiding van de mate waarin het INFORMATIEOBJECT voor de " "openbaarheid bestemd is.", ) auteur = models.CharField( max_length=200, help_text="De persoon of organisatie die in de eerste plaats " "verantwoordelijk is voor het creëren van de inhoud van het " "INFORMATIEOBJECT.", ) status = models.CharField( _("status"), max_length=20, blank=True, choices=Statussen.choices, help_text= _("Aanduiding van de stand van zaken van een INFORMATIEOBJECT. " "De waarden 'in bewerking' en 'ter vaststelling' komen niet " "voor als het attribuut `ontvangstdatum` van een waarde is voorzien. " "Wijziging van de Status in 'gearchiveerd' impliceert dat " "het informatieobject een duurzaam, niet-wijzigbaar Formaat dient te hebben." ), ) beschrijving = models.TextField( max_length=1000, blank=True, help_text="Een generieke beschrijving van de inhoud van het " "INFORMATIEOBJECT.", ) ontvangstdatum = models.DateField( _("ontvangstdatum"), null=True, blank=True, help_text=_( "De datum waarop het INFORMATIEOBJECT ontvangen is. Verplicht " "te registreren voor INFORMATIEOBJECTen die van buiten de " "zaakbehandelende organisatie(s) ontvangen zijn. " "Ontvangst en verzending is voorbehouden aan documenten die " "van of naar andere personen ontvangen of verzonden zijn " "waarbij die personen niet deel uit maken van de behandeling " "van de zaak waarin het document een rol speelt."), ) verzenddatum = models.DateField( _("verzenddatum"), null=True, blank=True, help_text=_( "De datum waarop het INFORMATIEOBJECT verzonden is, zoals " "deze op het INFORMATIEOBJECT vermeld is. Dit geldt voor zowel " "inkomende als uitgaande INFORMATIEOBJECTen. Eenzelfde " "informatieobject kan niet tegelijk inkomend en uitgaand zijn. " "Ontvangst en verzending is voorbehouden aan documenten die " "van of naar andere personen ontvangen of verzonden zijn " "waarbij die personen niet deel uit maken van de behandeling " "van de zaak waarin het document een rol speelt."), ) indicatie_gebruiksrecht = models.NullBooleanField( _("indicatie gebruiksrecht"), blank=True, default=None, help_text=_( "Indicatie of er beperkingen gelden aangaande het gebruik van " "het informatieobject anders dan raadpleging. Dit veld mag " "`null` zijn om aan te geven dat de indicatie nog niet bekend is. " "Als de indicatie gezet is, dan kan je de gebruiksrechten die " "van toepassing zijn raadplegen via de GEBRUIKSRECHTen resource."), ) # signing in some sort of way # TODO: De attribuutsoort mag niet van een waarde zijn voorzien # als de attribuutsoort ?Status? de waarde ?in bewerking? # of ?ter vaststelling? heeft. ondertekening_soort = models.CharField( _("ondertekeningsoort"), max_length=10, blank=True, choices=OndertekeningSoorten.choices, help_text=_( "Aanduiding van de wijze van ondertekening van het INFORMATIEOBJECT" ), ) ondertekening_datum = models.DateField( _("ondertekeningdatum"), blank=True, null=True, help_text= _("De datum waarop de ondertekening van het INFORMATIEOBJECT heeft plaatsgevonden." ), ) _informatieobjecttype_url = models.URLField( _("extern informatieobjecttype"), blank=True, max_length=1000, help_text= _("URL-referentie naar extern INFORMATIEOBJECTTYPE (in een andere Catalogi API)." ), ) _informatieobjecttype = models.ForeignKey( "catalogi.InformatieObjectType", on_delete=models.CASCADE, help_text=_( "URL-referentie naar het INFORMATIEOBJECTTYPE (in de Catalogi API)." ), null=True, blank=True, ) informatieobjecttype = FkOrURLField( fk_field="_informatieobjecttype", url_field="_informatieobjecttype_url", help_text= "URL-referentie naar het INFORMATIEOBJECTTYPE (in de Catalogi API).", ) objects = InformatieobjectQuerySet.as_manager() IDENTIFICATIE_PREFIX = "DOCUMENT" class Meta: verbose_name = "informatieobject" verbose_name_plural = "informatieobject" unique_together = ("bronorganisatie", "identificatie") abstract = True def __str__(self) -> str: return self.identificatie def save(self, *args, **kwargs): if not self.identificatie: self.identificatie = generate_unique_identification( self, "creatiedatum") super().save(*args, **kwargs) def clean(self): super().clean() validate_status(status=self.status, ontvangstdatum=self.ontvangstdatum, instance=self) ondertekening = GegevensGroepType({ "soort": ondertekening_soort, "datum": ondertekening_datum }) def unique_representation(self): return f"{self.bronorganisatie} - {self.identificatie}"
class Catalogus(models.Model): """ De verzameling van ZAAKTYPEn - incl. daarvoor relevante objecttypen - voor een Domein die als één geheel beheerd wordt. **Toelichting objecttype** Voor de inzet van de CATALOGUS in één uitvoerende organisatie (bijv. een gemeente) gaat KING ervan uit dat binnen de organisatie één CATALOGUS wordt gebruikt met alle ZAAKTYPEn van de organisatie. De unieke identificatie in dit voorbeeld wordt dan de combinatie van het Domein 'Gemeente', gevolgd door het RSIN van de betreffende gemeente. Standaardiserende organisaties zullen mogelijk meerdere catalogi willen publiceren en beheren. Denk aan een ministerie dat voor meerdere sectoren een CATALOGUS aanlegt. Via het Domein-attribuut krijgt zo elke CATALOGUS toch een unieke identificatie. KING bepaalt niet op voorhand welke waarden 'Domein' kan aannemen, maar registreert wel alle gebruikte waarden. """ uuid = models.UUIDField(unique=True, default=uuid.uuid4, help_text="Unieke resource identifier (UUID4)") # TODO [KING]: "Voor de waardenverzameling wordt door KING een waardenlijst beheerd waarin wordt # bijgehouden welke afkorting welk domein betreft." ZTC 2.1, blz 42 - Waar dan? domein = models.CharField( # waardenverzameling hoofdletters _('domein'), max_length=5, validators=[validate_uppercase], help_text=_("Een afkorting waarmee wordt aangegeven voor welk domein " "in een CATALOGUS ZAAKTYPEn zijn uitgewerkt.")) rsin = RSINField( _('rsin'), help_text=_( "Het door een kamer toegekend uniek nummer voor de INGESCHREVEN " "NIET-NATUURLIJK PERSOON die de eigenaar is van een CATALOGUS.")) contactpersoon_beheer_naam = models.CharField( _('naam'), max_length=40, help_text= _("De naam van de contactpersoon die verantwoordelijk is voor het beheer van de CATALOGUS." )) contactpersoon_beheer_telefoonnummer = models.CharField( _('telefoonnummer'), max_length=20, blank=True, help_text=_( "Het telefoonnummer van de contactpersoon die verantwoordelijk " "is voor het beheer van de CATALOGUS.")) # specificatie waardenverzameling conform RFC 5321 en RFC 5322 contactpersoon_beheer_emailadres = models.EmailField( _('emailadres'), max_length=254, blank=True, help_text= _('Het emailadres van de contactpersoon die verantwoordelijk is voor het beheer van de CATALOGUS.' )) class Meta: mnemonic = 'CAT' unique_together = ('domein', 'rsin') verbose_name = _('catalogus') verbose_name_plural = _('catalogussen') ordering = unique_together filter_fields = ( 'domein', 'rsin', ) ordering_fields = filter_fields search_fields = ( 'domein', 'rsin', 'contactpersoon_beheer_naam', ) def __str__(self): return '{} - {}'.format(self.domein, self.rsin)
class Besluit(APIMixin, models.Model): uuid = models.UUIDField(default=_uuid.uuid4) identificatie = models.CharField( "identificatie", max_length=50, blank=True, validators=[alphanumeric_excluding_diacritic], help_text="Identificatie van het besluit binnen de organisatie die " "het besluit heeft vastgesteld. Indien deze niet opgegeven is, " "dan wordt die gegenereerd.", ) verantwoordelijke_organisatie = RSINField( "verantwoordelijke organisatie", help_text="Het RSIN van de niet-natuurlijk persoon zijnde de " "organisatie die het besluit heeft vastgesteld.", ) besluittype = models.ForeignKey( "catalogi.BesluitType", on_delete=models.CASCADE, help_text="URL-referentie naar het BESLUITTYPE (in de Catalogi API).", ) zaak = models.ForeignKey( "zaken.Zaak", on_delete=models.PROTECT, null=True, blank=True, # een besluit kan niet bij een zaak horen help_text= "URL-referentie naar de ZAAK (in de Zaken API) waarvan dit besluit uitkomst is.", ) datum = models.DateField( "datum", validators=[UntilTodayValidator()], help_text="De beslisdatum (AWB) van het besluit.", ) toelichting = models.TextField("toelichting", blank=True, help_text="Toelichting bij het besluit.") # TODO: hoe dit valideren? Beter ook objectregistratie en URL referentie? # Alleen de namen van bestuursorganen mogen gebruikt # worden die voor de desbetrreffende (sic) organisatie van # toepassing zijn. Voor een gemeente zijn dit # 'Burgemeester', 'Gemeenteraad' en 'College van B&W'. # Indien het, bij mandatering, een bestuursorgaan van # een andere organisatie betreft dan de organisatie die # verantwoordelijk is voor de behandeling van de zaak, # dan moet tevens de naam van die andere organisatie # vermeld worden (bijvoorbeeld "Burgemeester gemeente # Lent"). bestuursorgaan = models.CharField( "bestuursorgaan", max_length=50, blank=True, help_text="Een orgaan van een rechtspersoon krachtens publiekrecht " "ingesteld of een persoon of college, met enig openbaar gezag " "bekleed onder wiens verantwoordelijkheid het besluit " "vastgesteld is.", ) ingangsdatum = models.DateField( "ingangsdatum", help_text="Ingangsdatum van de werkingsperiode van het besluit.") vervaldatum = models.DateField( "vervaldatum", null=True, blank=True, help_text="Datum waarop de werkingsperiode van het besluit eindigt.", ) vervalreden = models.CharField( "vervalreden", max_length=30, blank=True, choices=VervalRedenen.choices, help_text= _("De omschrijving die aangeeft op grond waarvan het besluit is of komt te vervallen." ), ) publicatiedatum = models.DateField( "publicatiedatum", null=True, blank=True, help_text="Datum waarop het besluit gepubliceerd wordt.", ) verzenddatum = models.DateField( "verzenddatum", null=True, blank=True, help_text="Datum waarop het besluit verzonden is.", ) # TODO: validator # Afleidbaar gegeven (uit BESLUITTYPE.Reactietermijn en # BESLUIT.Besluitdatum) # .. note: (rekening houdend met weekend- en feestdagen uiterlijke_reactiedatum = models.DateField( "uiterlijke reactiedatum", null=True, blank=True, help_text="De datum tot wanneer verweer tegen het besluit mogelijk is.", ) objects = BesluitQuerySet.as_manager() class Meta: verbose_name = "besluit" verbose_name_plural = "besluiten" unique_together = (("identificatie", "verantwoordelijke_organisatie"), ) def __str__(self): return f"{self.verantwoordelijke_organisatie} - {self.identificatie}" def save(self, *args, **kwargs): if not self.identificatie: self.identificatie = generate_unique_identification(self, "datum") super().save(*args, **kwargs) def unique_representation(self): return f"{self.identificatie}"
class Verzoek(APIMixin, models.Model): """ Verzoek is een speciaal contactmoment. """ uuid = models.UUIDField( unique=True, default=uuid.uuid4, help_text="Unieke resource identifier (UUID4)" ) bronorganisatie = RSINField( help_text="Het RSIN van de Niet-natuurlijk persoon zijnde de " "organisatie die de klantinteractie heeft gecreeerd. Dit moet een " "geldig RSIN zijn van 9 nummers en voldoen aan " "https://nl.wikipedia.org/wiki/Burgerservicenummer#11-proef" ) registratiedatum = models.DateTimeField( default=timezone.now, help_text=_("De datum en het tijdstip waarop het VERZOEK is geregistreerd."), ) tekst = models.TextField( blank=True, help_text=_( "Een toelichting die inhoudelijk het VERZOEK van de KLANT beschrijft." ), ) voorkeurskanaal = models.CharField( max_length=50, blank=True, help_text=_( "Het communicatiekanaal dat voor opvolging van het VERZOEK de voorkeur heeft van de KLANT." ), ) identificatie = models.CharField( max_length=40, blank=True, help_text="De unieke identificatie van het VERZOEK binnen de " "organisatie die verantwoordelijk is voor de behandeling van " "het VERZOEK.", validators=[alphanumeric_excluding_diacritic], ) externe_identificatie = models.CharField( max_length=40, blank=True, help_text="De identificatie van het VERZOEK buiten de eigen organisatie.", validators=[alphanumeric_excluding_diacritic], ) status = models.CharField( max_length=20, choices=VerzoekStatus, help_text="De waarden van de typering van de voortgang van afhandeling van een VERZOEK.", ) in_te_trekken_verzoek = models.OneToOneField( "self", null=True, blank=True, on_delete=models.CASCADE, related_name="intrekkende_verzoek", help_text="URL-referentie naar het (eerdere) VERZOEK dat door dit VERZOEK wordt verzocht ingetrokken te worden.", ) aangevulde_verzoek = models.OneToOneField( "self", null=True, blank=True, on_delete=models.CASCADE, related_name="aanvullende_verzoek", help_text="URL-referentie naar het (eerdere) VERZOEK dat door dit VERZOEK wordt aangevuld.", ) class Meta: unique_together = ("bronorganisatie", "identificatie") verbose_name = "verzoek" verbose_name_plural = "verzoeken" def save(self, *args, **kwargs): if not self.identificatie: self.identificatie = generate_unique_identification( self, "registratiedatum" ) super().save(*args, **kwargs) def unique_representation(self): return f"{self.bronorganisatie} - {self.identificatie}"
class Klant(APIMixin, models.Model): uuid = models.UUIDField(unique=True, default=uuid.uuid4, help_text="Unieke resource identifier (UUID4)") bronorganisatie = RSINField( help_text="Het RSIN van de Niet-natuurlijk persoon zijnde de " "organisatie die de klant heeft gecreeerd. Dit moet een " "geldig RSIN zijn van 9 nummers en voldoen aan " "https://nl.wikipedia.org/wiki/Burgerservicenummer#11-proef") klantnummer = models.CharField( max_length=8, help_text= "De unieke identificatie van de klant binnen de bronorganisatie.", ) bedrijfsnaam = models.CharField(max_length=200, blank=True, help_text="De bedrijfsnaam van de klant.") website_url = models.URLField( "Website URL", max_length=1000, help_text= "Het label of etiket dat aan de specifieke informatiebron, zoals een webpagina, een bestand of een plaatje op internet is toegewezen waar de KLANT in de regel op het internet vindbaar is.", ) voornaam = models.CharField( max_length=200, blank=True, help_text="De voornaam, voorletters of roepnaam van de klant.", ) voorvoegsel_achternaam = models.CharField( max_length=10, blank=True, help_text="Het voorvoegsel van de achternaam van de klant.", ) achternaam = models.CharField(max_length=200, blank=True, help_text="De achternaam van de klant.") functie = models.CharField(max_length=200, blank=True, help_text="De functie van de klant.") telefoonnummer = models.CharField( max_length=20, blank=True, help_text="Het mobiele of vaste telefoonnummer van de klant.", ) emailadres = models.EmailField(blank=True, help_text="Het e-mail adres van de klant.") subject = models.URLField(help_text="URL-referentie naar een subject", max_length=1000, blank=True) subject_type = models.CharField( max_length=100, null=True, blank=True, choices=KlantType.choices, help_text="Type van de `subject`.", ) class Meta: verbose_name = "klant" verbose_name_plural = "klanten" unique_together = ("bronorganisatie", "klantnummer") @property def subject_identificatie(self): if hasattr(self, self.subject_type): return getattr(self, self.subject_type) return None def unique_representation(self): return f"{self.bronorganisatie} - {self.klantnummer}"
class ContactMoment(APIMixin, models.Model): uuid = models.UUIDField(unique=True, default=uuid.uuid4, help_text="Unieke resource identifier (UUID4)") bronorganisatie = RSINField( help_text="Het RSIN van de Niet-natuurlijk persoon zijnde de " "organisatie die de klantinteractie heeft gecreeerd. Dit moet een " "geldig RSIN zijn van 9 nummers en voldoen aan " "https://nl.wikipedia.org/wiki/Burgerservicenummer#11-proef") klant = models.URLField( max_length=1000, blank=True, help_text= _("URL-referentie naar een KLANT (in Klanten API) indien de klantinteractie niet anoniem is." ), ) interactiedatum = models.DateTimeField( default=timezone.now, help_text= _("De datum en het tijdstip waarop de klantinteractie heeft plaatsgevonden." ), ) tekst = models.TextField( blank=True, help_text= _("Een toelichting die inhoudelijk de klantinteractie van de klant beschrijft." ), ) voorkeurskanaal = models.CharField( max_length=50, blank=True, help_text= _("Het communicatiekanaal dat voor opvolging van de klantinteractie de voorkeur heeft van de KLANT." ), ) kanaal = models.CharField( blank=True, max_length=50, help_text=_( "Het communicatiekanaal waarlangs het CONTACTMOMENT gevoerd wordt" ), ) initiatiefnemer = models.CharField( max_length=20, blank=True, choices=InitiatiefNemer.choices, help_text=_("De partij die het contact heeft geïnitieerd."), ) medewerker = models.URLField( help_text="URL-referentie naar een medewerker", max_length=1000, blank=True) onderwerp_links = ArrayField( models.URLField( _("onderwerp link"), max_length=1000, help_text= _("URL naar een product, webpagina of andere entiteit zodat contactmomenten gegroepeerd kunnen worden." ), ), help_text=_( "Eén of meerdere links naar een product, webpagina of andere entiteit " "zodat contactmomenten gegroepeerd kunnen worden op onderwerp."), blank=True, default=list, ) class Meta: verbose_name = "contactmoment" verbose_name_plural = "contactmomenten" def save(self, *args, **kwargs): # workaround for https://github.com/gradam/django-better-admin-arrayfield/issues/17 if self.onderwerp_links is None: self.onderwerp_links = [] super().save(*args, **kwargs) def unique_representation(self): klant_path = self.klant if klant_path.endswith("/"): klant_path = klant_path.rstrip("/") klant_id = klant_path.rsplit("/")[-1] return f"{self.bronorganisatie} {klant_id} at {self.interactiedatum} via {self.kanaal}"
class Zaak(APIMixin, models.Model): """ Modelleer de structuur van een ZAAK. Een samenhangende hoeveelheid werk met een welgedefinieerde aanleiding en een welgedefinieerd eindresultaat, waarvan kwaliteit en doorlooptijd bewaakt moeten worden. """ uuid = models.UUIDField(unique=True, default=uuid.uuid4, help_text="Unieke resource identifier (UUID4)") # Relate 'is_deelzaak_van' # De relatie vanuit een zaak mag niet verwijzen naar # dezelfde zaak d.w.z. moet verwijzen naar een andere # zaak. Die andere zaak mag geen relatie ?is deelzaak # van? hebben (d.w.z. deelzaken van deelzaken worden # niet ondersteund). hoofdzaak = models.ForeignKey( 'self', limit_choices_to={'hoofdzaak__isnull': True}, null=True, blank=True, on_delete=models.CASCADE, related_name='deelzaken', verbose_name='is deelzaak van', help_text=_("De verwijzing naar de ZAAK, waarom verzocht is door de " "initiator daarvan, die behandeld wordt in twee of meer " "separate ZAAKen waarvan de onderhavige ZAAK er één is.")) identificatie = models.CharField( max_length=40, blank=True, help_text='De unieke identificatie van de ZAAK binnen de organisatie ' 'die verantwoordelijk is voor de behandeling van de ZAAK.', validators=[alphanumeric_excluding_diacritic]) bronorganisatie = RSINField( help_text='Het RSIN van de Niet-natuurlijk persoon zijnde de ' 'organisatie die de zaak heeft gecreeerd. Dit moet een geldig ' 'RSIN zijn van 9 nummers en voldoen aan ' 'https://nl.wikipedia.org/wiki/Burgerservicenummer#11-proef') omschrijving = models.CharField( max_length=80, blank=True, help_text='Een korte omschrijving van de zaak.') toelichting = models.TextField(max_length=1000, blank=True, help_text='Een toelichting op de zaak.') zaaktype = models.URLField( _("zaaktype"), help_text="URL naar het zaaktype in de CATALOGUS waar deze voorkomt", max_length=1000) registratiedatum = models.DateField( help_text='De datum waarop de zaakbehandelende organisatie de ZAAK ' 'heeft geregistreerd. Indien deze niet opgegeven wordt, ' 'wordt de datum van vandaag gebruikt.', default=date.today) verantwoordelijke_organisatie = RSINField( help_text= 'Het RSIN van de Niet-natuurlijk persoon zijnde de organisatie ' 'die eindverantwoordelijk is voor de behandeling van de ' 'zaak. Dit moet een geldig RSIN zijn van 9 nummers en voldoen aan ' 'https://nl.wikipedia.org/wiki/Burgerservicenummer#11-proef') startdatum = models.DateField( help_text='De datum waarop met de uitvoering van de zaak is gestart') einddatum = models.DateField( blank=True, null=True, help_text='De datum waarop de uitvoering van de zaak afgerond is.', ) einddatum_gepland = models.DateField( blank=True, null=True, help_text='De datum waarop volgens de planning verwacht wordt dat de ' 'zaak afgerond wordt.', ) uiterlijke_einddatum_afdoening = models.DateField( blank=True, null=True, help_text='De laatste datum waarop volgens wet- en regelgeving de zaak ' 'afgerond dient te zijn.') publicatiedatum = models.DateField( _("publicatiedatum"), null=True, blank=True, help_text=_( "Datum waarop (het starten van) de zaak gepubliceerd is of wordt.") ) producten_of_diensten = ArrayField( models.URLField(_("URL naar product/dienst"), max_length=1000), default=list, help_text=_( "De producten en/of diensten die door de zaak worden voortgebracht. " "Dit zijn URLs naar de resources zoals die door de producten- " "en dienstencatalogus-API wordt ontsloten. " "De producten/diensten moeten bij het zaaktype vermeld zijn.")) communicatiekanaal = models.URLField( _("communicatiekanaal"), blank=True, max_length=1000, help_text= _("Het medium waarlangs de aanleiding om een zaak te starten is ontvangen. " "URL naar een communicatiekanaal in de VNG-Referentielijst van communicatiekanalen." )) vertrouwelijkheidaanduiding = VertrouwelijkheidsAanduidingField( _("vertrouwlijkheidaanduiding"), help_text= _("Aanduiding van de mate waarin het zaakdossier van de ZAAK voor de openbaarheid bestemd is." )) betalingsindicatie = models.CharField( _("betalingsindicatie"), max_length=20, blank=True, choices=BetalingsIndicatie.choices, help_text=_("Indicatie of de, met behandeling van de zaak gemoeide, " "kosten betaald zijn door de desbetreffende betrokkene.")) laatste_betaaldatum = models.DateTimeField( _("laatste betaaldatum"), blank=True, null=True, help_text=_( "De datum waarop de meest recente betaling is verwerkt " "van kosten die gemoeid zijn met behandeling van de zaak.")) zaakgeometrie = GeometryField( blank=True, null=True, help_text="Punt, lijn of (multi-)vlak geometrie-informatie.") verlenging_reden = models.CharField( _("reden verlenging"), max_length=200, blank=True, help_text= _("Omschrijving van de reden voor het verlengen van de behandeling van de zaak." )) verlenging_duur = DaysDurationField( _("duur verlenging"), blank=True, null=True, help_text=_( "Het aantal werkbare dagen waarmee de doorlooptijd van de " "behandeling van de ZAAK is verlengd (of verkort) ten opzichte " "van de eerder gecommuniceerde doorlooptijd.")) verlenging = GegevensGroepType({ 'reden': verlenging_reden, 'duur': verlenging_duur, }) opschorting_indicatie = models.BooleanField( _("indicatie opschorting"), default=False, help_text=_( "Aanduiding of de behandeling van de ZAAK tijdelijk is opgeschort." )) opschorting_reden = models.CharField( _("reden opschorting"), max_length=200, blank=True, help_text= _("Omschrijving van de reden voor het opschorten van de behandeling van de zaak." )) opschorting = GegevensGroepType({ 'indicatie': opschorting_indicatie, 'reden': opschorting_reden, }) selectielijstklasse = models.URLField( _("selectielijstklasse"), blank=True, max_length=1000, help_text= _("URL-referentie naar de categorie in de gehanteerde 'Selectielijst Archiefbescheiden' die, gezien " "het zaaktype en het resultaattype van de zaak, bepalend is voor het archiefregime van de zaak." )) relevante_andere_zaken = ArrayField(models.URLField( _("URL naar andere zaak"), max_length=1000), blank=True, default=list) # Archiving archiefnominatie = models.CharField( _("archiefnominatie"), max_length=40, null=True, blank=True, choices=Archiefnominatie.choices, help_text= _("Aanduiding of het zaakdossier blijvend bewaard of na een bepaalde termijn vernietigd moet worden." )) archiefstatus = models.CharField( _("archiefstatus"), max_length=40, choices=Archiefstatus.choices, default=Archiefstatus.nog_te_archiveren, help_text= _("Aanduiding of het zaakdossier blijvend bewaard of na een bepaalde termijn vernietigd moet worden." )) archiefactiedatum = models.DateField( _("archiefactiedatum"), null=True, blank=True, help_text= _("De datum waarop het gearchiveerde zaakdossier vernietigd moet worden dan wel overgebracht moet " "worden naar een archiefbewaarplaats. Wordt automatisch berekend bij het aanmaken of wijzigen van " "een RESULTAAT aan deze ZAAK indien nog leeg.")) class Meta: verbose_name = 'zaak' verbose_name_plural = 'zaken' unique_together = ('bronorganisatie', 'identificatie') def __str__(self): return self.identificatie def save(self, *args, **kwargs): if not self.identificatie: self.identificatie = str(uuid.uuid4()) if self.betalingsindicatie == BetalingsIndicatie.nvt and self.laatste_betaaldatum: self.laatste_betaaldatum = None super().save(*args, **kwargs) @property def current_status_uuid(self): status = self.status_set.order_by('-datum_status_gezet').first() return status.uuid if status else None def get_brondatum(self, afleidingswijze: str, datum_kenmerk: str = None, objecttype: str = None, procestermijn: str = None) -> date: """ To calculate the Archiefactiedatum, we first need the "brondatum" which is like the start date of the storage period. :param afleidingswijze: One of the `Afleidingswijze` choices. :param datum_kenmerk: A `string` representing an arbitrary attribute name. Currently only needed when `afleidingswijze` is `eigenschap` or `zaakobject`. :param objecttype: A `string` representing an arbitrary objecttype name. Currently only needed when `afleidingswijze` is `zaakobject`. :param procestermijn: A `string` representing an ISO8601 period that is considered the process term of the Zaak. Currently only needed when `afleidingswijze` is `termijn`. :return: A specific date that marks the start of the storage period, or `None`. """ if afleidingswijze == BrondatumArchiefprocedureAfleidingswijze.afgehandeld: return self.einddatum elif afleidingswijze == BrondatumArchiefprocedureAfleidingswijze.hoofdzaak: # TODO: Document that hoofdzaak can not an external zaak return self.hoofdzaak.einddatum if self.hoofdzaak else None elif afleidingswijze == BrondatumArchiefprocedureAfleidingswijze.eigenschap: if not datum_kenmerk: raise DetermineProcessEndDateException( _('Geen datumkenmerk aanwezig om de eigenschap te achterhalen voor het bepalen van de brondatum.' )) eigenschap = self.zaakeigenschap_set.filter( _naam=datum_kenmerk).first() if eigenschap: if not eigenschap.waarde: return None try: return parse_isodatetime(eigenschap.waarde).date() except ValueError: raise DetermineProcessEndDateException( _('Geen geldige datumwaarde in eigenschap "{}": {}'). format(datum_kenmerk, eigenschap.waarde)) else: raise DetermineProcessEndDateException( _('Geen eigenschap gevonden die overeenkomt met het datumkenmerk "{}" voor het bepalen van de ' 'brondatum.').format(datum_kenmerk)) elif afleidingswijze == BrondatumArchiefprocedureAfleidingswijze.ander_datumkenmerk: # The brondatum, and therefore the archiefactiedatum, needs to be determined manually. return None elif afleidingswijze == BrondatumArchiefprocedureAfleidingswijze.zaakobject: if not objecttype: raise DetermineProcessEndDateException( _('Geen objecttype aanwezig om het zaakobject te achterhalen voor het bepalen van de brondatum.' )) if not datum_kenmerk: raise DetermineProcessEndDateException( _('Geen datumkenmerk aanwezig om het attribuut van het zaakobject te achterhalen voor het bepalen ' 'van de brondatum.')) for zaak_object in self.zaakobject_set.filter( object_type=objecttype): object = zaak_object._get_object() if datum_kenmerk in object: try: return parse_isodatetime(object[datum_kenmerk]).date() except ValueError: raise DetermineProcessEndDateException( _('Geen geldige datumwaarde in attribuut "{}": {}' ).format(datum_kenmerk, object[datum_kenmerk])) raise DetermineProcessEndDateException( _('Geen attribuut gevonden die overeenkomt met het datumkenmerk "{}" voor het bepalen van de ' 'brondatum.').format(datum_kenmerk)) elif afleidingswijze == BrondatumArchiefprocedureAfleidingswijze.termijn: if self.einddatum is None: # TODO: Not sure if we should raise an error instead. return None if procestermijn is None: raise DetermineProcessEndDateException( _('Geen procestermijn aanwezig voor het bepalen van de brondatum.' )) try: return self.einddatum + isodate.parse_duration(procestermijn) except (ValueError, TypeError) as e: raise DetermineProcessEndDateException( _('Geen geldige periode in procestermijn: {}').format( procestermijn)) elif afleidingswijze == BrondatumArchiefprocedureAfleidingswijze.gerelateerde_zaak: # TODO: Determine what this means... raise NotImplementedError elif afleidingswijze == BrondatumArchiefprocedureAfleidingswijze.ingangsdatum_besluit: # TODO: Relation from Zaak to Besluit is not implemented yet... raise NotImplementedError elif afleidingswijze == BrondatumArchiefprocedureAfleidingswijze.vervaldatum_besluit: # TODO: Relation from Zaak to Besluit is not implemented yet... raise NotImplementedError raise ValueError(f'Onbekende "Afleidingswijze": {afleidingswijze}')