From 1cd171d1e30dd3cb18b5b69e6f1abc8b7af25296 Mon Sep 17 00:00:00 2001 From: Niek Otten Date: Tue, 21 Jul 2026 14:42:10 +0200 Subject: [PATCH] feat(infra): seed Geannuleerd statustype + Vervallen resultaattype (refs #106) S-10c needs a distinct cancellation status/resultaat. Adds a non-terminal Geannuleerd statustype (between Ontvangen and the Afgehandeld eindstatus) and a second Vervallen resultaattype, both idempotent by omschrijving. Both resultaattypen draw their selectielijstklasse from the zaaktype's single procestype so they validate on publish. Co-Authored-By: Claude Opus 4.8 (1M context) --- infra/openzaak/seed_catalogus.py | 55 ++++++++++++++++++++++---------- 1 file changed, 38 insertions(+), 17 deletions(-) diff --git a/infra/openzaak/seed_catalogus.py b/infra/openzaak/seed_catalogus.py index ed4ad67..9aae3fb 100644 --- a/infra/openzaak/seed_catalogus.py +++ b/infra/openzaak/seed_catalogus.py @@ -10,7 +10,7 @@ Creates (if absent): Auth uses the JWT client provisioned by setup_configuration (see ADR-0002). Stdlib only — no pip deps. Re-running is safe (matches existing by identifier). """ -import base64, hashlib, hmac, json, os, sys, time, urllib.error, urllib.request +import base64, hashlib, hmac, json, os, sys, time, urllib.error, urllib.parse, urllib.request BASE = os.environ.get("OZ_BASE", "http://localhost:8000") CLIENT_ID = os.environ.get("OZ_CLIENT_ID", "big-reference-seed") @@ -77,8 +77,12 @@ def publish_zaaktype(zt): Selectielijst `selectielijstklasse` whose procestype matches the zaaktype's `selectielijstProcestype`, plus a `resultaattypeomschrijving`. """ + # Ontvangen (begin) → Afgehandeld (eind, highest volgnummer). "Geannuleerd" (S-10c) sits between + # them: a non-terminal status the document-timeout branch sets, so it never displaces the Afgehandeld + # eindstatus the approval path resolves. Keyed by volgnummer on a fresh catalogus (CI reseeds); a + # stale local stack must reset its OpenZaak volumes for the renumbering to take effect. have_st = {s.get("volgnummer") for s in find(f"/statustypen?zaaktype={zt['url']}&status=alles")} - for volgnummer, omschrijving in [(1, "Ontvangen"), (2, "Afgehandeld")]: + for volgnummer, omschrijving in [(1, "Ontvangen"), (2, "Geannuleerd"), (3, "Afgehandeld")]: if volgnummer not in have_st: st, body = api("POST", "/statustypen", { "omschrijving": omschrijving, "zaaktype": zt["url"], "volgnummer": volgnummer}) @@ -95,25 +99,42 @@ def publish_zaaktype(zt): sys.exit(f"create roltype -> {st}: {json.dumps(body, indent=2)}") print("create roltype Aanvrager") - if find(f"/resultaattypen?zaaktype={zt['url']}&status=alles"): - print("skip resultaattype Geregistreerd") + # Two resultaattypen, keyed by omschrijving so each is created independently (idempotent): + # "Geregistreerd" — the approval outcome (S-09b) + # "Vervallen" — the document-timeout cancellation outcome (S-10c) + # Both selectielijstklassen must share the zaaktype's selectielijstProcestype, so pick two + # Selectielijst resultaten from a single procestype and set that procestype on the zaaktype. + have_rt = {r.get("omschrijving") for r in find(f"/resultaattypen?zaaktype={zt['url']}&status=alles")} + wanted = [("Geregistreerd", "blijvend_bewaren"), ("Vervallen", "vernietigen")] + if all(naam in have_rt for naam, _ in wanted): + print("skip resultaattypen Geregistreerd + Vervallen") else: - resultaat = selectielijst("/resultaten?pageSize=1")["results"][0] + # Anchor on the procestype of an arbitrary resultaat, then fetch that procestype's resultaten so + # both klassen validate against the zaaktype's selectielijstProcestype. + procestype = selectielijst("/resultaten?pageSize=1")["results"][0]["procesType"] + resultaten = selectielijst(f"/resultaten?procesType={urllib.parse.quote(procestype, safe='')}")["results"] + if len(resultaten) < len(wanted): + sys.exit(f"selectielijst procestype has too few resultaten ({len(resultaten)}) for {len(wanted)} resultaattypen") omschrijvingen = selectielijst("/resultaattypeomschrijvingen") - oms = (omschrijvingen if isinstance(omschrijvingen, list) else omschrijvingen["results"])[0]["url"] - # The selectielijstklasse and the zaaktype must share a procestype. - st, body = api("PATCH", zt["url"], {"selectielijstProcestype": resultaat["procesType"]}) + oms_list = omschrijvingen if isinstance(omschrijvingen, list) else omschrijvingen["results"] + + st, body = api("PATCH", zt["url"], {"selectielijstProcestype": procestype}) if st != 200: sys.exit(f"set procestype -> {st}: {json.dumps(body, indent=2)}") - st, body = api("POST", "/resultaattypen", { - "zaaktype": zt["url"], "omschrijving": "Geregistreerd", - "resultaattypeomschrijving": oms, "selectielijstklasse": resultaat["url"], - "archiefnominatie": "blijvend_bewaren", - "brondatumArchiefprocedure": {"afleidingswijze": "afgehandeld"}, - }) - if st != 201: - sys.exit(f"create resultaattype -> {st}: {json.dumps(body, indent=2)}") - print("create resultaattype Geregistreerd") + + for i, (naam, archiefnominatie) in enumerate(wanted): + if naam in have_rt: + print(f"skip resultaattype {naam}") + continue + st, body = api("POST", "/resultaattypen", { + "zaaktype": zt["url"], "omschrijving": naam, + "resultaattypeomschrijving": oms_list[i]["url"], "selectielijstklasse": resultaten[i]["url"], + "archiefnominatie": archiefnominatie, + "brondatumArchiefprocedure": {"afleidingswijze": "afgehandeld"}, + }) + if st != 201: + sys.exit(f"create resultaattype {naam} -> {st}: {json.dumps(body, indent=2)}") + print(f"create resultaattype {naam}") if zt.get("concept", True): st, body = api("POST", f"{zt['url']}/publish")