class InformatieObjectType(GeldigheidMixin, ConceptMixin, models.Model):
    """
    Aanduiding van de aard van INFORMATIEOBJECTen zoals gehanteerd door de zaakbehandelende organisatie.

    Unieke aanduiding van CATALOGUS in combinatie met Informatieobjecttype-omschrijving.
    """
    uuid = models.UUIDField(
        unique=True, default=_uuid.uuid4,
        help_text="Unieke resource identifier (UUID4)"
    )
    omschrijving = models.CharField(
        _('omschrijving'), max_length=80,
        help_text=_('Omschrijving van de aard van informatieobjecten van dit INFORMATIEOBJECTTYPE.'))
    omschrijving_generiek = models.ForeignKey(
        'datamodel.InformatieObjectTypeOmschrijvingGeneriek', verbose_name=_('omschrijving generiek'),
        blank=True, null=True, on_delete=models.CASCADE,
        help_text=_('Algemeen gehanteerde omschrijving van het INFORMATIEOBJECTTYPE.'))
    informatieobjectcategorie = models.CharField(
        _('categorie'), max_length=80,
        help_text=_('Typering van de aard van informatieobjecten van dit INFORMATIEOBJECTTYPE.'))
    trefwoord = ArrayField(
        models.CharField(_('trefwoord'), max_length=30), default=list,
        blank=True, help_text=_('Trefwoord(en) waarmee informatieobjecten van het INFORMATIEOBJECTTYPE kunnen worden '
                                'gekarakteriseerd. (Gebruik een komma om waarden van elkaar te onderscheiden.)'))
    vertrouwelijkheidaanduiding = VertrouwelijkheidsAanduidingField(
        _('vertrouwelijkheidaanduiding'),
        help_text=_(
            'Aanduiding van de mate waarin informatieobjecten van dit INFORMATIEOBJECTTYPE voor de '
            'openbaarheid bestemd zijn.')
    )
    model = ArrayField(
        models.URLField(_('model')),
        blank=True,  default=list, help_text=_(
            'De URL naar het model / sjabloon dat wordt gebruikt voor de creatie van informatieobjecten '
            'van dit INFORMATIEOBJECTTYPE. (Gebruik een komma om waarden van elkaar te onderscheiden.)'))
    toelichting = models.CharField(
        _('toelichting'), max_length=1000, blank=True, null=True,
        help_text=_('Een eventuele toelichting op dit INFORMATIEOBJECTTYPE.'))

    catalogus = models.ForeignKey(
        'datamodel.Catalogus', verbose_name=_('maakt deel uit van'),
        on_delete=models.CASCADE, help_text=('URL-referentie naar de CATALOGUS waartoe dit INFORMATIEOBJECTTYPE behoort.')
    )

    zaaktypes = models.ManyToManyField(
        'datamodel.ZaakType', verbose_name=_('zaaktypes'),
        related_name='heeft_relevant_informatieobjecttype',
        through='datamodel.ZaakInformatieobjectType',
        help_text=_('ZAAKTYPE met ZAAKen die relevant kunnen zijn voor dit INFORMATIEOBJECTTYPE')
    )

    class Meta:
        unique_together = ('catalogus', 'omschrijving')
        verbose_name = _('Informatieobjecttype')
        verbose_name_plural = _('Informatieobjecttypen')

    def __str__(self):
        return '{} - {}'.format(self.catalogus, self.omschrijving)
