示例#1
0
class Zaak(AuditTrailMixin, 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],
        db_index=True,
    )
    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_url = models.URLField(
        _("extern zaaktype"),
        blank=True,
        max_length=1000,
        help_text=_(
            "URL-referentie naar extern ZAAKTYPE (in een andere Catalogi API)."
        ),
    )
    _zaaktype = models.ForeignKey(
        "catalogi.ZaakType",
        on_delete=models.CASCADE,
        help_text="URL-referentie naar het ZAAKTYPE (in de Catalogi API).",
        null=True,
        blank=True,
    )
    zaaktype = FkOrURLField(
        fk_field="_zaaktype",
        url_field="_zaaktype_url",
        help_text="URL-referentie naar het ZAAKTYPE (in de Catalogi API).",
    )

    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",
        db_index=True,
    )
    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 = DurationField(
        _("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."
          ),
        db_index=True,
    )
    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."
          ),
        db_index=True,
    )
    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."),
        db_index=True,
    )

    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.first()
        return status.uuid if status else None

    @property
    def is_closed(self) -> bool:
        return self.einddatum is not None

    def unique_representation(self):
        return f"{self.bronorganisatie} - {self.identificatie}"
示例#2
0
class ZaakType(APIMixin, ConceptMixin, GeldigheidMixin, models.Model):
    """
    Het geheel van karakteristieke eigenschappen van zaken van eenzelfde soort

    Toelichting objecttype
    Het betreft de indeling of groepering van zaken naar hun aard, zoals “Behandelen aanvraag
    bouwvergunning” en “Behandelen aanvraag ontheffing parkeren”. Wat in een individueel geval
    een zaak is, waar die begint en waar die eindigt, wordt bekeken vanuit het perspectief van de
    initiator van de zaak (burger, bedrijf, medewerker, etc.). Het traject van (aan)vraag cq.
    aanleiding voor de zaak tot en met de levering van de producten/of diensten die een passend
    antwoord vormen op die aanleiding, bepaalt de omvang en afbakening van de zaak en
    daarmee van het zaaktype. Hiermee komt de afbakening van een zaaktype overeen met een
    bedrijfsproces: ‘van klant tot klant’. Dit betekent ondermeer dat onderdelen van
    bedrijfsprocessen geen zelfstandige zaken vormen. Het betekent ook dat een aanleiding die
    niet leidt tot de start van de uitvoering van een bedrijfsproces, niet leidt tot een zaak (deze
    wordt behandeld in het kader van een reeds lopende zaak).
    Zie ook de toelichtingen bij de relatiesoorten ‘ZAAKTYPE is deelzaaktype van ZAAKTYPE’ en
    ‘ZAAKTYPE heeft gerelateerd ZAAKTYPE’ voor wat betreft zaaktypen van deelzaken
    respectievelijk gerelateerde zaken.
    """

    uuid = models.UUIDField(unique=True,
                            default=uuid.uuid4,
                            help_text="Unieke resource identifier (UUID4)")
    identificatie = models.CharField(
        _("identificatie"),
        max_length=50,
        blank=True,
        help_text=
        _("Unieke identificatie van het ZAAKTYPE binnen de CATALOGUS waarin het ZAAKTYPE voorkomt."
          ),
        validators=[alphanumeric_excluding_diacritic],
        db_index=True,
    )
    zaaktype_omschrijving = models.CharField(
        _("omschrijving"),
        max_length=80,
        help_text=_("Omschrijving van de aard van ZAAKen van het ZAAKTYPE."),
    )
    # TODO [KING]: waardenverzameling zoals vastgelegt in CATALOGUS, wat is deze waardeverzameling dan?
    zaaktype_omschrijving_generiek = models.CharField(
        _("omschrijving generiek"),
        max_length=80,
        blank=True,
        help_text=
        _("Algemeen gehanteerde omschrijving van de aard van ZAAKen van het ZAAKTYPE"
          ),
    )
    vertrouwelijkheidaanduiding = VertrouwelijkheidsAanduidingField(
        _("vertrouwelijkheidaanduiding"),
        help_text=
        _("Aanduiding van de mate waarin zaakdossiers van ZAAKen van "
          "dit ZAAKTYPE voor de openbaarheid bestemd zijn. Indien de zaak bij het "
          "aanmaken geen vertrouwelijkheidaanduiding krijgt, dan wordt deze waarde gezet."
          ),
    )
    doel = models.TextField(
        _("doel"),
        help_text=
        _("Een omschrijving van hetgeen beoogd is te bereiken met een zaak van dit zaaktype."
          ),
    )
    aanleiding = models.TextField(
        _("aanleiding"),
        help_text=_("Een omschrijving van de gebeurtenis die leidt tot het "
                    "starten van een ZAAK van dit ZAAKTYPE."),
    )
    toelichting = models.TextField(
        _("toelichting"),
        blank=True,
        help_text=_(
            "Een eventuele toelichting op dit zaaktype, zoals een beschrijving "
            "van het procesverloop op de hoofdlijnen."),
    )
    indicatie_intern_of_extern = models.CharField(
        _("indicatie intern of extern"),
        max_length=6,
        choices=InternExtern.choices,
        help_text=_(
            "Een aanduiding waarmee onderscheid wordt gemaakt tussen "
            "ZAAKTYPEn die Intern respectievelijk Extern geïnitieerd worden. "
            "Indien van beide sprake kan zijn, dan prevaleert de externe initiatie."
        ),
    )
    handeling_initiator = models.CharField(
        _("handeling initiator"),
        max_length=20,
        help_text=
        _("Werkwoord dat hoort bij de handeling die de initiator verricht bij dit zaaktype. "
          "Meestal 'aanvragen', 'indienen' of 'melden'. Zie ook het IOB model op "
          "https://www.gemmaonline.nl/index.php/Imztc_2.1/doc/attribuutsoort/zaaktype.handeling_initiator"
          ),
    )
    onderwerp = models.CharField(
        _("onderwerp"),
        max_length=80,
        help_text=
        _("Het onderwerp van ZAAKen van dit ZAAKTYPE. In veel gevallen nauw gerelateerd aan de product- of "
          "dienstnaam uit de Producten- en Dienstencatalogus (PDC). Bijvoorbeeld: 'Evenementenvergunning', "
          "'Geboorte', 'Klacht'. Zie ook het IOB model op "
          "https://www.gemmaonline.nl/index.php/Imztc_2.1/doc/attribuutsoort/zaaktype.onderwerp"
          ),
    )
    handeling_behandelaar = models.CharField(
        _("handeling behandelaar"),
        max_length=20,
        help_text=
        _("Werkwoord dat hoort bij de handeling die de behandelaar verricht bij het afdoen van ZAAKen van "
          "dit ZAAKTYPE. Meestal 'behandelen', 'uitvoeren', 'vaststellen' of 'onderhouden'. "
          "Zie ook het IOB model op "
          "https://www.gemmaonline.nl/index.php/Imztc_2.1/doc/attribuutsoort/zaaktype.handeling_behandelaar"
          ),
    )
    doorlooptijd_behandeling = DurationField(
        _("doorlooptijd behandeling"),
        help_text=_(
            "De periode waarbinnen volgens wet- en regelgeving een ZAAK van het ZAAKTYPE "
            "afgerond dient te zijn, in kalenderdagen."),
    )
    servicenorm_behandeling = DurationField(
        _("servicenorm behandeling"),
        blank=True,
        null=True,
        help_text=
        _("De periode waarbinnen verwacht wordt dat een ZAAK van het ZAAKTYPE afgerond wordt conform "
          "de geldende servicenormen van de zaakbehandelende organisatie(s)."),
    )
    opschorting_en_aanhouding_mogelijk = models.BooleanField(
        _("opschorting/aanhouding mogelijk"),
        help_text=_(
            "Aanduiding die aangeeft of ZAAKen van dit mogelijk ZAAKTYPE "
            "kunnen worden opgeschort en/of aangehouden."),
    )
    verlenging_mogelijk = models.BooleanField(
        _("verlenging mogelijk"),
        help_text=_(
            "Aanduiding die aangeeft of de Doorlooptijd behandeling van "
            "ZAAKen van dit ZAAKTYPE kan worden verlengd."),
    )
    verlengingstermijn = DurationField(
        _("verlengingstermijn"),
        blank=True,
        null=True,
        help_text=_(
            "De termijn (typisch een aantal dagen) waarmee de Doorlooptijd "
            "behandeling van ZAAKen van dit ZAAKTYPE kan worden verlengd. Mag "
            "alleen een waarde bevatten als verlenging mogelijk is."),
    )

    trefwoorden = ArrayField(
        models.CharField(_("trefwoord"), max_length=30),
        blank=True,
        default=list,
        help_text=
        _("Een trefwoord waarmee ZAAKen van het ZAAKTYPE kunnen worden gekarakteriseerd."
          ),
        db_index=True,
    )
    publicatie_indicatie = models.BooleanField(
        _("publicatie indicatie"),
        help_text=
        _("Aanduiding of (het starten van) een ZAAK dit ZAAKTYPE gepubliceerd moet worden."
          ),
    )
    publicatietekst = models.TextField(
        _("publicatietekst"),
        blank=True,
        help_text=_(
            "De generieke tekst van de publicatie van ZAAKen van dit ZAAKTYPE."
        ),
    )
    verantwoordingsrelatie = ArrayField(
        models.CharField(_("verantwoordingsrelatie"), max_length=40),
        blank=True,
        default=list,
        help_text=
        _("De relatie tussen ZAAKen van dit ZAAKTYPE en de beleidsmatige en/of financiële verantwoording."
          ),
    )
    versiedatum = models.DateField(
        _("versiedatum"),
        help_text=
        _("De datum waarop de (gewijzigde) kenmerken van het ZAAKTYPE geldig zijn geworden"
          ),
    )

    #
    # groepsattribuutsoorten
    #
    # TODO: should have shape validator, because the API resources need to conform
    producten_of_diensten = ArrayField(
        models.URLField(_("URL naar product/dienst"), max_length=1000),
        help_text=
        _("Het product of de dienst die door ZAAKen van dit ZAAKTYPE wordt voortgebracht."
          ),
        blank=True,
        default=list,
    )

    # TODO: validate shape & populate?
    selectielijst_procestype = models.URLField(
        _("selectielijst procestype"),
        blank=True,
        help_text=_(
            "URL-referentie naar een vanuit archiveringsoptiek onderkende groep processen met dezelfde "
            "kenmerken (PROCESTYPE in de Selectielijst API)."),
    )
    selectielijst_procestype_jaar = models.PositiveIntegerField(
        help_text=_("Het jaartal waartoe het procestype behoort."),
        blank=True,
        null=True,
    )
    referentieproces_naam = models.CharField(
        _("referentieprocesnaam"),
        max_length=80,
        help_text=_("De naam van het Referentieproces."),
    )
    referentieproces_link = models.URLField(
        _("referentieproceslink"),
        blank=True,
        help_text=_("De URL naar de beschrijving van het Referentieproces"),
    )
    referentieproces = GegevensGroepType(
        {
            "naam": referentieproces_naam,
            "link": referentieproces_link
        },
        optional=("link", ),
    )

    #
    # relaties
    #
    deelzaaktypen = models.ManyToManyField(
        "self",
        symmetrical=False,
        blank=True,
        related_name="hoofdzaaktypen",
        help_text=_(
            "De ZAAKTYPE(n) waaronder ZAAKen als deelzaak kunnen voorkomen bij "
            "ZAAKen van dit ZAAKTYPE."),
    )
    catalogus = models.ForeignKey(
        "catalogi.Catalogus",
        # verbose_name=_("maakt deel uit van"),
        on_delete=models.CASCADE,
        help_text=_(
            "URL-referentie naar de CATALOGUS waartoe dit ZAAKTYPE behoort."),
    )

    objects = SyncAutorisatieManager()

    IDENTIFICATIE_PREFIX = "ZAAKTYPE"

    class Meta:
        verbose_name = _("Zaaktype")
        verbose_name_plural = _("Zaaktypen")

    def __str__(self):
        return "{} ({})".format(
            self.zaaktype_omschrijving,
            "CONCEPT" if self.concept else self.versiedatum)

    @transaction.atomic
    def save(self, *args, **kwargs):
        # sync after creating new objects
        if not self.pk:
            transaction.on_commit(AutorisatieSpec.sync)

        if not self.identificatie:
            self.identificatie = generate_unique_identification(
                self, "versiedatum")

        if not self.verlenging_mogelijk:
            self.verlengingstermijn = None
        elif not self.verlengingstermijn:
            raise ValueError(
                "'verlengingstermijn' must be set if 'verlenging_mogelijk' is set."
            )

        super().save(*args, **kwargs)

    def clean(self):
        from ..utils import compare_relativedeltas, get_overlapping_zaaktypes

        super().clean()

        if self.verlenging_mogelijk and not self.verlengingstermijn:
            raise ValidationError(
                "'verlengingstermijn' moet ingevuld zijn als 'verlenging_mogelijk' gezet is."
            )

        # self.doorlooptijd_behandeling is empty if there are validation errors,
        # which would trigger a TypeError on the comparison
        if (self.doorlooptijd_behandeling
                and self.servicenorm_behandeling  # noqa
                and compare_relativedeltas(
                    self.servicenorm_behandeling,
                    self.doorlooptijd_behandeling)):  # noqa
            raise ValidationError(
                "'Servicenorm behandeling' periode mag niet langer zijn dan "
                "de periode van 'Doorlooptijd behandeling'.")

        if self.catalogus_id and self.datum_begin_geldigheid:
            query = get_overlapping_zaaktypes(
                self.catalogus,
                self.zaaktype_omschrijving,
                self.datum_begin_geldigheid,
                self.datum_einde_geldigheid,
                self,
            )

            # regel voor zaaktype omschrijving
            if query.exists():
                raise ValidationError(
                    "Zaaktype versies (dezelfde omschrijving) mogen geen "
                    "overlappende geldigheid hebben.")

    def get_absolute_api_url(self, request=None, **kwargs) -> str:
        kwargs["version"] = "1"
        return super().get_absolute_api_url(request=request, **kwargs)
