S-09b: Approval flow — temp admin endpoint + status transition to projection #77

Open
not wants to merge 10 commits from feat/75-approval-flow into main
Contributor

What & why

S-09b (#75, split from #10) — the approval flow that completes the walking skeleton. A behandelaar can now approve a submitted registration; the entry flips from INGEDIEND to INGESCHREVEN in the public register. Flow: POST /registrations/{id}/approve (domain) → ACL sets the zaak eindstatus (ZGW /statussen) → OpenZaak → NRC → event-subscriber → projection → openbaar.

Changes (bottom-up, each red→green TDD)

  • DomainRegistrationStatus.Ingeschreven + Registration.Approve() (guards: opened zaak, only from INGEDIEND); ApproveRegistration use case (idempotent) + temp POST /registrations/{id}/approve endpoint; IAclClient.ApproveZaakAsync.
  • ACL — resolves the zaaktype's eindstatus from the catalogus (isEindstatus / highest volgnummer) and POSTs a ZGW status; exposed as POST /statussen. Unit + real-OpenZaak integration test.
  • Event-subscriber — binds NRC hoofdObject, projects a status/create as INGESCHREVEN keyed on the zaak (updates the existing row), without reading OpenZaak (§8.1). Retains the ZGW resource in the log (new column + EF migration) so a rebuild reproduces the status.
  • e2e — extended: submit → public INGEDIEND → approve → public INGESCHREVEN.
  • Docs — ADR-0011 (the two non-obvious decisions + the walking-skeleton assumption) + demo note.

Key decisions (see ADR-0011)

  • ACL discovers the eindstatus (chosen over injecting a statustype URL): no new config/seed plumbing, domain stays ZGW-ignorant.
  • Any post-creation status-set ⇒ INGESCHREVEN: in the walking skeleton the only status ever set after creation is the approval, and the subscriber may not read ZGW — documented to tighten when more transitions arrive (S-12+).

Verification

  • All .NET unit suites green locally (domain 47, acl 11, event-subscriber 14, bff 16, acceptance 7); Release build + dotnet format clean.
  • No new compose config (the eindstatus-discovery approach avoided it).
  • The real-OpenZaak integration test (ACL status-set) and the full submit→approve→visible e2e run in CI verify-stack (live NRC→projection + selectielijst egress, not reproducible locally).

closes #75

## What & why S-09b (#75, split from #10) — the **approval flow** that completes the walking skeleton. A behandelaar can now approve a submitted registration; the entry flips from `INGEDIEND` to `INGESCHREVEN` in the public register. Flow: `POST /registrations/{id}/approve` (domain) → ACL sets the zaak eindstatus (ZGW `/statussen`) → OpenZaak → NRC → event-subscriber → projection → openbaar. ## Changes (bottom-up, each red→green TDD) - **Domain** — `RegistrationStatus.Ingeschreven` + `Registration.Approve()` (guards: opened zaak, only from INGEDIEND); `ApproveRegistration` use case (idempotent) + temp `POST /registrations/{id}/approve` endpoint; `IAclClient.ApproveZaakAsync`. - **ACL** — resolves the zaaktype's **eindstatus** from the catalogus (`isEindstatus` / highest volgnummer) and POSTs a ZGW status; exposed as `POST /statussen`. Unit + real-OpenZaak integration test. - **Event-subscriber** — binds NRC `hoofdObject`, projects a `status`/`create` as `INGESCHREVEN` keyed on the zaak (updates the existing row), **without reading OpenZaak** (§8.1). Retains the ZGW `resource` in the log (new column + EF migration) so a rebuild reproduces the status. - **e2e** — extended: submit → public INGEDIEND → approve → public INGESCHREVEN. - **Docs** — ADR-0011 (the two non-obvious decisions + the walking-skeleton assumption) + demo note. ## Key decisions (see ADR-0011) - **ACL discovers the eindstatus** (chosen over injecting a statustype URL): no new config/seed plumbing, domain stays ZGW-ignorant. - **Any post-creation status-set ⇒ INGESCHREVEN**: in the walking skeleton the only status ever set after creation is the approval, and the subscriber may not read ZGW — documented to tighten when more transitions arrive (S-12+). ## Verification - All .NET unit suites green locally (domain 47, acl 11, event-subscriber 14, bff 16, acceptance 7); Release build + `dotnet format` clean. - No new compose config (the eindstatus-discovery approach avoided it). - The real-OpenZaak integration test (ACL status-set) and the full submit→approve→visible e2e run in CI `verify-stack` (live NRC→projection + selectielijst egress, not reproducible locally). closes #75
not added 10 commits 2026-07-13 15:06:25 +00:00
Guards: an opened zaak is required and only a submitted registration can be
approved. Fails to compile — Registration.Approve() and RegistrationStatus.
Ingeschreven don't exist yet (red).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Approve() advances a submitted registration with an opened zaak to INGESCHREVEN;
re-approval and approval-before-zaak are rejected as invalid transitions.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Red: ApproveRegistration and IAclClient.ApproveZaakAsync don't exist yet.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Loads the registration, asks the ACL to set its zaak's final status (new
IAclClient.ApproveZaakAsync → POST /statussen), then advances the aggregate to
INGESCHREVEN. Idempotent: a repeated approval is a no-op. Adds HTTP-adapter tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Red: AclService.ApproveZaakAsync and IZaakGateway.SetZaakToEindstatusAsync don't exist yet.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The gateway resolves the zaaktype's eindstatus from the catalogus (isEindstatus,
falling back to the highest volgnummer) and POSTs a status against the zaak. Exposed
as POST /statussen for the domain's approve use case. Adds an integration test that
sets the eindstatus against a real OpenZaak and verifies the zaak's current status.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A zaken/status/create notification (hoofdObject = the zaak) updates the zaak's row to
INGESCHREVEN, and a rebuild reproduces it. Red: Notification has no hoofdObject/
IsZaakStatusSet and RegistrationStatus.Ingeschreven doesn't exist.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Bind the NRC hoofdObject, recognise a zaken/status/create notification, and key the
projection on the zaak (hoofdObject) so the status updates the existing INGEDIEND row
to INGESCHREVEN — without reading OpenZaak (§8.1). Retain the ZGW resource in the log
(new column + migration) so a rebuild reproduces the approved status. Updates the
acceptance fakes for the new IAclClient/IZaakGateway members.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Extend the walking-skeleton happy path: after the entry is publicly visible as
INGEDIEND, approve it via the temporary admin endpoint (waiting for the zaak to be
opened first), then poll the openbaar register until it shows INGESCHREVEN —
covering submit → BFF → domain → ACL → NRC → projection → public visibility.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
docs(architecture): ADR-0011 approval status flow + demo note (refs #75)
Some checks failed
CI / lint (pull_request) Successful in 1m7s
CI / build (pull_request) Successful in 54s
CI / unit (pull_request) Successful in 1m4s
CI / frontend (pull_request) Successful in 2m0s
CI / mutation (pull_request) Failing after 1m54s
CI / verify-stack (pull_request) Failing after 5m49s
8badef02af
Records the two non-obvious decisions: the ACL resolves the zaaktype eindstatus
(domain stays ZGW-ignorant), and the event-subscriber projects INGESCHREVEN from
the notification alone (hoofdObject + any status-create), never reading OpenZaak.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Some checks failed
CI / lint (pull_request) Successful in 1m7s
Required
Details
CI / build (pull_request) Successful in 54s
Required
Details
CI / unit (pull_request) Successful in 1m4s
Required
Details
CI / frontend (pull_request) Successful in 2m0s
CI / mutation (pull_request) Failing after 1m54s
Required
Details
CI / verify-stack (pull_request) Failing after 5m49s
Required
Details
Some required checks were not successful.
You are not authorized to merge this pull request.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin feat/75-approval-flow:feat/75-approval-flow
git checkout feat/75-approval-flow
Sign in to join this conversation.