Пример #2
0
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}"
Пример #3
0
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}"
Пример #4
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)
Пример #5
0
class AutorisatieSpec(models.Model):
    """
    Specification for Autorisatie to install.

    If a spec exists, it will be applied when a ZaakType, InformatieObjectType
    or BesluitType is created.
    """

    applicatie = models.ForeignKey(
        Applicatie,
        on_delete=models.CASCADE,
        related_name="autorisatie_specs",
        verbose_name=_("applicatie"),
    )
    component = models.CharField(
        _("component"),
        max_length=50,
        choices=ComponentTypes.choices,
        help_text=_("Component waarop autorisatie van toepassing is."),
    )
    scopes = ArrayField(
        models.CharField(max_length=100),
        verbose_name=_("scopes"),
        help_text=_("Komma-gescheiden lijst van scope labels."),
    )
    max_vertrouwelijkheidaanduiding = VertrouwelijkheidsAanduidingField(
        help_text=_(
            "Maximaal toegelaten vertrouwelijkheidaanduiding (inclusief)."),
        blank=True,
    )

    class Meta:
        verbose_name = _("autorisatiespec")
        verbose_name_plural = _("autorisatiespecs")
        # since the spec implicates this config is valid for _all_ zaaktypen/
        # informatieobjecttypen/besluittypen, the scopes/VA are constant for
        # these. The exact *type is implied by the component choice, so we need
        # a unique constraint on applicatie + component
        unique_together = ("applicatie", "component")

    def __str__(self):
        return _("{component} autorisatiespec voor {app}").format(
            component=self.get_component_display(), app=self.applicatie)

    @classmethod
    def sync(cls):
        """
        Synchronize the Autorisaties for all Applicaties.

        Invoke this method whenever a ZaakType/InformatieObjectType/BesluitType
        is created to set up the appropriate Autorisatie objects. This is best
        called as part of `transaction.on_commit`.
        """
        from .utils import send_applicatie_changed_notification

        qs = cls.objects.select_related("applicatie").prefetch_related(
            "applicatie__autorisaties")

        to_delete = []
        to_keep = []
        to_add = []

        for spec in qs:
            existing_autorisaties = [
                autorisatie
                for autorisatie in spec.applicatie.autorisaties.all()
                if autorisatie.component == spec.component
            ]

            for autorisatie in existing_autorisaties:
                # schedule for deletion if existing objects differ from the spec
                if (autorisatie.max_vertrouwelijkheidaanduiding !=
                        spec.max_vertrouwelijkheidaanduiding):
                    to_delete.append(autorisatie)
                    continue

                if set(autorisatie.scopes) != set(spec.scopes):
                    to_delete.append(autorisatie)
                    continue

                to_keep.append(autorisatie)

            TypeModel = apps.get_model(COMPONENT_TO_MODEL[spec.component])
            field = COMPONENT_TO_FIELD[spec.component]

            for obj in TypeModel.objects.all():
                url = build_absolute_url(obj.get_absolute_api_url())

                autorisatie = Autorisatie(applicatie=spec.applicatie,
                                          component=spec.component,
                                          scopes=spec.scopes,
                                          max_vertrouwelijkheidaanduiding=spec.
                                          max_vertrouwelijkheidaanduiding,
                                          **{field: url})
                to_add.append(autorisatie)

        Autorisatie.objects.filter(
            pk__in=[autorisatie.pk for autorisatie in to_delete])

        # de-duplicate - whatever is in to_keep should not be added again
        existing_urls = defaultdict(list)
        for autorisatie in to_keep:
            if autorisatie.component not in COMPONENT_TO_FIELD:
                continue
            url = getattr(autorisatie,
                          COMPONENT_TO_FIELD[autorisatie.component])
            existing_urls[autorisatie.component].append(url)

        _to_add = []
        for autorisatie in to_add:
            if autorisatie.component not in COMPONENT_TO_FIELD:
                continue
            url = getattr(autorisatie,
                          COMPONENT_TO_FIELD[autorisatie.component])
            if url in existing_urls[autorisatie.component]:
                continue
            _to_add.append(autorisatie)

        # created the de-duplicated, missing autorisaties
        Autorisatie.objects.bulk_create(_to_add)

        # determine which notifications to send
        changed = {
            autorisatie.applicatie
            for autorisatie in (to_delete + _to_add)
        }
        for applicatie in changed:
            send_applicatie_changed_notification(applicatie)