示例#3
0
class BesluitType(APIMixin, GeldigheidMixin, ConceptMixin, models.Model):
    """
    Generieke aanduiding van de aard van een besluit.

    **Populatie**
    Alle besluittypen van de besluiten die het resultaat kunnen zijn van het
    zaakgericht werken van de behandelende organisatie(s).

    **Toelichting objecttype**
    Het betreft de indeling of groepering van besluiten naar hun aard, zoals
    bouwvergunning, ontheffing geluidhinder en monumentensubsidie.
    """

    uuid = models.UUIDField(
        unique=True, default=_uuid.uuid4, help_text="Unieke resource identifier (UUID4)"
    )

    omschrijving = models.CharField(
        _("omschrijving"),
        max_length=80,
        blank=True,
        help_text=_("Omschrijving van de aard van BESLUITen van het BESLUITTYPE."),
    )

    # TODO [KING]: wat is de waardenverzameling?
    omschrijving_generiek = models.CharField(
        _("omschrijving generiek"),
        max_length=80,
        blank=True,
        help_text=_(
            "Algemeen gehanteerde omschrijving van de aard van BESLUITen van het BESLUITTYPE"
        ),
    )

    # TODO [KING]: waardenverzameling gebaseerd op de AWB, wat betekend dat?
    besluitcategorie = models.CharField(
        _("besluitcategorie"),
        max_length=40,
        blank=True,
        help_text=_("Typering van de aard van BESLUITen van het BESLUITTYPE."),
    )

    reactietermijn = DurationField(
        _("reactietermijn"),
        blank=True,
        null=True,
        help_text=_(
            "De duur (typisch een aantal dagen), gerekend vanaf de verzend- of publicatiedatum, "
            "waarbinnen verweer tegen een besluit van het besluittype mogelijk is."
        ),
    )

    publicatie_indicatie = models.BooleanField(
        _("publicatie indicatie"),
        null=False,
        help_text=_(
            "Aanduiding of BESLUITen van dit BESLUITTYPE gepubliceerd moeten worden."
        ),
    )

    publicatietekst = models.TextField(
        _("publicatietekst"),
        blank=True,
        help_text=_(
            "De generieke tekst van de publicatie van BESLUITen van dit BESLUITTYPE"
        ),
    )

    publicatietermijn = DurationField(
        _("publicatietermijn"),
        blank=True,
        null=True,
        help_text=_(
            "De duur (typisch een aantal dagen), gerekend vanaf de verzend- of publicatiedatum, "
            "dat BESLUITen van dit BESLUITTYPE gepubliceerd moeten blijven."
        ),
    )

    toelichting = models.TextField(
        _("toelichting"),
        blank=True,
        help_text=_("Een eventuele toelichting op dit BESLUITTYPE."),
    )

    catalogus = models.ForeignKey(
        "catalogi.Catalogus",
        on_delete=models.CASCADE,
        # verbose_name=_("catalogus"),
        help_text=_(
            "URL-referentie naar de CATALOGUS waartoe dit BESLUITTYPE behoort."
        ),
    )

    informatieobjecttypen = models.ManyToManyField(
        "catalogi.InformatieObjectType",
        blank=True,
        # verbose_name=_("informatieobjecttype"),
        related_name="besluittypen",
        help_text=_(
            "URL-referenties naar het INFORMATIEOBJECTTYPE van informatieobjecten waarin besluiten van dit "
            "BESLUITTYPE worden vastgelegd."
        ),
    )

    zaaktypen = models.ManyToManyField(
        "catalogi.ZaakType",
        # verbose_name=_("zaaktypen"),
        related_name="besluittypen",
        help_text=_(
            "ZAAKTYPE met ZAAKen die relevant kunnen zijn voor dit BESLUITTYPE"
        ),
    )

    objects = SyncAutorisatieManager()

    class Meta:
        verbose_name = _("besluittype")
        verbose_name_plural = _("besluittypen")
        unique_together = ("catalogus", "omschrijving")

    def __str__(self):
        representation = f"{self.catalogus} - {self.omschrijving}"
        if self.concept:
            representation = "{} (CONCEPT)".format(representation)
        return representation

    @transaction.atomic
    def save(self, *args, **kwargs):
        if not self.pk:
            transaction.on_commit(AutorisatieSpec.sync)
        super().save(*args, **kwargs)

    def get_absolute_api_url(self, request=None, **kwargs) -> str:
        kwargs["version"] = "1"
        return super().get_absolute_api_url(request=request, **kwargs)
