- fix(deps): pin @babel/core ^7.29.7 via overrides → npm audit 0 (dev+prod),
no --force / no Angular downgrade; README corrected
- feat(brief): field-level PII reveal (PRD-0002 §5c) — CaseContext BIG-nummer
ships masked; step-up-stubbed (X-Step-Up), audited POST /brief/reveal-bignummer
unmasks it; drafter-only capability, deny-by-default. Realized on the BIG-nummer
(no BSN on the wire)
- feat(authz): no-PII AuditAuthz log for reveal attempts + org-admin denials (§8)
- feat(routes): wire capabilityGuard('orgtemplate:edit') onto brief/huisstijl (§6)
- test: backend +5 (Authz + reveal endpoint), FE +3 (adapter boundary, store swap)
- docs: PRD-0002 §5c/§9, WP-18 follow-up, README
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
208 lines
9.9 KiB
C#
208 lines
9.9 KiB
C#
namespace BigRegister.Api.Contracts;
|
|
|
|
// Wire contracts (the FE⇄BE seam). Field names + shapes mirror the frontend's
|
|
// contracts/*.dto.ts 1:1; the NSwag-generated TS client is produced from these.
|
|
|
|
public sealed record AdresDto(string Straat, string Postcode, string Woonplaats);
|
|
|
|
public sealed record RegistrationStatusDto(
|
|
string Tag,
|
|
string? HerregistratieDatum = null,
|
|
string? GeschorstTot = null,
|
|
string? Reden = null,
|
|
string? DoorgehaaldOp = null);
|
|
|
|
public sealed record RegistrationDto(
|
|
string BigNummer,
|
|
string Naam,
|
|
string Beroep,
|
|
string Registratiedatum,
|
|
string Geboortedatum,
|
|
RegistrationStatusDto Status);
|
|
|
|
public sealed record PersonDto(string Naam, string Geboortedatum, AdresDto Adres);
|
|
|
|
public sealed record HerregistratieDecisionsDto(bool EligibleForHerregistratie, string? HerregistratieReason);
|
|
|
|
public sealed record DashboardViewDto(RegistrationDto Registration, PersonDto Person, HerregistratieDecisionsDto Decisions);
|
|
|
|
public sealed record AantekeningDto(string Type, string Omschrijving, string Datum);
|
|
|
|
public sealed record BrpAddressDto(bool Gevonden, AdresDto? Adres);
|
|
|
|
public sealed record PolicyQuestionDto(string Id, string Vraag, string Type);
|
|
|
|
public sealed record DuoDiplomaDto(
|
|
string Id,
|
|
string Naam,
|
|
string Instelling,
|
|
int Jaar,
|
|
string Beroep,
|
|
IReadOnlyList<PolicyQuestionDto> PolicyQuestions);
|
|
|
|
public sealed record ManualDiplomaPolicyDto(IReadOnlyList<string> Beroepen, IReadOnlyList<PolicyQuestionDto> PolicyQuestions);
|
|
|
|
public sealed record DuoLookupDto(IReadOnlyList<DuoDiplomaDto> Diplomas, ManualDiplomaPolicyDto Handmatig);
|
|
|
|
public sealed record IntakePolicyDto(int ScholingThreshold);
|
|
|
|
// --- Document upload contracts ---
|
|
|
|
public sealed record DocumentCategoryDto(
|
|
string CategoryId, string Label, string Description, bool Required,
|
|
IReadOnlyList<string> AcceptedTypes, int MaxSizeMb, bool Multiple, bool AllowPostDelivery);
|
|
|
|
public sealed record UploadCategoriesDto(IReadOnlyList<DocumentCategoryDto> Categories);
|
|
|
|
public sealed record UploadResponse(string DocumentId, string LocalId);
|
|
|
|
public sealed record UploadStatusItemDto(string LocalId, string Status, string? DocumentId);
|
|
public sealed record UploadStatusDto(IReadOnlyList<UploadStatusItemDto> Results);
|
|
|
|
// Per-category delivery intent on submit: digital categories carry their DocumentId,
|
|
// post-delivery categories carry Channel="post".
|
|
public sealed record DocumentRefDto(string CategoryId, string Channel, string? DocumentId = null);
|
|
|
|
// Submit requests carry only the fields the server re-validates (UX-only fields
|
|
// stay on the client). ponytail: a real submit would carry the full application.
|
|
public sealed record RegistratieRequest(string DiplomaHerkomst, IReadOnlyList<DocumentRefDto>? Documents = null);
|
|
public sealed record IntakeRequest(int Uren);
|
|
public sealed record HerregistratieRequest(int Uren, IReadOnlyList<DocumentRefDto>? Documents = null);
|
|
public sealed record ChangeRequestRequest(string Straat, string Postcode, string Woonplaats);
|
|
|
|
public sealed record ReferentieResponse(string Referentie);
|
|
|
|
// --- Applications (aanvragen): the system of record for the dashboard. ---
|
|
|
|
// Status is a discriminated union by Tag (Concept | InBehandeling | Goedgekeurd |
|
|
// Afgewezen); only the fields relevant to a tag are populated. Mirrors the FE union.
|
|
public sealed record AanvraagStatusDto(
|
|
string Tag,
|
|
int? StepIndex = null,
|
|
int? StepCount = null,
|
|
string? Referentie = null,
|
|
bool? Manual = null,
|
|
string? Reden = null);
|
|
|
|
public sealed record ApplicationSummaryDto(
|
|
string Id, string Type, AanvraagStatusDto Status,
|
|
IReadOnlyList<string> DocumentIds,
|
|
string CreatedAt, string UpdatedAt, string? SubmittedAt);
|
|
|
|
public sealed record ApplicationDetailDto(
|
|
string Id, string Type, AanvraagStatusDto Status,
|
|
System.Text.Json.JsonElement? Draft,
|
|
IReadOnlyList<string> DocumentIds,
|
|
string CreatedAt, string UpdatedAt, string? SubmittedAt);
|
|
|
|
public sealed record CreateApplicationRequest(string Type);
|
|
|
|
public sealed record DraftSyncRequest(
|
|
System.Text.Json.JsonElement Draft, int StepIndex, int StepCount,
|
|
IReadOnlyList<string>? DocumentIds = null);
|
|
|
|
// Submit carries only the fields the server re-validates per wizard type.
|
|
public sealed record SubmitApplicationRequest(
|
|
string? DiplomaHerkomst = null, int? Uren = null,
|
|
IReadOnlyList<DocumentRefDto>? Documents = null);
|
|
|
|
public sealed record SubmitApplicationResponse(string Referentie, AanvraagStatusDto Status);
|
|
|
|
// --- Brief (letter composition) contracts ---
|
|
// Rich text is a serialisable node tree; the node union is flattened with a `Type`
|
|
// discriminator + nullable fields, the same wire convention as AanvraagStatusDto.
|
|
// The backend treats the content as opaque (stores/returns it) — the FE owns the
|
|
// rich-text semantics and re-validates at its parse* boundary.
|
|
|
|
public sealed record RichTextNodeDto(string Type, string? Text = null, IReadOnlyList<string>? Marks = null, string? Key = null);
|
|
// List is null for a plain paragraph, "bullet" or "number" for a list item.
|
|
public sealed record ParagraphDto(IReadOnlyList<RichTextNodeDto> Nodes, string? List = null);
|
|
public sealed record RichTextBlockDto(IReadOnlyList<ParagraphDto> Paragraphs);
|
|
|
|
public sealed record PlaceholderDefDto(string Key, string Label, bool AutoResolvable, bool? Fillable = null, bool? Deprecated = null);
|
|
|
|
// LetterBlock union (passage | freeText), flattened: passage-only fields are nullable.
|
|
public sealed record LetterBlockDto(
|
|
string Type, string BlockId, RichTextBlockDto Content,
|
|
string? SourcePassageId = null, int? SourceVersion = null, bool? Edited = null);
|
|
|
|
// Locked sections (aanhef, slot) are predefined template text the drafter cannot edit.
|
|
public sealed record LetterSectionDto(string SectionKey, string Title, bool Required, IReadOnlyList<LetterBlockDto> Blocks, bool Locked = false);
|
|
|
|
// BriefStatus union, flattened by Tag (draft | submitted | approved | rejected | sent).
|
|
public sealed record BriefStatusDto(
|
|
string Tag,
|
|
string? SubmittedBy = null, string? SubmittedAt = null,
|
|
string? ApprovedBy = null, string? ApprovedAt = null,
|
|
string? RejectedBy = null, string? RejectedAt = null, string? Comments = null,
|
|
string? SentAt = null);
|
|
|
|
// Besluit/Reason tag the passage for guided drafting (WP-brief-v3): the behandelaar
|
|
// picks the besluit + reden and the FE filters this library to the matching passages.
|
|
// null besluit = relevant to any besluit; null reason = not reason-specific.
|
|
public sealed record LibraryPassageDto(
|
|
string PassageId, string Scope, string SectionKey, string Label,
|
|
RichTextBlockDto Content, int Version, string? Beroep = null, bool IsDefault = false,
|
|
string? Besluit = null, string? Reason = null);
|
|
|
|
public sealed record BriefDto(
|
|
string BriefId, string Beroep, string TemplateId,
|
|
IReadOnlyList<PlaceholderDefDto> Placeholders,
|
|
IReadOnlyList<LetterSectionDto> Sections,
|
|
BriefStatusDto Status, string DrafterId);
|
|
|
|
// Decision flags for the CURRENT acting principal + this brief's live status
|
|
// (PRD-0002 phase P1) — the FE renders these, it never recomputes them.
|
|
// CanRevealBigNummer (PRD-0002 §5c): whether the acting principal may unmask the
|
|
// BIG-nummer the case screen ships masked. Status-independent, unlike the action gates.
|
|
public sealed record BriefDecisionsDto(bool CanEdit, bool CanApprove, bool CanReject, bool CanSend, bool CanRevealBigNummer);
|
|
|
|
// The brief's screen DTO also carries the org template it renders with (WP-23):
|
|
// the sub-org's current PUBLISHED version — or, once sent, the version pinned at
|
|
// send time (sent letters are immutable; a republish never re-renders them).
|
|
// The case this letter is about — the zorgverlener + aanvraag the behandelaar is
|
|
// handling. Server-joined onto the brief's screen DTO so brief/ stays a shared-only
|
|
// leaf context (no cross-context import of registratie).
|
|
public sealed record CaseContextDto(string ZorgverlenerNaam, string BigNummer, string Beroep, string AanvraagReferentie);
|
|
|
|
public sealed record BriefViewDto(
|
|
BriefDto Brief, IReadOnlyList<LibraryPassageDto> AvailablePassages, BriefDecisionsDto Decisions,
|
|
OrgTemplateDto OrgTemplate, CaseContextDto CaseContext);
|
|
|
|
public sealed record SaveBriefRequest(IReadOnlyList<LetterSectionDto> Sections);
|
|
public sealed record RejectBriefRequest(string Comments);
|
|
// The unmasked BIG-nummer, returned only from the audited + step-up-gated reveal
|
|
// endpoint (PRD-0002 §5c). Never logged.
|
|
public sealed record RevealBigNummerResponse(string BigNummer);
|
|
|
|
// PRD-0002 §6: coarse, role-derived capabilities for nav/menu-level checks.
|
|
public sealed record MeDto(IReadOnlyList<string> Capabilities);
|
|
|
|
// --- Organization templates (WP-23, Brief v2 PRD §3) ---
|
|
// The second template axis: appearance/identity per sub-organization (letterhead,
|
|
// footer, signature, margins). Orthogonal to the case-type template (sections +
|
|
// placeholders); the two only meet at render time.
|
|
|
|
public sealed record MarginsDto(int TopMm, int RightMm, int BottomMm, int LeftMm);
|
|
|
|
// Version: 0 = a draft (work in progress), n>0 = the published snapshot it is.
|
|
public sealed record OrgTemplateDto(
|
|
string SubOrgId, string OrgName, string ReturnAddress, string? LogoDocumentId,
|
|
string FooterContact, string FooterLegal,
|
|
string SignatureName, string SignatureRole, string SignatureClosing,
|
|
MarginsDto Margins, int Version = 0);
|
|
|
|
public sealed record OrgTemplateVersionDto(int Version, string PublishedAt, OrgTemplateDto Template);
|
|
|
|
// Screen DTO for the admin editor: the editable draft, what's live, the append-only
|
|
// history, and the publish-impact count ("dit raakt N nog niet verzonden brieven").
|
|
public sealed record OrgTemplateAdminViewDto(
|
|
OrgTemplateDto Draft, int PublishedVersion,
|
|
IReadOnlyList<OrgTemplateVersionDto> History, int UnsentBriefs);
|
|
|
|
public sealed record SubOrgSummaryDto(string SubOrgId, string OrgName, int PublishedVersion);
|
|
|
|
public sealed record SaveOrgTemplateRequest(OrgTemplateDto Draft);
|
|
|
|
public sealed record PublishOrgTemplateResponse(int Version, int AffectedUnsentBriefs);
|