Пример #6
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)")
    zaaktype_identificatie = models.PositiveIntegerField(  # N5, integer with max_length of 5
        _("identificatie"),
        validators=[MaxValueValidator(99999)],
        help_text=
        _("Unieke identificatie van het ZAAKTYPE binnen de CATALOGUS waarin het ZAAKTYPE voorkomt."
          ),
    )
    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."
          ),
    )

    # TODO [KING]: waardenverzameling zie Zaaktypecatalogus, is dat de
    # catalogus die bij dit zaaktype hoort? Wat is de categorie dan?
    # see also: https://github.com/VNG-Realisatie/gemma-zaken/issues/695
    zaakcategorie = models.CharField(
        _("zaakcategorie"),
        max_length=40,
        blank=True,
        help_text=_("Typering van de aard van ZAAKen van het ZAAKTYPE."),
    )

    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 = DaysDurationField(
        _("doorlooptijd behandeling"),
        help_text=_(
            "De periode waarbinnen volgens wet- en regelgeving een ZAAK van het ZAAKTYPE "
            "afgerond dient te zijn, in kalenderdagen."),
    )
    servicenorm_behandeling = DaysDurationField(
        _("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 = DaysDurationField(
        _("verlengingstermijn"),
        blank=True,
        null=True,
        help_text=_(
            "De termijn in 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."
          ),
    )
    # TODO [KING]: ?? waardenverzameling: De classificatiecode in het gehanteerde
    # archiveringsclassificatiestelsel, gevolgd door een spatie en –
    # tussen haakjes - de gebruikelijke afkorting van de naam van het gehanteerde classificatiestelsel.
    archiefclassificatiecode = models.CharField(
        _("archiefclassificatiecode"),
        max_length=20,
        blank=True,
        null=True,
        help_text=_(
            "De systematische identificatie van zaakdossiers van dit ZAAKTYPE overeenkomstig logisch gestructureerde "
            "conventies, methoden en procedureregels."),
    )
    # TODO [KING]: waardenverzameling heeft de volgende regel, momenteel valideren we hier niets,
    # maar wellicht kan het wel: Indien het om een zaaktype in een catalogus voor een specifieke organisatie gaat,
    # dan de naam van een Organisatorische eenheid of Medewerker overeenkomstig het RGBZ.
    # Hoe weten we of een catalogus van een specifieke organisatie is? Als we Catalogus.contactpersoon_beheer_naam
    # gebruiken dan is dit veld overbodig want dan gebruiken we gewoon
    # ZaakType.catalogus.contactpersoon_beheer_naam
    verantwoordelijke = models.CharField(
        _("verantwoordelijke"),
        max_length=50,
        help_text=_(
            "De (soort) organisatorische eenheid of (functie van) medewerker die verantwoordelijk is voor "
            "de uitvoering van zaken van het ZAAKTYPE."),
    )
    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."
          ),
    )

    # 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)."),
    )

    formulier = models.ManyToManyField(
        "catalogi.Formulier",
        verbose_name=_("formulier"),
        blank=True,
        help_text=_(
            "Formulier Het formulier dat ZAAKen van dit ZAAKTYPE initieert."),
    )

    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", ),
    )

    broncatalogus = models.ForeignKey(
        "catalogi.BronCatalogus",
        verbose_name=_("broncatalogus"),
        blank=True,
        null=True,
        on_delete=models.CASCADE,
        help_text=_("De CATALOGUS waaraan het ZAAKTYPE is ontleend."),
    )
    bronzaaktype = models.ForeignKey(
        "catalogi.BronZaakType",
        verbose_name=("bronzaaktype"),
        blank=True,
        null=True,
        on_delete=models.CASCADE,
        help_text=_(
            "Het zaaktype binnen de CATALOGUS waaraan dit ZAAKTYPE is ontleend."
        ),
    )

    #
    # relaties
    #
    is_deelzaaktype_van = models.ManyToManyField(
        "catalogi.ZaakType",
        verbose_name=_("is deelzaaktype van"),
        blank=True,
        related_name="zaak_typen_is_deelzaaktype_van",
        help_text=
        _("De ZAAKTYPEn (van de hoofdzaken) waaronder ZAAKen van dit ZAAKTYPE als deelzaak kunnen voorkomen."
          ),
    )

    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."),
    )

    class Meta:
        verbose_name = _("Zaaktype")
        verbose_name_plural = _("Zaaktypen")
        ordering = ("catalogus", "zaaktype_identificatie")

        filter_fields = (
            "catalogus",
            "publicatie_indicatie",
            "verlenging_mogelijk",
            "opschorting_en_aanhouding_mogelijk",
            "indicatie_intern_of_extern",
            "vertrouwelijkheidaanduiding",
        )
        ordering_fields = filter_fields
        search_fields = (
            "zaaktype_identificatie",
            "zaaktype_omschrijving",
            "zaaktype_omschrijving_generiek",
            "zaakcategorie",
            "doel",
            "aanleiding",
            "onderwerp",
            "toelichting",
        )

    def __str__(self):
        return "{} - {}".format(self.catalogus, self.zaaktype_identificatie)

    def save(self, *args, **kwargs):
        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):
        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 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:
            query = ZaakType.objects.filter(
                Q(catalogus=self.catalogus),
                Q(zaaktype_omschrijving=self.zaaktype_omschrijving),
                Q(datum_einde_geldigheid=None)
                | Q(datum_einde_geldigheid__gte=self.datum_begin_geldigheid
                    ),  # noqa
            )
            if self.datum_einde_geldigheid is not None:
                query = query.filter(
                    datum_begin_geldigheid__lte=self.datum_einde_geldigheid)

            # regel voor zaaktype omschrijving
            if query.exclude(pk=self.pk).exists():
                raise ValidationError(
                    "Zaaktype-omschrijving moet uniek zijn binnen de CATALOGUS."
                )

        self._clean_geldigheid(self)

    def get_absolute_api_url(self, request=None, **kwargs) -> str:
        kwargs["version"] = "1"
        return super().get_absolute_api_url(request=request, **kwargs)
