using System.Text.Json; namespace BigRegister.Domain.Applications; /// /// The aanvraag lifecycle as a closed union: (the pre-submission /// wizard draft) → (awaiting a behandelaar's decision, or already /// auto-rejected at submission time — see ) → /// (a behandelaar's outcome recorded). Each variant carries only the fields that make sense for /// it; the private base constructor closes the hierarchy to the nested sealed records below, so /// a caller can never construct a fourth variant, a with no referentie, or /// an Afwijzen/MeerInfoGevraagd with no toelichting — each is a compile error (a missing /// `required` member, CS9035), not a runtime null-check the way the old flat, mutable /// Aanvraag needed one. /// /// is the EF-mapped persistence row this maps to/from /// (Api.Data.AanvraagMapper's ToDomain/ApplyTo/ToEntity) — it stays a /// flat, mutable bag with no invariants of its own (SQLite needs exactly that shape); this type /// is what production code actually reads and writes everywhere else. The wire-facing, /// point-in-time a screen renders is a further, time-dependent /// projection (StatusAt, in Api.Data) — the auto-approval window is a function of /// wall-clock time, not of this stored shape, so it stays a derived read rather than a fourth /// member of this union. /// public abstract record Aanvraag { public required string Id { get; init; } public required string Type { get; init; } // registratie | herregistratie | intake public required string Owner { get; init; } public required IReadOnlyList DocumentIds { get; init; } public required DateTimeOffset CreatedAt { get; init; } public required DateTimeOffset UpdatedAt { get; init; } /// The OpenZaak zaak's URL, set once CreateZaak registers one — null under /// the local source, or before a zaak has been registered at all. public string? ZaakUrl { get; init; } /// Non-null means the ZGW side of this aanvraag's last write did not /// complete — see Api.Data.ApplicationStore.SetZgwError. public string? ZgwError { get; init; } private Aanvraag() { } /// Pre-submission wizard draft. public sealed record Concept : Aanvraag { public JsonElement? Draft { get; init; } public int StepIndex { get; } public int StepCount { get; } /// 0 <= <= — the /// non-strict upper bound, not the strict "<" a wizard's own step cursor uses, because /// ApplicationStore.CreateConcept's freshly-created row is (StepIndex: 0, StepCount: /// 0) before the wizard's first draft sync ever runs, and that has to stay constructible. /// public Concept(int stepIndex, int stepCount) { if (stepIndex < 0 || stepCount < 0 || stepIndex > stepCount) throw new ArgumentOutOfRangeException( nameof(stepIndex), stepIndex, $"StepIndex must be within [0, StepCount ({stepCount})]."); StepIndex = stepIndex; StepCount = stepCount; } } /// Submitted, no behandelaar decision recorded yet. non-null /// means SubmissionRules rejected it automatically at submission time (e.g. a manually /// entered diploma) — terminal in practice (BeoordelingRules.CanDecide refuses a /// besluit once the projected status is already Afgewezen) but structurally still "no besluit /// was ever recorded", hence it lives here rather than in . public sealed record Submitted : Aanvraag { public required string Referentie { get; init; } public required DateTimeOffset SubmittedAt { get; init; } public required bool AutoApprovable { get; init; } public string? Reden { get; init; } } /// A behandelaar's decision — closed by besluit: only /// / require a toelichting /// (BeoordelingRules.RequiresToelichting's rule, now also a type, not just an endpoint /// check) — omitting it is a compile error, not merely a 400 the type happens to also let /// slip through at runtime. public abstract record Decided : Aanvraag { public required string Referentie { get; init; } public required DateTimeOffset SubmittedAt { get; init; } private Decided() { } public sealed record Goedgekeurd : Decided; public sealed record Afgewezen : Decided { public required string Toelichting { get; init; } } public sealed record MeerInfoGevraagd : Decided { public required string Toelichting { get; init; } } } }