示例#4
0
class ResultaatType(models.Model):
    """
    Het betreft de indeling of groepering van resultaten van zaken van hetzelfde
    ZAAKTYPE naar hun aard, zoals 'verleend', 'geweigerd', 'verwerkt', et cetera.

    Toelichting objecttype
    Elke zaak heeft een resultaat. In een aantal gevallen valt dit resultaat samen met een besluit:
    ‘Evenementenvergunning verleend’, ‘Energiesubsidie geweigerd’, et cetera. Het komt echter
    ook voor dat zaken worden afgehandeld zonder dat er een besluit wordt genomen. Dit is
    bijvoorbeeld het geval bij aangiften (geboorte, verhuizing), meldingen (openbare ruimte), maar
    ook bij het intrekken van een aanvraag. Het resultaat van een zaak is van groot belang voor de
    archivering: het resultaattype bepaalt mede of de zaak en het bijbehorende dossier moeten
    worden vernietigd (na enige termijn) of blijvend bewaard moeten worden (en na enige termijn
    ‘overgebracht’ worden naar een archiefbewaarplaats). Met RESULTAATTYPE worden de
    mogelijke resultaten benoemd bij het desbetreffende zaaktype. Daarmee is het archiefregime
    bepaald voor het gehele zaakdossier: alle informatie over en documenten bij de zaken van het
    ZAAKTYPE.
    In uitzonderingsgevallen kan er sprake van zijn dat documenten van een bepaald
    INFORMATIEOBJECTTYPE in zaakdossiers bij zaken van het ZAAKTYPE een afwijkend
    archiefregime hebben ten opzichte van het zaakdossier. Privacy-gevoeligheid kan er reden
    voor zijn om documenten van een ZAAKTYPEINFORMATIEOBJECTTYPE eerder te vernietigen dan het
    zaakdossier als geheel. Specifieke wetgeving, zoals die voor de BAG, leidt er daarentegen toe
    dat een Omgevingsvergunning (activiteit bouwen) ten eeuwige dage bewaard moet blijven
    terwijl het zaakdossier na 20 jaar vernietigd dient te worden. De relatiesoort ‘RESULTAATTYPE
    bepaalt afwijkend archiefregime van ZAAK-INFORMATIEOBJECT-TYPE’ geeft de mogelijkheid
    deze uitzonderingsgevallen te documenteren.
    """

    uuid = models.UUIDField(unique=True,
                            default=uuid.uuid4,
                            help_text="Unieke resource identifier (UUID4)")
    zaaktype = models.ForeignKey(
        "catalogi.ZaakType",
        # verbose_name=_("is relevant voor"),
        on_delete=models.CASCADE,
        related_name="resultaattypen",
        help_text=_(
            "URL-referentie naar het ZAAKTYPE van ZAAKen waarin resultaten van "
            "dit RESULTAATTYPE bereikt kunnen worden."),
    )

    # core data - used by ZRC to calculate archival-related dates
    omschrijving = models.CharField(
        _("omschrijving"),
        max_length=20,
        help_text=_(
            "Omschrijving van de aard van resultaten van het RESULTAATTYPE."),
    )
    resultaattypeomschrijving = models.URLField(
        _("resultaattypeomschrijving"),
        max_length=1000,
        help_text=
        _("Algemeen gehanteerde omschrijving van de aard van resultaten van het RESULTAATTYPE. "
          "Dit moet een URL-referentie zijn naar de referenlijst van generieke "
          "resultaattypeomschrijvingen. Im ImZTC heet dit 'omschrijving generiek'"
          ),
    )
    omschrijving_generiek = models.CharField(
        _("omschrijving generiek"),
        max_length=20,
        blank=True,
        editable=False,
        help_text=
        _("Gecachete tekstuele waarde van de generieke resultaattypeomschrijving."
          ),
    )

    # TODO: validate that this matches the Zaaktype.procestype
    selectielijstklasse = models.URLField(
        _("selectielijstklasse"),
        max_length=1000,
        help_text=
        _("URL-referentie naar de, voor het archiefregime bij het RESULTAATTYPE relevante, "
          "categorie in de Selectielijst Archiefbescheiden (RESULTAAT in de Selectielijst API) "
          "van de voor het ZAAKTYPE verantwoordelijke overheidsorganisatie."),
    )

    # derived fields from selectielijstklasse
    archiefnominatie = models.CharField(
        _("archiefnominatie"),
        default="",
        choices=Archiefnominatie.choices,
        max_length=20,
        blank=True,
        help_text=_(
            "Aanduiding die aangeeft of ZAAKen met een resultaat van "
            "dit RESULTAATTYPE blijvend moeten worden bewaard of "
            "(op termijn) moeten worden vernietigd. Indien niet expliciet "
            "opgegeven wordt dit gevuld vanuit de selectielijst."),
    )

    archiefactietermijn = DurationField(
        _("archiefactietermijn"),
        null=True,
        blank=True,
        help_text=_(
            "De termijn, na het vervallen van het bedrjfsvoeringsbelang, "
            "waarna het zaakdossier (de ZAAK met alle bijbehorende "
            "INFORMATIEOBJECTen) van een ZAAK met een resultaat van dit "
            "RESULTAATTYPE vernietigd of overgebracht (naar een "
            "archiefbewaarplaats) moet worden. Voor te vernietigen "
            "dossiers betreft het de in die Selectielijst genoemde "
            "bewaartermjn. Voor blijvend te bewaren zaakdossiers "
            "betreft het de termijn vanaf afronding van de zaak tot "
            "overbrenging (de procestermijn is dan nihil)."),
    )

    # TODO: validate dependencies between fields
    brondatum_archiefprocedure_afleidingswijze = models.CharField(
        _("afleidingswijze brondatum"),
        max_length=20,
        choices=Afleidingswijze.choices,
        help_text=_("Wijze van bepalen van de brondatum."),
    )
    # TODO: this could/should be validated against a remote OAS 3.0!
    brondatum_archiefprocedure_datumkenmerk = models.CharField(
        _("datumkenmerk"),
        max_length=80,
        blank=True,
        help_text=_("Naam van de attribuutsoort van het procesobject dat "
                    "bepalend is voor het einde van de procestermijn."),
    )
    brondatum_archiefprocedure_einddatum_bekend = models.BooleanField(
        _("einddatum bekend"),
        default=False,
        help_text=_(
            "Indicatie dat de einddatum van het procesobject gedurende "
            "de uitvoering van de zaak bekend moet worden. Indien deze "
            "nog niet bekend is en deze waarde staat op `true`, dan "
            "kan de zaak (nog) niet afgesloten worden."),
    )
    brondatum_archiefprocedure_objecttype = models.CharField(
        _("objecttype"),
        max_length=80,
        blank=True,
        choices=ZaakobjectTypes.choices,
        help_text=
        _("Het soort object in de registratie dat het procesobject representeert."
          ),
    )
    # TODO: standardize content so that consumers understand this?
    brondatum_archiefprocedure_registratie = models.CharField(
        _("registratie"),
        max_length=80,
        blank=True,
        help_text=
        _("De naam van de registratie waarvan het procesobject deel uit maakt."
          ),
    )
    brondatum_archiefprocedure_procestermijn = DurationField(
        _("procestermijn"),
        null=True,
        blank=True,
        help_text=_(
            "De periode dat het zaakdossier na afronding van de zaak "
            "actief gebruikt en/of geraadpleegd wordt ter ondersteuning "
            "van de taakuitoefening van de organisatie. Enkel relevant "
            "indien de afleidingswijze 'termijn' is."),
    )

    brondatum_archiefprocedure = GegevensGroepType(
        {
            "afleidingswijze": brondatum_archiefprocedure_afleidingswijze,
            "datumkenmerk": brondatum_archiefprocedure_datumkenmerk,
            "einddatum_bekend": brondatum_archiefprocedure_einddatum_bekend,
            "objecttype": brondatum_archiefprocedure_objecttype,
            "registratie": brondatum_archiefprocedure_registratie,
            "procestermijn": brondatum_archiefprocedure_procestermijn,
        },
        optional=(
            "datumkenmerk",
            "einddatum_bekend",
            "objecttype",
            "registratie",
            "procestermijn",
        ),
        required=False,
        none_for_empty=False,
    )

    # meta-information - this is mostly informative
    toelichting = models.TextField(
        _("toelichting"),
        blank=True,
        help_text=_(
            "Een toelichting op dit RESULTAATTYPE en het belang hiervan "
            "voor ZAAKen waarin een resultaat van dit RESULTAATTYPE wordt geselecteerd."
        ),
    )

    class Meta:
        unique_together = ("zaaktype", "omschrijving")
        verbose_name = _("resultaattype")
        verbose_name_plural = _("resultaattypen")

    def save(self, *args, **kwargs):
        """
        Save some derived fields into local object as a means of caching.
        """
        if (self.resultaattypeomschrijving
                and self.resultaattypeomschrijving is not no_fetch):
            response = requests.get(self.resultaattypeomschrijving).json()
            self.omschrijving_generiek = response["omschrijving"]

        # derive the default archiefnominatie
        if not self.archiefnominatie and self.selectielijstklasse:
            selectielijstklasse = self.get_selectielijstklasse()
            self.archiefnominatie = selectielijstklasse["waardering"]

        if not self.archiefactietermijn and self.selectielijstklasse:
            selectielijstklasse = self.get_selectielijstklasse()
            parsed_relativedelta = (
                parse_relativedelta(selectielijstklasse["bewaartermijn"])
                if selectielijstklasse["bewaartermijn"] else None)
            self.archiefactietermijn = parsed_relativedelta

        # Met de attribuutsoorten van dit groepatribuutsoort worden deze situaties
        # geparametrieerd. Dit is alleen relevant indien sprake is van de Archiefnominatie
        # "vernietigen"; voor te bewaren zaakdossiers start de Archiefactietermijn op de
        # einddatum van de zaak.
        if (self.archiefnominatie == Archiefnominatie.blijvend_bewaren
                and not self.brondatum_archiefprocedure_afleidingswijze):
            self.brondatum_archiefprocedure_afleidingswijze = (
                Afleidingswijze.afgehandeld)

        super().save(*args, **kwargs)

    def __str__(self):
        return f"{self.zaaktype} - {self.omschrijving}"

    def get_selectielijstklasse(self):
        if not hasattr(self, "_selectielijstklasse"):
            # selectielijstklasse should've been validated at this point by either
            # forms or serializers
            response = requests.get(self.selectielijstklasse)
            response.raise_for_status()
            self._selectielijstklasse = response.json()
        return self._selectielijstklasse