Пример #7
0
class InformatieObjectType(APIMixin, GeldigheidMixin, ConceptMixin,
                           models.Model):
    """
    Aanduiding van de aard van INFORMATIEOBJECTen zoals gehanteerd door de zaakbehandelende organisatie.

    Unieke aanduiding van CATALOGUS in combinatie met Informatieobjecttype-omschrijving.
    """

    uuid = models.UUIDField(unique=True,
                            default=_uuid.uuid4,
                            help_text="Unieke resource identifier (UUID4)")
    omschrijving = models.CharField(
        _("omschrijving"),
        max_length=80,
        help_text=
        _("Omschrijving van de aard van informatieobjecten van dit INFORMATIEOBJECTTYPE."
          ),
    )
    vertrouwelijkheidaanduiding = VertrouwelijkheidsAanduidingField(
        _("vertrouwelijkheidaanduiding"),
        help_text=_(
            "Aanduiding van de mate waarin informatieobjecten van dit INFORMATIEOBJECTTYPE voor de "
            "openbaarheid bestemd zijn."),
    )
    catalogus = models.ForeignKey(
        "catalogi.Catalogus",
        # verbose_name=_("maakt deel uit van"),
        on_delete=models.CASCADE,
        help_text=
        ("URL-referentie naar de CATALOGUS waartoe dit INFORMATIEOBJECTTYPE behoort."
         ),
    )

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

    objects = SyncAutorisatieManager()

    class Meta:
        verbose_name = _("Informatieobjecttype")
        verbose_name_plural = _("Informatieobjecttypen")

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

    def clean(self):
        from ..utils import get_overlapping_informatieobjecttypes

        super().clean()
        if self.catalogus and self.omschrijving:
            query = get_overlapping_informatieobjecttypes(
                self.catalogus,
                self.omschrijving,
                self.datum_begin_geldigheid,
                self.datum_einde_geldigheid,
                self,
            )

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

    @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)
