feat(#13): S-12a — beoordeling decision model (domain) (#82)
All checks were successful
CI / lint (push) Successful in 1m15s
CI / build (push) Successful in 58s
CI / unit (push) Successful in 1m9s
CI / frontend (push) Successful in 2m23s
CI / mutation (push) Successful in 5m3s
CI / verify-stack (push) Successful in 8m37s

## What & why

First sub-slice of **S-12 (#13)** — the **beoordeling decision model** in the Domain Service. Foundation for the behandel-portal: it gives the domain a proper decision lifecycle before any UI/Flowable/BFF work.

- **Statuses:** add `InBehandeling` and `Afgewezen` to `RegistrationStatus`.
- **Aggregate:** `TakeIntoBehandeling()` (`Ingediend → InBehandeling`, idempotent, guards terminal states); generalise the behandelaar decision — `Approve()` (requires a zaak) and new `Reject()` both act on an `Ingediend`/`InBehandeling` registration → `Ingeschreven`/`Afgewezen`.
- **Use-case:** `BeoordeelRegistratie` (`goedkeuren` sets the zaak's final status via the ACL §8.1 → `Ingeschreven`; `afwijzen` → `Afgewezen`, domain-only for now). Idempotent.
- **Endpoint:** `POST /registrations/{id}/decide` (`{ "besluit": "goedkeuren" | "afwijzen" }`), superseding the temporary `/approve` (retired when the portal lands, S-12d).
- **BDD:** `EenRegistratieBeoordelen.feature` — goedkeuren + afwijzen scenarios (feature-scoped bindings).

**Scoped out** to later S-12 sub-slices: Flowable user-task claim/complete + BPMN `userTask` (S-12b), BFF `/behandel/*` + medewerker authz (S-12c), the Angular behandel-portal + e2e (S-12d), and propagating a *rejection* to the zaak/projection via the ACL.

## Definition of Done

- [x] Linked issue: #13 (umbrella; this PR `refs`, does not close)
- [x] Tests first; red → green per behaviour
- [x] Unit + acceptance green (`make unit`): domain 65, acceptance 9
- [x] Mutation ≥ break(90): domain 98.77%, no survivors in new code (the one unkilled mutant is the pre-existing `FlowableWorkflowClient` baseline)
- [ ] CI green (pending)

Part of #13.

Reviewed-on: #82
This commit was merged in pull request #82.
This commit is contained in:
2026-07-15 07:12:19 +00:00
parent 3023bb6fbe
commit d4ed0ffc22
8 changed files with 450 additions and 12 deletions

View File

@@ -121,4 +121,100 @@ public class RegistrationTests
Assert.Contains("only an INGEDIEND", ex.Message);
Assert.Equal(RegistrationStatus.Ingeschreven, registration.Status);
}
[Fact]
public void Taking_a_registration_into_behandeling_moves_it_to_in_behandeling()
{
var registration = Registration.Submit("123456782");
registration.TakeIntoBehandeling();
Assert.Equal(RegistrationStatus.InBehandeling, registration.Status);
}
[Fact]
public void Re_taking_an_in_behandeling_registration_is_idempotent()
{
var registration = Registration.Submit("123456782");
registration.TakeIntoBehandeling();
registration.TakeIntoBehandeling();
Assert.Equal(RegistrationStatus.InBehandeling, registration.Status);
}
[Fact]
public void Taking_a_decided_registration_into_behandeling_is_rejected()
{
var registration = Registration.Submit("123456782");
registration.AttachZaak(new Uri("http://openzaak/zaken/api/v1/zaken/abc"));
registration.Approve();
var ex = Assert.Throws<InvalidOperationException>(() => registration.TakeIntoBehandeling());
Assert.Contains("only an INGEDIEND", ex.Message);
Assert.Equal(RegistrationStatus.Ingeschreven, registration.Status);
}
[Fact]
public void Approving_an_in_behandeling_registration_with_a_zaak_sets_it_ingeschreven()
{
var registration = Registration.Submit("123456782");
registration.AttachZaak(new Uri("http://openzaak/zaken/api/v1/zaken/abc"));
registration.TakeIntoBehandeling();
registration.Approve();
Assert.Equal(RegistrationStatus.Ingeschreven, registration.Status);
}
[Fact]
public void Rejecting_a_registration_sets_it_afgewezen()
{
var registration = Registration.Submit("123456782");
registration.Reject();
Assert.Equal(RegistrationStatus.Afgewezen, registration.Status);
}
[Fact]
public void Rejecting_needs_no_zaak()
{
// A registration can be turned down before its zaak is ever opened, so Reject must not require one.
var registration = Registration.Submit("123456782");
registration.Reject();
Assert.Equal(RegistrationStatus.Afgewezen, registration.Status);
Assert.Null(registration.ZaakUrl);
}
[Fact]
public void Rejecting_an_in_behandeling_registration_sets_it_afgewezen()
{
var registration = Registration.Submit("123456782");
registration.TakeIntoBehandeling();
registration.Reject();
Assert.Equal(RegistrationStatus.Afgewezen, registration.Status);
}
[Fact]
public void Deciding_an_already_decided_registration_is_rejected()
{
var registration = Registration.Submit("123456782");
registration.Reject();
var approveEx = Assert.Throws<InvalidOperationException>(() =>
{
registration.AttachZaak(new Uri("http://openzaak/zaken/api/v1/zaken/abc"));
registration.Approve();
});
Assert.Contains("IN_BEHANDELING", approveEx.Message);
var rejectEx = Assert.Throws<InvalidOperationException>(() => registration.Reject());
Assert.Contains("Afgewezen", rejectEx.Message);
Assert.Equal(RegistrationStatus.Afgewezen, registration.Status);
}
}