Пример #8
0
class InformatieObjectType(GeldigheidMixin, ConceptMixin, models.Model):
    """
    Aanduiding van de aard van INFORMATIEOBJECTen zoals gehanteerd door de zaakbehandelende organisatie.

    Unieke aanduiding van CATALOGUS in combinatie met Informatieobjecttype-omschrijving.
    """

    uuid = models.UUIDField(unique=True,
                            default=_uuid.uuid4,
                            help_text="Unieke resource identifier (UUID4)")
    omschrijving = models.CharField(
        _("omschrijving"),
        max_length=80,
        help_text=
        _("Omschrijving van de aard van informatieobjecten van dit INFORMATIEOBJECTTYPE."
          ),
    )
    omschrijving_generiek = models.ForeignKey(
        "catalogi.InformatieObjectTypeOmschrijvingGeneriek",
        verbose_name=_("omschrijving generiek"),
        blank=True,
        null=True,
        on_delete=models.CASCADE,
        help_text=_(
            "Algemeen gehanteerde omschrijving van het INFORMATIEOBJECTTYPE."),
    )
    informatieobjectcategorie = models.CharField(
        _("categorie"),
        max_length=80,
        help_text=
        _("Typering van de aard van informatieobjecten van dit INFORMATIEOBJECTTYPE."
          ),
    )
    trefwoord = ArrayField(
        models.CharField(_("trefwoord"), max_length=30),
        default=list,
        blank=True,
        help_text=
        _("Trefwoord(en) waarmee informatieobjecten van het INFORMATIEOBJECTTYPE kunnen worden "
          "gekarakteriseerd. (Gebruik een komma om waarden van elkaar te onderscheiden.)"
          ),
    )
    vertrouwelijkheidaanduiding = VertrouwelijkheidsAanduidingField(
        _("vertrouwelijkheidaanduiding"),
        help_text=_(
            "Aanduiding van de mate waarin informatieobjecten van dit INFORMATIEOBJECTTYPE voor de "
            "openbaarheid bestemd zijn."),
    )
    model = ArrayField(
        models.URLField(_("model")),
        blank=True,
        default=list,
        help_text=
        _("De URL naar het model / sjabloon dat wordt gebruikt voor de creatie van informatieobjecten "
          "van dit INFORMATIEOBJECTTYPE. (Gebruik een komma om waarden van elkaar te onderscheiden.)"
          ),
    )
    toelichting = models.CharField(
        _("toelichting"),
        max_length=1000,
        blank=True,
        null=True,
        help_text=_("Een eventuele toelichting op dit INFORMATIEOBJECTTYPE."),
    )

    catalogus = models.ForeignKey(
        "catalogi.Catalogus",
        verbose_name=_("maakt deel uit van"),
        on_delete=models.CASCADE,
        help_text=
        ("URL-referentie naar de CATALOGUS waartoe dit INFORMATIEOBJECTTYPE behoort."
         ),
    )

    zaaktypes = models.ManyToManyField(
        "catalogi.ZaakType",
        verbose_name=_("zaaktypes"),
        related_name="heeft_relevant_informatieobjecttype",
        through="catalogi.ZaakInformatieobjectType",
        help_text=
        _("ZAAKTYPE met ZAAKen die relevant kunnen zijn voor dit INFORMATIEOBJECTTYPE"
          ),
    )

    class Meta:
        unique_together = ("catalogus", "omschrijving")
        verbose_name = _("Informatieobjecttype")
        verbose_name_plural = _("Informatieobjecttypen")

    def __str__(self):
        return "{} - {}".format(self.catalogus, self.omschrijving)
Пример #9
0
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}')