refactor: rename Application → Aanvraag across the wire (Step 1/8)

The wire said Application, the domain said Aanvraag — one aggregate with
two names at every hop. Rename the backend DTOs and the /applications
route to /aanvragen, regenerate the typed client, and rename the frontend
adapter/store to match.

Renamed: ApplicationSummaryDto/DetailDto, CreateApplicationRequest,
SubmitApplicationRequest/Response → Aanvraag* equivalents;
ApplicationsAdapter/Store → AanvragenAdapter/Store;
applications.adapter.ts/applications.store.ts → aanvragen.*.

Left untouched: the admin Case/Zaak vocabulary (/admin/cases,
AdminCasesStore) — a separate read model, not part of this rename; the
internal BigRegister.Domain.Applications namespace and the Applications
EF table (renaming those needs a new EF migration, out of scope here).

Part of the dashboard-readability refactor (see the approved plan).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
eho
2026-09-04 14:33:16 +02:00
co-authored by Claude Opus 5
parent faad772f85
commit 194cccfd02
36 changed files with 400 additions and 412 deletions
@@ -1,6 +1,6 @@
import { Injectable, inject } from '@angular/core'; import { Injectable, inject } from '@angular/core';
import { Result, ok, err } from '@shared/kernel/fp'; import { Result, ok, err } from '@shared/kernel/fp';
import { ApiClient, ApplicationSummaryDto } from '@shared/infrastructure/api-client'; import { ApiClient, AanvraagSummaryDto } from '@shared/infrastructure/api-client';
import { import {
WerkvoorraadItem, WerkvoorraadItem,
WerkvoorraadStatus, WerkvoorraadStatus,
@@ -18,7 +18,7 @@ import {
export class WerkvoorraadAdapter { export class WerkvoorraadAdapter {
private client = inject(ApiClient); private client = inject(ApiClient);
list(): Promise<ApplicationSummaryDto[]> { list(): Promise<AanvraagSummaryDto[]> {
return this.client.werkvoorraad(); return this.client.werkvoorraad();
} }
} }
@@ -26,7 +26,7 @@ export class WerkvoorraadAdapter {
const AANVRAAG_TYPES: readonly string[] = ['registratie', 'herregistratie', 'intake']; const AANVRAAG_TYPES: readonly string[] = ['registratie', 'herregistratie', 'intake'];
function parseWerkvoorraadStatus( function parseWerkvoorraadStatus(
s: ApplicationSummaryDto['status'] | undefined, s: AanvraagSummaryDto['status'] | undefined,
): Result<string, WerkvoorraadStatus> { ): Result<string, WerkvoorraadStatus> {
if (!s || typeof s.tag !== 'string') return err('werkvoorraad: missing status'); if (!s || typeof s.tag !== 'string') return err('werkvoorraad: missing status');
switch (s.tag) { switch (s.tag) {
@@ -44,7 +44,7 @@ function parseWerkvoorraadStatus(
export function parseWerkvoorraadItem(json: unknown): Result<string, WerkvoorraadItem> { export function parseWerkvoorraadItem(json: unknown): Result<string, WerkvoorraadItem> {
if (typeof json !== 'object' || json === null) return err('werkvoorraad: not an object'); if (typeof json !== 'object' || json === null) return err('werkvoorraad: not an object');
const dto = json as ApplicationSummaryDto; const dto = json as AanvraagSummaryDto;
if (typeof dto.id !== 'string') return err('werkvoorraad: missing id'); if (typeof dto.id !== 'string') return err('werkvoorraad: missing id');
if (typeof dto.type !== 'string' || !AANVRAAG_TYPES.includes(dto.type)) if (typeof dto.type !== 'string' || !AANVRAAG_TYPES.includes(dto.type))
return err(`werkvoorraad: bad type ${dto.type}`); return err(`werkvoorraad: bad type ${dto.type}`);
@@ -1,8 +1,8 @@
import { TestBed } from '@angular/core/testing'; import { TestBed } from '@angular/core/testing';
import { describe, it, expect, vi } from 'vitest'; import { describe, it, expect, vi } from 'vitest';
import { SUBMIT_FAILED } from '@shared/application/submit'; import { SUBMIT_FAILED } from '@shared/application/submit';
import { ApplicationsAdapter } from '@registratie/infrastructure/applications.adapter'; import { AanvragenAdapter } from '@registratie/infrastructure/aanvragen.adapter';
import { ApplicationsStore } from './applications.store'; import { AanvragenStore } from './aanvragen.store';
const summary = (id: string) => ({ const summary = (id: string) => ({
id, id,
@@ -13,20 +13,20 @@ const summary = (id: string) => ({
updatedAt: '2026-07-23T10:00:00Z', updatedAt: '2026-07-23T10:00:00Z',
}); });
function setup(adapter: Partial<ApplicationsAdapter>): ApplicationsStore { function setup(adapter: Partial<AanvragenAdapter>): AanvragenStore {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
providers: [{ provide: ApplicationsAdapter, useValue: adapter }], providers: [{ provide: AanvragenAdapter, useValue: adapter }],
}); });
// The store's own constructor kicks off `load()` (dashboard revisit refresh) — // The store's own constructor kicks off `load()` (dashboard revisit refresh) —
// give every test a `list` so that initial call has something to resolve. // give every test a `list` so that initial call has something to resolve.
return TestBed.inject(ApplicationsStore); return TestBed.inject(AanvragenStore);
} }
describe('ApplicationsStore', () => { describe('AanvragenStore', () => {
it('loads and parses the list', async () => { it('loads and parses the list', async () => {
const store = setup({ list: () => Promise.resolve([summary('a'), summary('b')]) }); const store = setup({ list: () => Promise.resolve([summary('a'), summary('b')]) });
await store.load(); await store.load();
const s = store.applications(); const s = store.aanvragen();
expect(s.tag).toBe('Success'); expect(s.tag).toBe('Success');
expect(s.tag === 'Success' && s.value.map((a) => a.id)).toEqual(['a', 'b']); expect(s.tag === 'Success' && s.value.map((a) => a.id)).toEqual(['a', 'b']);
}); });
@@ -41,7 +41,7 @@ describe('ApplicationsStore', () => {
await store.cancel('a'); await store.cancel('a');
expect(cancel).toHaveBeenCalledWith('a'); expect(cancel).toHaveBeenCalledWith('a');
const s = store.applications(); const s = store.aanvragen();
expect(s.tag === 'Success' && s.value.map((a) => a.id)).toEqual(['b']); expect(s.tag === 'Success' && s.value.map((a) => a.id)).toEqual(['b']);
expect(store.lastError()).toBeNull(); expect(store.lastError()).toBeNull();
}); });
@@ -55,7 +55,7 @@ describe('ApplicationsStore', () => {
await store.load(); await store.load();
await store.cancel('a'); await store.cancel('a');
const s = store.applications(); const s = store.aanvragen();
expect(s.tag === 'Success' && s.value.map((a) => a.id)).toEqual(['a']); // reappears expect(s.tag === 'Success' && s.value.map((a) => a.id)).toEqual(['a']); // reappears
expect(store.lastError()).toBe(SUBMIT_FAILED); expect(store.lastError()).toBe(SUBMIT_FAILED);
}); });
@@ -2,15 +2,12 @@ import { Injectable, inject, signal } from '@angular/core';
import { RemoteData } from '@shared/application/remote-data'; import { RemoteData } from '@shared/application/remote-data';
import { runSubmit, SUBMIT_FAILED } from '@shared/application/submit'; import { runSubmit, SUBMIT_FAILED } from '@shared/application/submit';
import { Aanvraag } from '@registratie/domain/aanvraag'; import { Aanvraag } from '@registratie/domain/aanvraag';
import { import { AanvragenAdapter, parseAanvragen } from '@registratie/infrastructure/aanvragen.adapter';
ApplicationsAdapter,
parseApplications,
} from '@registratie/infrastructure/applications.adapter';
type Err = Error | undefined; type Err = Error | undefined;
/** /**
* The dashboard's view of the user's applications (aanvragen) — the backend is the * The dashboard's view of the user's aanvragen — the backend is the
* system of record (PRD 0001). One root singleton OWNS the list as a writable * system of record (PRD 0001). One root singleton OWNS the list as a writable
* RemoteData signal (CLAUDE.md §3: change state only through methods). Cancel removes * RemoteData signal (CLAUDE.md §3: change state only through methods). Cancel removes
* the row SYNCHRONOUSLY, so the block disappears deterministically — no dependence on * the row SYNCHRONOUSLY, so the block disappears deterministically — no dependence on
@@ -20,11 +17,11 @@ type Err = Error | undefined;
* surfaces `lastError` on failure (RB-20). * surfaces `lastError` on failure (RB-20).
*/ */
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class ApplicationsStore { export class AanvragenStore {
private adapter = inject(ApplicationsAdapter); private adapter = inject(AanvragenAdapter);
private state = signal<RemoteData<Err, Aanvraag[]>>({ tag: 'Loading' }); private state = signal<RemoteData<Err, Aanvraag[]>>({ tag: 'Loading' });
readonly applications = this.state.asReadonly(); readonly aanvragen = this.state.asReadonly();
/** Set on a failed cancel (RB-20): the optimistic removal already rolled back by /** Set on a failed cancel (RB-20): the optimistic removal already rolled back by
then, this is only the message for the alert the page renders above the list. */ then, this is only the message for the alert the page renders above the list. */
@@ -40,7 +37,7 @@ export class ApplicationsStore {
async load() { async load() {
if (this.state().tag !== 'Success') this.state.set({ tag: 'Loading' }); if (this.state().tag !== 'Success') this.state.set({ tag: 'Loading' });
try { try {
const parsed = parseApplications(await this.adapter.list()); const parsed = parseAanvragen(await this.adapter.list());
this.state.set( this.state.set(
parsed.ok parsed.ok
? { tag: 'Success', value: parsed.value } ? { tag: 'Success', value: parsed.value }
@@ -1,7 +1,7 @@
import { TestBed } from '@angular/core/testing'; import { TestBed } from '@angular/core/testing';
import { describe, it, expect, vi } from 'vitest'; import { describe, it, expect, vi } from 'vitest';
import { SUBMIT_FAILED } from '@shared/application/submit'; import { SUBMIT_FAILED } from '@shared/application/submit';
import { ApplicationsAdapter } from '@registratie/infrastructure/applications.adapter'; import { AanvragenAdapter } from '@registratie/infrastructure/aanvragen.adapter';
import { AdminCasesStore } from './admin-cases.store'; import { AdminCasesStore } from './admin-cases.store';
const summary = (id: string) => ({ const summary = (id: string) => ({
@@ -14,9 +14,9 @@ const summary = (id: string) => ({
owner: '19012345601', owner: '19012345601',
}); });
function setup(adapter: Partial<ApplicationsAdapter>): AdminCasesStore { function setup(adapter: Partial<AanvragenAdapter>): AdminCasesStore {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
providers: [{ provide: ApplicationsAdapter, useValue: adapter }], providers: [{ provide: AanvragenAdapter, useValue: adapter }],
}); });
return TestBed.inject(AdminCasesStore); return TestBed.inject(AdminCasesStore);
} }
@@ -2,16 +2,13 @@ import { Injectable, inject, signal } from '@angular/core';
import { RemoteData } from '@shared/application/remote-data'; import { RemoteData } from '@shared/application/remote-data';
import { runSubmit, SUBMIT_FAILED } from '@shared/application/submit'; import { runSubmit, SUBMIT_FAILED } from '@shared/application/submit';
import { Aanvraag } from '@registratie/domain/aanvraag'; import { Aanvraag } from '@registratie/domain/aanvraag';
import { import { AanvragenAdapter, parseAanvragen } from '@registratie/infrastructure/aanvragen.adapter';
ApplicationsAdapter,
parseApplications,
} from '@registratie/infrastructure/applications.adapter';
type Err = Error | undefined; type Err = Error | undefined;
/** /**
* Admin view of ALL cases across owners (WP-36; `cases:manage`) — the back-office * Admin view of ALL cases across owners (WP-36; `cases:manage`) — the back-office
* counterpart of the user-facing `ApplicationsStore`. Same shape: one root singleton * counterpart of the user-facing `AanvragenStore`. Same shape: one root singleton
* owns the list as a writable RemoteData signal, delete removes the row synchronously * owns the list as a writable RemoteData signal, delete removes the row synchronously
* (optimistic), goes through `runSubmit`, and rolls back plus surfaces `lastError` on * (optimistic), goes through `runSubmit`, and rolls back plus surfaces `lastError` on
* failure (RB-20). Admin delete removes any case (any owner, submitted or not — the * failure (RB-20). Admin delete removes any case (any owner, submitted or not — the
@@ -19,7 +16,7 @@ type Err = Error | undefined;
*/ */
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class AdminCasesStore { export class AdminCasesStore {
private adapter = inject(ApplicationsAdapter); private adapter = inject(AanvragenAdapter);
private state = signal<RemoteData<Err, Aanvraag[]>>({ tag: 'Loading' }); private state = signal<RemoteData<Err, Aanvraag[]>>({ tag: 'Loading' });
readonly cases = this.state.asReadonly(); readonly cases = this.state.asReadonly();
@@ -34,7 +31,7 @@ export class AdminCasesStore {
async load() { async load() {
if (this.state().tag !== 'Success') this.state.set({ tag: 'Loading' }); if (this.state().tag !== 'Success') this.state.set({ tag: 'Loading' });
try { try {
const parsed = parseApplications(await this.adapter.listAll()); const parsed = parseAanvragen(await this.adapter.listAll());
this.state.set( this.state.set(
parsed.ok parsed.ok
? { tag: 'Success', value: parsed.value } ? { tag: 'Success', value: parsed.value }
@@ -2,14 +2,14 @@ import { ApplicationRef, signal } from '@angular/core';
import { TestBed } from '@angular/core/testing'; import { TestBed } from '@angular/core/testing';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { ApplicationsAdapter } from '@registratie/infrastructure/applications.adapter'; import { AanvragenAdapter } from '@registratie/infrastructure/aanvragen.adapter';
import { createDraftSync, DraftSnapshot } from './draft-sync'; import { createDraftSync, DraftSnapshot } from './draft-sync';
function setup(adapter: Partial<ApplicationsAdapter>) { function setup(adapter: Partial<AanvragenAdapter>) {
const navigate = vi.fn().mockResolvedValue(true); const navigate = vi.fn().mockResolvedValue(true);
TestBed.configureTestingModule({ TestBed.configureTestingModule({
providers: [ providers: [
{ provide: ApplicationsAdapter, useValue: adapter }, { provide: AanvragenAdapter, useValue: adapter },
{ provide: Router, useValue: { navigate } }, { provide: Router, useValue: { navigate } },
{ provide: ActivatedRoute, useValue: { snapshot: { queryParamMap: { get: () => null } } } }, { provide: ActivatedRoute, useValue: { snapshot: { queryParamMap: { get: () => null } } } },
], ],
@@ -4,11 +4,11 @@ import { Result } from '@shared/kernel/fp';
import { runSubmit, SUBMIT_FAILED } from '@shared/application/submit'; import { runSubmit, SUBMIT_FAILED } from '@shared/application/submit';
import { registerPendingSave } from '@shared/application/pending-saves'; import { registerPendingSave } from '@shared/application/pending-saves';
import type { import type {
SubmitApplicationRequest, AanvraagIndienenRequest,
SubmitApplicationResponse, AanvraagIndienenResponse,
} from '@shared/infrastructure/api-client'; } from '@shared/infrastructure/api-client';
import { AanvraagType } from '@registratie/domain/aanvraag'; import { AanvraagType } from '@registratie/domain/aanvraag';
import { ApplicationsAdapter } from '@registratie/infrastructure/applications.adapter'; import { AanvragenAdapter } from '@registratie/infrastructure/aanvragen.adapter';
import { findConcept, loadConcept } from './find-concept'; import { findConcept, loadConcept } from './find-concept';
/** What a wizard persists per step: the opaque machine snapshot + progress + docs. */ /** What a wizard persists per step: the opaque machine snapshot + progress + docs. */
@@ -46,7 +46,7 @@ const DEBOUNCE_MS = 600; // ponytail: fixed debounce; tune if the sync feels lag
* Inert without a Router (stories) or when `enabled()` is false — no network, no resume. * Inert without a Router (stories) or when `enabled()` is false — no network, no resume.
*/ */
export function createDraftSync(deps: DraftSyncDeps) { export function createDraftSync(deps: DraftSyncDeps) {
const adapter = inject(ApplicationsAdapter); const adapter = inject(AanvragenAdapter);
const router = inject(Router, { optional: true }); const router = inject(Router, { optional: true });
const route = inject(ActivatedRoute, { optional: true }); const route = inject(ActivatedRoute, { optional: true });
const active = () => deps.enabled() && !!router && !!route; const active = () => deps.enabled() && !!router && !!route;
@@ -190,7 +190,7 @@ export function createDraftSync(deps: DraftSyncDeps) {
/** Submit through the aanvraag lifecycle: ensure the Concept exists, then /** Submit through the aanvraag lifecycle: ensure the Concept exists, then
`POST /applications/{id}/submit` (server sets autoApprovable + transitions). `POST /applications/{id}/submit` (server sets autoApprovable + transitions).
Folded into a Result like the old submit-* commands. */ Folded into a Result like the old submit-* commands. */
submit(body: SubmitApplicationRequest): Promise<Result<string, SubmitApplicationResponse>> { submit(body: AanvraagIndienenRequest): Promise<Result<string, AanvraagIndienenResponse>> {
return runSubmit(async () => adapter.submit(await ensureId(), body), SUBMIT_FAILED); return runSubmit(async () => adapter.submit(await ensureId(), body), SUBMIT_FAILED);
}, },
@@ -1,11 +1,11 @@
import { describe, it, expect } from 'vitest'; import { describe, it, expect } from 'vitest';
import { ApplicationsAdapter } from '@registratie/infrastructure/applications.adapter'; import { AanvragenAdapter } from '@registratie/infrastructure/aanvragen.adapter';
import { findConcept, loadConcept } from './find-concept'; import { findConcept, loadConcept } from './find-concept';
// Free functions taking the adapter as a parameter (no inject()) — a plain fake // Free functions taking the adapter as a parameter (no inject()) — a plain fake
// object is enough, no Angular TestBed needed. // object is enough, no Angular TestBed needed.
function fakeAdapter(overrides: Partial<ApplicationsAdapter>): ApplicationsAdapter { function fakeAdapter(overrides: Partial<AanvragenAdapter>): AanvragenAdapter {
return overrides as ApplicationsAdapter; return overrides as AanvragenAdapter;
} }
describe('findConcept', () => { describe('findConcept', () => {
@@ -1,8 +1,5 @@
import { AanvraagType } from '@registratie/domain/aanvraag'; import { AanvraagType } from '@registratie/domain/aanvraag';
import { import { AanvragenAdapter, parseAanvragen } from '@registratie/infrastructure/aanvragen.adapter';
ApplicationsAdapter,
parseApplications,
} from '@registratie/infrastructure/applications.adapter';
/** /**
* Read half of the Concept lookup that `createDraftSync` (`draft-sync.ts`) needs * Read half of the Concept lookup that `createDraftSync` (`draft-sync.ts`) needs
@@ -14,11 +11,11 @@ import {
/** Find the user's existing Concept of a given type (at most one), if any. */ /** Find the user's existing Concept of a given type (at most one), if any. */
export async function findConcept( export async function findConcept(
adapter: ApplicationsAdapter, adapter: AanvragenAdapter,
type: AanvraagType, type: AanvraagType,
): Promise<string | undefined> { ): Promise<string | undefined> {
try { try {
const parsed = parseApplications(await adapter.list()); const parsed = parseAanvragen(await adapter.list());
return parsed.ok return parsed.ok
? parsed.value.find((a) => a.type === type && a.status.tag === 'Concept')?.id ? parsed.value.find((a) => a.type === type && a.status.tag === 'Concept')?.id
: undefined; : undefined;
@@ -34,10 +31,7 @@ export async function findConcept(
export type LoadedConcept = { tag: 'concept'; draft: unknown | null } | { tag: 'not-concept' }; export type LoadedConcept = { tag: 'concept'; draft: unknown | null } | { tag: 'not-concept' };
/** Load a specific Concept by id and report whether it is still editable. */ /** Load a specific Concept by id and report whether it is still editable. */
export async function loadConcept( export async function loadConcept(adapter: AanvragenAdapter, id: string): Promise<LoadedConcept> {
adapter: ApplicationsAdapter,
id: string,
): Promise<LoadedConcept> {
try { try {
const dto = await adapter.detail(id); const dto = await adapter.detail(id);
if (dto.status && dto.status.tag !== 'Concept') return { tag: 'not-concept' }; if (dto.status && dto.status.tag !== 'Concept') return { tag: 'not-concept' };
@@ -1,10 +1,10 @@
import { describe, it, expect } from 'vitest'; import { describe, it, expect } from 'vitest';
import { import {
parseAanvraagStatus, parseAanvraagStatus,
parseApplicationSummary, parseAanvraagSummary,
parseApplications, parseAanvragen,
parseApplicationDetail, parseAanvraagDetail,
} from './applications.adapter'; } from './aanvragen.adapter';
const concept = { const concept = {
id: 'a1', id: 'a1',
@@ -42,29 +42,29 @@ describe('parseAanvraagStatus', () => {
}); });
}); });
describe('parseApplicationSummary', () => { describe('parseAanvraagSummary', () => {
it('maps a valid DTO to domain', () => { it('maps a valid DTO to domain', () => {
const r = parseApplicationSummary(concept); const r = parseAanvraagSummary(concept);
expect(r.ok && r.value.type).toBe('registratie'); expect(r.ok && r.value.type).toBe('registratie');
expect(r.ok && r.value.status.tag).toBe('Concept'); expect(r.ok && r.value.status.tag).toBe('Concept');
}); });
it('rejects a bad type and non-objects', () => { it('rejects a bad type and non-objects', () => {
expect(parseApplicationSummary({ ...concept, type: 'onbekend' }).ok).toBe(false); expect(parseAanvraagSummary({ ...concept, type: 'onbekend' }).ok).toBe(false);
expect(parseApplicationSummary(null).ok).toBe(false); expect(parseAanvraagSummary(null).ok).toBe(false);
expect(parseApplicationSummary({ ...concept, id: 42 }).ok).toBe(false); expect(parseAanvraagSummary({ ...concept, id: 42 }).ok).toBe(false);
}); });
}); });
describe('parseApplications / parseApplicationDetail', () => { describe('parseAanvragen / parseAanvraagDetail', () => {
it('parses a list and fails fast on a bad element', () => { it('parses a list and fails fast on a bad element', () => {
expect(parseApplications([concept, concept]).ok).toBe(true); expect(parseAanvragen([concept, concept]).ok).toBe(true);
expect(parseApplications([concept, { ...concept, status: { tag: 'x' } }]).ok).toBe(false); expect(parseAanvragen([concept, { ...concept, status: { tag: 'x' } }]).ok).toBe(false);
expect(parseApplications({}).ok).toBe(false); expect(parseAanvragen({}).ok).toBe(false);
}); });
it('carries the opaque draft through detail', () => { it('carries the opaque draft through detail', () => {
const r = parseApplicationDetail({ ...concept, draft: { beroep: 'arts' } }); const r = parseAanvraagDetail({ ...concept, draft: { beroep: 'arts' } });
expect(r.ok && (r.value.draft as { beroep: string }).beroep).toBe('arts'); expect(r.ok && (r.value.draft as { beroep: string }).beroep).toBe('arts');
}); });
}); });
@@ -3,11 +3,11 @@ import { Result, ok, err } from '@shared/kernel/fp';
import { import {
ApiClient, ApiClient,
AanvraagStatusDto, AanvraagStatusDto,
ApplicationSummaryDto, AanvraagSummaryDto,
ApplicationDetailDto, AanvraagDetailDto,
DraftSyncRequest, DraftSyncRequest,
SubmitApplicationRequest, AanvraagIndienenRequest,
SubmitApplicationResponse, AanvraagIndienenResponse,
} from '@shared/infrastructure/api-client'; } from '@shared/infrastructure/api-client';
import { import {
Aanvraag, Aanvraag,
@@ -19,21 +19,21 @@ import {
/** /**
* Infrastructure adapter for the backend-owned Aanvraag aggregate — the only place * Infrastructure adapter for the backend-owned Aanvraag aggregate — the only place
* its HTTP lives (ADR-0001 anti-corruption boundary). The list is a resource; the * its HTTP lives (ADR-0001 anti-corruption boundary). The list is a resource; the
* mutations (create/sync/cancel/submit) are thin commands the ApplicationsStore * mutations (create/sync/cancel/submit) are thin commands the AanvragenStore
* orchestrates optimistically. The untrusted response is validated + mapped to * orchestrates optimistically. The untrusted response is validated + mapped to
* domain by the hand-written parse* boundary below. * domain by the hand-written parse* boundary below.
*/ */
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class ApplicationsAdapter { export class AanvragenAdapter {
private client = inject(ApiClient); private client = inject(ApiClient);
/** The dashboard's application list (raw DTOs; the store parses at the boundary). */ /** The dashboard's aanvraag list (raw DTOs; the store parses at the boundary). */
list(): Promise<ApplicationSummaryDto[]> { list(): Promise<AanvraagSummaryDto[]> {
return this.client.applicationsAll(); return this.client.aanvragenAll();
} }
/** Admin: every case across all owners (WP-36; `cases:manage`). Parsed at the boundary. */ /** Admin: every case across all owners (WP-36; `cases:manage`). Parsed at the boundary. */
listAll(): Promise<ApplicationSummaryDto[]> { listAll(): Promise<AanvraagSummaryDto[]> {
return this.client.casesAll(); return this.client.casesAll();
} }
@@ -42,26 +42,26 @@ export class ApplicationsAdapter {
return this.client.cases(id); return this.client.cases(id);
} }
detail(id: string): Promise<ApplicationDetailDto> { detail(id: string): Promise<AanvraagDetailDto> {
return this.client.applicationsGET(id); return this.client.aanvragenGET(id);
} }
/** Create a Concept for a wizard type; resolves to the new aanvraag id. */ /** Create a Concept for a wizard type; resolves to the new aanvraag id. */
create(type: AanvraagType): Promise<string> { create(type: AanvraagType): Promise<string> {
return this.client.applicationsPOST({ type }).then((d) => d.id ?? ''); return this.client.aanvragenPOST({ type }).then((d) => d.id ?? '');
} }
/** Draft sync per step (idempotent). Keep it debounced at the call site — it is chatty. */ /** Draft sync per step (idempotent). Keep it debounced at the call site — it is chatty. */
syncDraft(id: string, body: DraftSyncRequest): Promise<void> { syncDraft(id: string, body: DraftSyncRequest): Promise<void> {
return this.client.applicationsPUT(id, body); return this.client.aanvragenPUT(id, body);
} }
/** Cancel a Concept (cascades to its unlinked documents server-side). */ /** Cancel a Concept (cascades to its unlinked documents server-side). */
cancel(id: string): Promise<void> { cancel(id: string): Promise<void> {
return this.client.applicationsDELETE(id); return this.client.aanvragenDELETE(id);
} }
submit(id: string, body: SubmitApplicationRequest): Promise<SubmitApplicationResponse> { submit(id: string, body: AanvraagIndienenRequest): Promise<AanvraagIndienenResponse> {
return this.client.submit(id, body); return this.client.submit(id, body);
} }
} }
@@ -101,7 +101,7 @@ export function parseAanvraagStatus(
} }
} }
function parseCommon(dto: ApplicationSummaryDto): Result<string, Aanvraag> { function parseCommon(dto: AanvraagSummaryDto): Result<string, Aanvraag> {
if (typeof dto.id !== 'string') return err('aanvraag: missing id'); if (typeof dto.id !== 'string') return err('aanvraag: missing id');
if (typeof dto.type !== 'string' || !AANVRAAG_TYPES.includes(dto.type)) if (typeof dto.type !== 'string' || !AANVRAAG_TYPES.includes(dto.type))
return err(`aanvraag: bad type ${dto.type}`); return err(`aanvraag: bad type ${dto.type}`);
@@ -121,25 +121,25 @@ function parseCommon(dto: ApplicationSummaryDto): Result<string, Aanvraag> {
}); });
} }
export function parseApplicationSummary(json: unknown): Result<string, Aanvraag> { export function parseAanvraagSummary(json: unknown): Result<string, Aanvraag> {
if (typeof json !== 'object' || json === null) return err('aanvraag: not an object'); if (typeof json !== 'object' || json === null) return err('aanvraag: not an object');
return parseCommon(json as ApplicationSummaryDto); return parseCommon(json as AanvraagSummaryDto);
} }
export function parseApplications(json: unknown): Result<string, Aanvraag[]> { export function parseAanvragen(json: unknown): Result<string, Aanvraag[]> {
if (!Array.isArray(json)) return err('aanvragen: not an array'); if (!Array.isArray(json)) return err('aanvragen: not an array');
const out: Aanvraag[] = []; const out: Aanvraag[] = [];
for (const item of json) { for (const item of json) {
const parsed = parseApplicationSummary(item); const parsed = parseAanvraagSummary(item);
if (!parsed.ok) return parsed; if (!parsed.ok) return parsed;
out.push(parsed.value); out.push(parsed.value);
} }
return ok(out); return ok(out);
} }
export function parseApplicationDetail(json: unknown): Result<string, AanvraagDetail> { export function parseAanvraagDetail(json: unknown): Result<string, AanvraagDetail> {
if (typeof json !== 'object' || json === null) return err('aanvraag: not an object'); if (typeof json !== 'object' || json === null) return err('aanvraag: not an object');
const base = parseCommon(json as ApplicationDetailDto); const base = parseCommon(json as AanvraagDetailDto);
if (!base.ok) return base; if (!base.ok) return base;
return ok({ ...base.value, draft: (json as ApplicationDetailDto).draft ?? null }); return ok({ ...base.value, draft: (json as AanvraagDetailDto).draft ?? null });
} }
@@ -6,7 +6,7 @@ import { AlertComponent } from '@shared/ui/alert/alert.component';
import { DataBlockComponent } from '@shared/ui/data-block/data-block.component'; import { DataBlockComponent } from '@shared/ui/data-block/data-block.component';
import { DataRowComponent } from '@shared/ui/data-row/data-row.component'; import { DataRowComponent } from '@shared/ui/data-row/data-row.component';
import { ASYNC } from '@shared/ui/async/async.component'; import { ASYNC } from '@shared/ui/async/async.component';
import { ApplicationsStore } from '@registratie/application/applications.store'; import { AanvragenStore } from '@registratie/application/aanvragen.store';
import { Aanvraag } from '@registratie/domain/aanvraag'; import { Aanvraag } from '@registratie/domain/aanvraag';
import { detailRows } from '@registratie/domain/aanvraag-view'; import { detailRows } from '@registratie/domain/aanvraag-view';
@@ -29,9 +29,9 @@ import { detailRows } from '@registratie/domain/aanvraag-view';
heading="Aanvraag" heading="Aanvraag"
backLink="/dashboard" backLink="/dashboard"
> >
<app-async [data]="store.applications()"> <app-async [data]="store.aanvragen()">
<ng-template appAsyncLoaded> <ng-template appAsyncLoaded>
@if (applications(); as list) { @if (aanvragen(); as list) {
@let a = find(list); @let a = find(list);
@if (a) { @if (a) {
<app-data-block <app-data-block
@@ -60,15 +60,15 @@ import { detailRows } from '@registratie/domain/aanvraag-view';
`, `,
}) })
export class AanvraagDetailPage { export class AanvraagDetailPage {
protected store = inject(ApplicationsStore); protected store = inject(AanvragenStore);
private id = inject(ActivatedRoute).snapshot.paramMap.get('id') ?? ''; private id = inject(ActivatedRoute).snapshot.paramMap.get('id') ?? '';
protected find = (list: Aanvraag[]): Aanvraag | undefined => list.find((a) => a.id === this.id); protected find = (list: Aanvraag[]): Aanvraag | undefined => list.find((a) => a.id === this.id);
protected rows = detailRows; protected rows = detailRows;
/** See DashboardPage's `profile` for why this narrows via a computed instead of `let-`. */ /** See DashboardPage's `profile` for why this narrows via a computed instead of `let-`. */
protected readonly applications = computed(() => { protected readonly aanvragen = computed(() => {
const rd = this.store.applications(); const rd = this.store.aanvragen();
return rd.tag === 'Success' ? rd.value : undefined; return rd.tag === 'Success' ? rd.value : undefined;
}); });
} }
@@ -18,7 +18,7 @@ import { RegistrationSummaryComponent } from '@registratie/ui/registration-summa
import { RegistrationTableComponent } from '@registratie/ui/registration-table/registration-table.component'; import { RegistrationTableComponent } from '@registratie/ui/registration-table/registration-table.component';
import { AanvraagBlockComponent } from '@registratie/ui/aanvraag-block/aanvraag-block.component'; import { AanvraagBlockComponent } from '@registratie/ui/aanvraag-block/aanvraag-block.component';
import { BigProfileStore } from '@registratie/application/big-profile.store'; import { BigProfileStore } from '@registratie/application/big-profile.store';
import { ApplicationsStore } from '@registratie/application/applications.store'; import { AanvragenStore } from '@registratie/application/aanvragen.store';
import { Registration } from '@registratie/domain/registration'; import { Registration } from '@registratie/domain/registration';
import { Aanvraag, AanvraagType } from '@registratie/domain/aanvraag'; import { Aanvraag, AanvraagType } from '@registratie/domain/aanvraag';
import { submittedRow } from '@registratie/domain/aanvraag-view'; import { submittedRow } from '@registratie/domain/aanvraag-view';
@@ -214,7 +214,7 @@ import { tasksFromProfile } from '@registratie/domain/tasks';
}) })
export class DashboardPage { export class DashboardPage {
protected store = inject(BigProfileStore); protected store = inject(BigProfileStore);
private apps = inject(ApplicationsStore); private apps = inject(AanvragenStore);
private access = inject(AccessStore); private access = inject(AccessStore);
private flags = inject(FeatureFlagStore); private flags = inject(FeatureFlagStore);
private router = inject(Router); private router = inject(Router);
@@ -232,10 +232,10 @@ export class DashboardPage {
this.apps.reload(); this.apps.reload();
} }
/** The user's applications, sorted Concept → In behandeling → resolved. Empty → /** The user's aanvragen, sorted Concept → In behandeling → resolved. Empty →
the"Mijn aanvragen" section is hidden (see template). */ the"Mijn aanvragen" section is hidden (see template). */
protected aanvragen = computed<Aanvraag[]>(() => { protected aanvragen = computed<Aanvraag[]>(() => {
const rd = this.apps.applications(); const rd = this.apps.aanvragen();
if (rd.tag !== 'Success') return []; if (rd.tag !== 'Success') return [];
const order: Record<Aanvraag['status']['tag'], number> = { const order: Record<Aanvraag['status']['tag'], number> = {
Concept: 0, Concept: 0,
@@ -99,19 +99,19 @@ public sealed record AanvraagStatusDto(
bool? Manual = null, bool? Manual = null,
string? Reden = null); string? Reden = null);
public sealed record ApplicationSummaryDto( public sealed record AanvraagSummaryDto(
string Id, string Type, AanvraagStatusDto Status, string Id, string Type, AanvraagStatusDto Status,
IReadOnlyList<string> DocumentIds, IReadOnlyList<string> DocumentIds,
string CreatedAt, string UpdatedAt, string? SubmittedAt, string CreatedAt, string UpdatedAt, string? SubmittedAt,
string? Owner = null); // populated for the admin cross-owner list (WP-36); the user's own list ignores it string? Owner = null); // populated for the admin cross-owner list (WP-36); the user's own list ignores it
public sealed record ApplicationDetailDto( public sealed record AanvraagDetailDto(
string Id, string Type, AanvraagStatusDto Status, string Id, string Type, AanvraagStatusDto Status,
System.Text.Json.JsonElement? Draft, System.Text.Json.JsonElement? Draft,
IReadOnlyList<string> DocumentIds, IReadOnlyList<string> DocumentIds,
string CreatedAt, string UpdatedAt, string? SubmittedAt); string CreatedAt, string UpdatedAt, string? SubmittedAt);
public sealed record CreateApplicationRequest(string Type); public sealed record CreateAanvraagRequest(string Type);
public sealed record DraftSyncRequest( public sealed record DraftSyncRequest(
System.Text.Json.JsonElement Draft, int StepIndex, int StepCount, System.Text.Json.JsonElement Draft, int StepIndex, int StepCount,
@@ -120,12 +120,12 @@ public sealed record DraftSyncRequest(
// Submit carries only the fields the server re-validates per wizard type. // Submit carries only the fields the server re-validates per wizard type.
// AanvullendeScholing/ScholingPunten (WP-69) — intake-typed aanvragen only (gated by // AanvullendeScholing/ScholingPunten (WP-69) — intake-typed aanvragen only (gated by
// IntakePolicy.RejectIncompleteScholing's caller), null for the others. // IntakePolicy.RejectIncompleteScholing's caller), null for the others.
public sealed record SubmitApplicationRequest( public sealed record AanvraagIndienenRequest(
string? DiplomaHerkomst = null, int? Uren = null, string? DiplomaHerkomst = null, int? Uren = null,
IReadOnlyList<DocumentRefDto>? Documents = null, IReadOnlyList<DocumentRefDto>? Documents = null,
bool? AanvullendeScholing = null, int? ScholingPunten = null); bool? AanvullendeScholing = null, int? ScholingPunten = null);
public sealed record SubmitApplicationResponse(string Referentie, AanvraagStatusDto Status); public sealed record AanvraagIndienenResponse(string Referentie, AanvraagStatusDto Status);
// --- Beoordeling (WP-65): the behandelportal's case-detail screen. --- // --- Beoordeling (WP-65): the behandelportal's case-detail screen. ---
@@ -137,7 +137,7 @@ public sealed record BeoordelingDocumentDto(string DocumentId, string CategoryId
public sealed record BeoordelingDecisionsDto(bool CanBesluiten); public sealed record BeoordelingDecisionsDto(bool CanBesluiten);
public sealed record BeoordelingViewDto( public sealed record BeoordelingViewDto(
ApplicationSummaryDto Aanvraag, AanvraagSummaryDto Aanvraag,
IReadOnlyList<BeoordelingDocumentDto> Documenten, IReadOnlyList<BeoordelingDocumentDto> Documenten,
BeoordelingDecisionsDto Decisions); BeoordelingDecisionsDto Decisions);
@@ -65,7 +65,7 @@ public static class Mappers
/// reads it past that point; see <c>AanvraagMapper.ApplyTo</c>'s Submitted branch).</summary> /// reads it past that point; see <c>AanvraagMapper.ApplyTo</c>'s Submitted branch).</summary>
private static JsonElement? DraftOf(Aanvraag a) => a is Aanvraag.Concept c ? c.Draft : null; private static JsonElement? DraftOf(Aanvraag a) => a is Aanvraag.Concept c ? c.Draft : null;
public static ApplicationSummaryDto ToSummaryDto(this Aanvraag a, DateTimeOffset now) => new( public static AanvraagSummaryDto ToSummaryDto(this Aanvraag a, DateTimeOffset now) => new(
a.Id, a.Type, a.ToStatusDto(now), a.DocumentIds, a.Id, a.Type, a.ToStatusDto(now), a.DocumentIds,
a.CreatedAt.ToString("o"), a.UpdatedAt.ToString("o"), SubmittedAtOf(a)); a.CreatedAt.ToString("o"), a.UpdatedAt.ToString("o"), SubmittedAtOf(a));
@@ -74,10 +74,10 @@ public static class Mappers
/// someone who is not the subject (`/admin/cases`, `/werkvoorraad`), so it goes out masked /// someone who is not the subject (`/admin/cases`, `/werkvoorraad`), so it goes out masked
/// (RB-03/BIO-003). Masking here rather than at each endpoint means a third cross-owner /// (RB-03/BIO-003). Masking here rather than at each endpoint means a third cross-owner
/// list cannot be added that forgets to. /// list cannot be added that forgets to.
public static ApplicationSummaryDto ToAdminSummaryDto(this Aanvraag a, DateTimeOffset now) => public static AanvraagSummaryDto ToAdminSummaryDto(this Aanvraag a, DateTimeOffset now) =>
a.ToSummaryDto(now) with { Owner = Pii.MaskTail(a.Owner, 3) }; a.ToSummaryDto(now) with { Owner = Pii.MaskTail(a.Owner, 3) };
public static ApplicationDetailDto ToDetailDto(this Aanvraag a, DateTimeOffset now) => new( public static AanvraagDetailDto ToDetailDto(this Aanvraag a, DateTimeOffset now) => new(
a.Id, a.Type, a.ToStatusDto(now), DraftOf(a), a.DocumentIds, a.Id, a.Type, a.ToStatusDto(now), DraftOf(a), a.DocumentIds,
a.CreatedAt.ToString("o"), a.UpdatedAt.ToString("o"), SubmittedAtOf(a)); a.CreatedAt.ToString("o"), a.UpdatedAt.ToString("o"), SubmittedAtOf(a));
} }
@@ -43,7 +43,7 @@ public static class DocumentStore
/// SeedData.Registration.BigNummer ("19012345601", 11 digits — the seeded doctor's BIG-nummer, /// SeedData.Registration.BigNummer ("19012345601", 11 digits — the seeded doctor's BIG-nummer,
/// a different Dutch identifier scheme). Previously this constant reused that BigNummer value /// a different Dutch identifier scheme). Previously this constant reused that BigNummer value
/// as a stand-in BSN, which is invalid Dutch-BSN shape: harmless against the local store, but /// as a stand-in BSN, which is invalid Dutch-BSN shape: harmless against the local store, but
/// a real OpenZaak instance rejects it outright — GET /api/v1/applications 500s (`inpBsn` query /// a real OpenZaak instance rejects it outright — GET /api/v1/aanvragen 500s (`inpBsn` query
/// filter validation) and every submit's rol-creation POST fails (`inpBsn` max_length) once /// filter validation) and every submit's rol-creation POST fails (`inpBsn` max_length) once
/// Zgw:Enabled=true. Not "111222333" or "999888777" — both already mean a different fixture /// Zgw:Enabled=true. Not "111222333" or "999888777" — both already mean a different fixture
/// identity (the OpenZaak-harness/unit-test caller, and ApplicationTests' "other citizen"). /// identity (the OpenZaak-harness/unit-test caller, and ApplicationTests' "other citizen").
@@ -7,7 +7,7 @@ namespace BigRegister.Api.Data;
/// <summary> /// <summary>
/// The cases (zaken) READ seam (WP-49). A "zaak" in ZGW terms is an <see cref="Aanvraag"/> /// The cases (zaken) READ seam (WP-49). A "zaak" in ZGW terms is an <see cref="Aanvraag"/>
/// here; this interface is the one injection point that lets a real ZGW backend (OpenZaak) /// here; this interface is the one injection point that lets a real ZGW backend (OpenZaak)
/// replace the local SQLite store <em>behind the same <see cref="ApplicationSummaryDto"/> /// replace the local SQLite store <em>behind the same <see cref="AanvraagSummaryDto"/>
/// contract</em> — so the frontend never changes (BFF-lite anti-corruption, ADR-0001). /// contract</em> — so the frontend never changes (BFF-lite anti-corruption, ADR-0001).
/// ///
/// Default binding is <see cref="LocalZaakSource"/> (offline). Setting <c>Zgw:Enabled=true</c> /// Default binding is <see cref="LocalZaakSource"/> (offline). Setting <c>Zgw:Enabled=true</c>
@@ -20,7 +20,7 @@ public interface IZaakSource
{ {
/// <summary>Every case across every owner, newest-first (the admin cross-owner list, /// <summary>Every case across every owner, newest-first (the admin cross-owner list,
/// WP-36) — cases:manage only, deliberately NOT citizen-scoped.</summary> /// WP-36) — cases:manage only, deliberately NOT citizen-scoped.</summary>
IReadOnlyList<ApplicationSummaryDto> ListCases(DateTimeOffset now); IReadOnlyList<AanvraagSummaryDto> ListCases(DateTimeOffset now);
/// <summary> /// <summary>
/// Only <paramref name="caller"/>'s own cases (WP-53) — the citizen-scoped counterpart of /// Only <paramref name="caller"/>'s own cases (WP-53) — the citizen-scoped counterpart of
@@ -29,7 +29,7 @@ public interface IZaakSource
/// <c>rol__betrokkeneIdentificatie__natuurlijkPersoon__inpBsn</c> query filter so a citizen /// <c>rol__betrokkeneIdentificatie__natuurlijkPersoon__inpBsn</c> query filter so a citizen
/// can never see another citizen's zaken. /// can never see another citizen's zaken.
/// </summary> /// </summary>
IReadOnlyList<ApplicationSummaryDto> ListMyCases(ZorgverlenerCaller caller, DateTimeOffset now); IReadOnlyList<AanvraagSummaryDto> ListMyCases(ZorgverlenerCaller caller, DateTimeOffset now);
/// <summary> /// <summary>
/// Register a just-submitted <paramref name="aanvraag"/> as a zaak (WP-50). The aanvraag is /// Register a just-submitted <paramref name="aanvraag"/> as a zaak (WP-50). The aanvraag is
@@ -12,12 +12,12 @@ namespace BigRegister.Api.Data;
/// </summary> /// </summary>
public sealed class LocalZaakSource : IZaakSource public sealed class LocalZaakSource : IZaakSource
{ {
public IReadOnlyList<ApplicationSummaryDto> ListCases(DateTimeOffset now) => public IReadOnlyList<AanvraagSummaryDto> ListCases(DateTimeOffset now) =>
ApplicationStore.ListAll().Select(a => a.ToAdminSummaryDto(now)).ToList(); ApplicationStore.ListAll().Select(a => a.ToAdminSummaryDto(now)).ToList();
/// <summary>Citizen-scoped (WP-53) — exactly what <c>GET /applications</c> used to compute /// <summary>Citizen-scoped (WP-53) — exactly what <c>GET /aanvragen</c> used to compute
/// inline before it was routed through this seam.</summary> /// inline before it was routed through this seam.</summary>
public IReadOnlyList<ApplicationSummaryDto> ListMyCases(ZorgverlenerCaller caller, DateTimeOffset now) => public IReadOnlyList<AanvraagSummaryDto> ListMyCases(ZorgverlenerCaller caller, DateTimeOffset now) =>
ApplicationStore.List(caller.Bsn) ApplicationStore.List(caller.Bsn)
.OrderByDescending(a => a.UpdatedAt) .OrderByDescending(a => a.UpdatedAt)
.Select(a => a.ToSummaryDto(now)).ToList(); .Select(a => a.ToSummaryDto(now)).ToList();
@@ -9,7 +9,7 @@ public sealed record FeatureFlagDef(string Key, string Description, bool Default
public static class FeatureFlags public static class FeatureFlags
{ {
/// Whether self-service registration (inschrijving) is open. When off, the FE hides the /// Whether self-service registration (inschrijving) is open. When off, the FE hides the
/// "Inschrijven" action and POST /applications for a `registratie` is refused (server-enforced). /// "Inschrijven" action and POST /aanvragen for a `registratie` is refused (server-enforced).
public const string InschrijvingOpen = "inschrijving-open"; public const string InschrijvingOpen = "inschrijving-open";
public static readonly IReadOnlyList<FeatureFlagDef> Catalog = new[] public static readonly IReadOnlyList<FeatureFlagDef> Catalog = new[]
@@ -6,7 +6,7 @@ namespace BigRegister.Domain.Intake;
/// (<c>GET /intake/policy</c>) and applies it for instant UX feedback /// (<c>GET /intake/policy</c>) and applies it for instant UX feedback
/// (<c>intake.machine.ts</c>'s <c>lageUren</c>); <see cref="RejectIncompleteScholing"/> is the /// (<c>intake.machine.ts</c>'s <c>lageUren</c>); <see cref="RejectIncompleteScholing"/> is the
/// backend re-validating it as the authority on submit (WP-69) — /// backend re-validating it as the authority on submit (WP-69) —
/// <c>POST /applications/{id}/submit</c> (intake-typed aanvragen only) calls it before /// <c>POST /aanvragen/{id}/submit</c> (intake-typed aanvragen only) calls it before
/// writing anything, and a violation 400s (<c>ProblemDetails</c>), never silently accepts /// writing anything, and a violation 400s (<c>ProblemDetails</c>), never silently accepts
/// an incomplete answer. /// an incomplete answer.
/// </summary> /// </summary>
+15 -15
View File
@@ -342,19 +342,19 @@ api.MapDelete("/admin/uploads/{documentId}", (string documentId, HttpContext ctx
// ApplicationStore directly — under Zgw:Enabled=true a citizen's own dashboard list comes from // ApplicationStore directly — under Zgw:Enabled=true a citizen's own dashboard list comes from
// OpenZaak (BSN-filtered) too, closing the last "reads a static store directly" gap // OpenZaak (BSN-filtered) too, closing the last "reads a static store directly" gap
// openzaak-integration.md's ACL caveat used to flag for this endpoint. // openzaak-integration.md's ACL caveat used to flag for this endpoint.
api.MapGet("/applications", (HttpContext ctx, IZaakSource zaken) => api.MapGet("/aanvragen", (HttpContext ctx, IZaakSource zaken) =>
zaken.ListMyCases(ctx.Zorgverlener(), DateTimeOffset.UtcNow)); zaken.ListMyCases(ctx.Zorgverlener(), DateTimeOffset.UtcNow));
api.MapGet("/applications/{id}", (string id, HttpContext ctx) => api.MapGet("/aanvragen/{id}", (string id, HttpContext ctx) =>
ApplicationStore.Get(id, ctx.Zorgverlener().Bsn) is { } a ApplicationStore.Get(id, ctx.Zorgverlener().Bsn) is { } a
? Results.Ok(a.ToDetailDto(DateTimeOffset.UtcNow)) ? Results.Ok(a.ToDetailDto(DateTimeOffset.UtcNow))
: Results.NotFound()) : Results.NotFound())
.Produces<ApplicationDetailDto>() .Produces<AanvraagDetailDto>()
.Produces(StatusCodes.Status404NotFound); .Produces(StatusCodes.Status404NotFound);
// --- writes --- // --- writes ---
api.MapPost("/applications", (CreateApplicationRequest req, HttpContext ctx) => api.MapPost("/aanvragen", (CreateAanvraagRequest req, HttpContext ctx) =>
{ {
// Feature flag (WP-47): self-service registration can be closed by an admin. // Feature flag (WP-47): self-service registration can be closed by an admin.
if (req.Type == "registratie" && !FeatureFlagStore.IsEnabled(FeatureFlags.InschrijvingOpen)) if (req.Type == "registratie" && !FeatureFlagStore.IsEnabled(FeatureFlags.InschrijvingOpen))
@@ -364,13 +364,13 @@ api.MapPost("/applications", (CreateApplicationRequest req, HttpContext ctx) =>
return Results.Problem( return Results.Problem(
detail: "U hebt al een concept van dit type. Rond dat eerst af of verwijder het.", detail: "U hebt al een concept van dit type. Rond dat eerst af of verwijder het.",
statusCode: StatusCodes.Status409Conflict); statusCode: StatusCodes.Status409Conflict);
return Results.Created($"/api/v1/applications/{a.Id}", a.ToDetailDto(DateTimeOffset.UtcNow)); return Results.Created($"/api/v1/aanvragen/{a.Id}", a.ToDetailDto(DateTimeOffset.UtcNow));
}) })
.Produces<ApplicationDetailDto>(StatusCodes.Status201Created) .Produces<AanvraagDetailDto>(StatusCodes.Status201Created)
.ProducesProblem(StatusCodes.Status409Conflict); .ProducesProblem(StatusCodes.Status409Conflict);
// Draft sync per step — idempotent; keep it debounced on the client (it is chatty). // Draft sync per step — idempotent; keep it debounced on the client (it is chatty).
api.MapPut("/applications/{id}", (string id, DraftSyncRequest req, HttpContext ctx) => api.MapPut("/aanvragen/{id}", (string id, DraftSyncRequest req, HttpContext ctx) =>
{ {
var owner = ctx.Zorgverlener().Bsn; var owner = ctx.Zorgverlener().Bsn;
// A citizen may only reference their own uploads in a draft — reject before the sync // A citizen may only reference their own uploads in a draft — reject before the sync
@@ -388,7 +388,7 @@ api.MapPut("/applications/{id}", (string id, DraftSyncRequest req, HttpContext c
// Cancel a Concept (cascades to its unlinked documents). Submitted aanvragen cannot // Cancel a Concept (cascades to its unlinked documents). Submitted aanvragen cannot
// be withdrawn (out of scope — no "intrekken"). // be withdrawn (out of scope — no "intrekken").
api.MapDelete("/applications/{id}", (string id, HttpContext ctx) => api.MapDelete("/aanvragen/{id}", (string id, HttpContext ctx) =>
{ {
var a = ApplicationStore.Get(id, ctx.Zorgverlener().Bsn); var a = ApplicationStore.Get(id, ctx.Zorgverlener().Bsn);
if (a is null) return Results.NotFound(); if (a is null) return Results.NotFound();
@@ -403,7 +403,7 @@ api.MapDelete("/applications/{id}", (string id, HttpContext ctx) =>
// Submit runs the server-owned rules, sets autoApprovable, and transitions the // Submit runs the server-owned rules, sets autoApprovable, and transitions the
// aanvraag. handmatig no longer 422s (ADR-0002): it becomes a manual (pending) case. // aanvraag. handmatig no longer 422s (ADR-0002): it becomes a manual (pending) case.
api.MapPost("/applications/{id}/submit", (string id, SubmitApplicationRequest req, HttpContext ctx, IZaakSource zaken, IDocumentSource documents) => api.MapPost("/aanvragen/{id}/submit", (string id, AanvraagIndienenRequest req, HttpContext ctx, IZaakSource zaken, IDocumentSource documents) =>
{ {
var existing = ApplicationStore.Get(id, ctx.Zorgverlener().Bsn); var existing = ApplicationStore.Get(id, ctx.Zorgverlener().Bsn);
if (existing is null) return Results.NotFound(); if (existing is null) return Results.NotFound();
@@ -480,9 +480,9 @@ api.MapPost("/applications/{id}/submit", (string id, SubmitApplicationRequest re
} }
} }
return Results.Ok(new SubmitApplicationResponse(referentie, status)); return Results.Ok(new AanvraagIndienenResponse(referentie, status));
}) })
.Produces<SubmitApplicationResponse>() .Produces<AanvraagIndienenResponse>()
.ProducesProblem(StatusCodes.Status400BadRequest) .ProducesProblem(StatusCodes.Status400BadRequest)
.ProducesProblem(StatusCodes.Status409Conflict) .ProducesProblem(StatusCodes.Status409Conflict)
.Produces(StatusCodes.Status404NotFound); .Produces(StatusCodes.Status404NotFound);
@@ -494,7 +494,7 @@ api.MapPost("/applications/{id}/submit", (string id, SubmitApplicationRequest re
api.MapGet("/admin/cases", (HttpContext ctx, IZaakSource zaken) => CasesAdmin(ctx, () => api.MapGet("/admin/cases", (HttpContext ctx, IZaakSource zaken) => CasesAdmin(ctx, () =>
Results.Ok(zaken.ListCases(DateTimeOffset.UtcNow)))) Results.Ok(zaken.ListCases(DateTimeOffset.UtcNow))))
.Gate("CasesAdmin") .Gate("CasesAdmin")
.Produces<List<ApplicationSummaryDto>>() .Produces<List<AanvraagSummaryDto>>()
.ProducesProblem(StatusCodes.Status403Forbidden); .ProducesProblem(StatusCodes.Status403Forbidden);
// Queryable authz/PII-reveal audit trail (WP-41) — data-minimised, no PII. Admin-gated // Queryable authz/PII-reveal audit trail (WP-41) — data-minimised, no PII. Admin-gated
@@ -510,7 +510,7 @@ api.MapGet("/admin/audit", (HttpContext ctx) => CasesAdmin(ctx, () =>
// --- writes --- // --- writes ---
// Admin delete removes ANY case (any owner, submitted or not) — unlike the user-facing // Admin delete removes ANY case (any owner, submitted or not) — unlike the user-facing
// DELETE /applications/{id}. A missing id is a 404. // DELETE /aanvragen/{id}. A missing id is a 404.
api.MapDelete("/admin/cases/{id}", (string id, HttpContext ctx) => CasesAdmin(ctx, () => api.MapDelete("/admin/cases/{id}", (string id, HttpContext ctx) => CasesAdmin(ctx, () =>
{ {
if (!ApplicationStore.DeleteAny(id)) return Results.NotFound(); if (!ApplicationStore.DeleteAny(id)) return Results.NotFound();
@@ -531,14 +531,14 @@ api.MapGet("/werkvoorraad", (HttpContext ctx, IZaakSource zaken) => Beoordelen(c
.Where(c => c.Status.Tag is "Ingediend" or "InBehandeling") .Where(c => c.Status.Tag is "Ingediend" or "InBehandeling")
.ToList()))) .ToList())))
.Gate("Beoordelen") .Gate("Beoordelen")
.Produces<List<ApplicationSummaryDto>>() .Produces<List<AanvraagSummaryDto>>()
.ProducesProblem(StatusCodes.Status403Forbidden); .ProducesProblem(StatusCodes.Status403Forbidden);
// --- Beoordeling (WP-65): one aanvraag's case-treatment detail — read side only (recording // --- Beoordeling (WP-65): one aanvraag's case-treatment detail — read side only (recording
// a decision is WP-65's second half). Reads through IZaakSource.ListCases (no new seam method: // a decision is WP-65's second half). Reads through IZaakSource.ListCases (no new seam method:
// adding one now would force an OpenZaak get-by-id + mapper, which is WP-66's surface) — O(n) // adding one now would force an OpenZaak get-by-id + mapper, which is WP-66's surface) — O(n)
// over a POC-sized table. A Concept isn't a case a behandelaar can treat yet, so it 404s here // over a POC-sized table. A Concept isn't a case a behandelaar can treat yet, so it 404s here
// same as an unknown id (only /applications/{id}, citizen-scoped, shows a Concept). // same as an unknown id (only /aanvragen/{id}, citizen-scoped, shows a Concept).
api.MapGet("/beoordeling/{id}", (string id, HttpContext ctx, IZaakSource zaken) => api.MapGet("/beoordeling/{id}", (string id, HttpContext ctx, IZaakSource zaken) =>
Beoordelen(ctx, $"aanvraag/{id}", () => Beoordelen(ctx, $"aanvraag/{id}", () =>
{ {
@@ -18,9 +18,9 @@ public sealed record ZgwPage<T>(
/// The <see cref="IZaakSource"/> backed by a real OpenZaak / ZGW Zaken API (WP-49 read, WP-50 /// The <see cref="IZaakSource"/> backed by a real OpenZaak / ZGW Zaken API (WP-49 read, WP-50
/// write). Reads zaken (following pagination), maps each zaak's zaaktype URL back to the /// write). Reads zaken (following pagination), maps each zaak's zaaktype URL back to the
/// internal aanvraag-type key via <c>Zgw:ZaaktypeUrls</c> (a local lookup — NOT OpenZaak's /// internal aanvraag-type key via <c>Zgw:ZaaktypeUrls</c> (a local lookup — NOT OpenZaak's
/// human zaaktype label, which isn't a value <see cref="ApplicationSummaryDto.Type"/>'s /// human zaaktype label, which isn't a value <see cref="AanvraagSummaryDto.Type"/>'s
/// contract accepts; see <see cref="AanvraagTypeFor"/>), and maps into /// contract accepts; see <see cref="AanvraagTypeFor"/>), and maps into
/// <see cref="ApplicationSummaryDto"/> via <see cref="ZgwZaakMapper"/>. Creates a zaak + /// <see cref="AanvraagSummaryDto"/> via <see cref="ZgwZaakMapper"/>. Creates a zaak +
/// status + rol for a just-submitted aanvraag. Selected only when <c>Zgw:Enabled=true</c>; /// status + rol for a just-submitted aanvraag. Selected only when <c>Zgw:Enabled=true</c>;
/// the default stays <see cref="LocalZaakSource"/>. /// the default stays <see cref="LocalZaakSource"/>.
/// ///
@@ -36,16 +36,16 @@ public sealed class OpenZaakZaakSource(HttpClient http, ZgwTokenProvider tokens,
// sync /admin/cases endpoint, and ASP.NET Core has no sync-context to deadlock on. Make the // sync /admin/cases endpoint, and ASP.NET Core has no sync-context to deadlock on. Make the
// whole cases read path async (endpoint + CasesAdmin + interface) if OpenZaak becomes the // whole cases read path async (endpoint + CasesAdmin + interface) if OpenZaak becomes the
// default and this blocking call shows up under load. // default and this blocking call shows up under load.
public IReadOnlyList<ApplicationSummaryDto> ListCases(DateTimeOffset now) => public IReadOnlyList<AanvraagSummaryDto> ListCases(DateTimeOffset now) =>
ListCasesAsync(bsn: null, caller: null).GetAwaiter().GetResult(); ListCasesAsync(bsn: null, caller: null).GetAwaiter().GetResult();
/// <summary>WP-53: same read, filtered to one citizen's own zaken via ZGW's rol filter param /// <summary>WP-53: same read, filtered to one citizen's own zaken via ZGW's rol filter param
/// (see <see cref="ListCasesAsync"/>) — and minted with that citizen's identity, not the /// (see <see cref="ListCasesAsync"/>) — and minted with that citizen's identity, not the
/// system-level one <see cref="ListCases"/> uses.</summary> /// system-level one <see cref="ListCases"/> uses.</summary>
public IReadOnlyList<ApplicationSummaryDto> ListMyCases(ZorgverlenerCaller caller, DateTimeOffset now) => public IReadOnlyList<AanvraagSummaryDto> ListMyCases(ZorgverlenerCaller caller, DateTimeOffset now) =>
ListCasesAsync(caller.Bsn, caller).GetAwaiter().GetResult(); ListCasesAsync(caller.Bsn, caller).GetAwaiter().GetResult();
private async Task<IReadOnlyList<ApplicationSummaryDto>> ListCasesAsync(string? bsn, CallerIdentity? caller) private async Task<IReadOnlyList<AanvraagSummaryDto>> ListCasesAsync(string? bsn, CallerIdentity? caller)
{ {
var url = $"{options.ZrcBaseUrl}/zaken"; var url = $"{options.ZrcBaseUrl}/zaken";
if (bsn is not null) if (bsn is not null)
@@ -55,7 +55,7 @@ public sealed class OpenZaakZaakSource(HttpClient http, ZgwTokenProvider tokens,
} }
/// <summary>Real, live-repro'd bug (behandelportal's werkvoorraad always failed to parse): /// <summary>Real, live-repro'd bug (behandelportal's werkvoorraad always failed to parse):
/// <c>ApplicationSummaryDto.Type</c>'s contract is the internal aanvraag-type key (e.g. /// <c>AanvraagSummaryDto.Type</c>'s contract is the internal aanvraag-type key (e.g.
/// "herregistratie" — what <see cref="LocalZaakSource"/>/<c>Mappers.ToSummaryDto</c> send, /// "herregistratie" — what <see cref="LocalZaakSource"/>/<c>Mappers.ToSummaryDto</c> send,
/// and what the FE's <c>AANVRAAG_TYPES</c> trust boundary accepts), NOT OpenZaak's human /// and what the FE's <c>AANVRAAG_TYPES</c> trust boundary accepts), NOT OpenZaak's human
/// zaaktype label ("Herregistratie arts") this used to resolve via an extra Catalogi round /// zaaktype label ("Herregistratie arts") this used to resolve via an extra Catalogi round
@@ -6,7 +6,7 @@ namespace BigRegister.Api.Zgw;
/// <summary> /// <summary>
/// The subset of a ZGW Zaak (Zaken API / ZRC) the read slice needs. The full resource has /// The subset of a ZGW Zaak (Zaken API / ZRC) the read slice needs. The full resource has
/// dozens of fields; we bind only what maps to <see cref="ApplicationSummaryDto"/>. Note the /// dozens of fields; we bind only what maps to <see cref="AanvraagSummaryDto"/>. Note the
/// two ZGW traits that force an anti-corruption layer: <see cref="Url"/> is the resource's /// two ZGW traits that force an anti-corruption layer: <see cref="Url"/> is the resource's
/// identity (not a bare id), and <see cref="Zaaktype"/> is a URL <em>into another service</em> /// identity (not a bare id), and <see cref="Zaaktype"/> is a URL <em>into another service</em>
/// (Catalogi/ZTC) that must be resolved to a human label. /// (Catalogi/ZTC) that must be resolved to a human label.
@@ -20,7 +20,7 @@ public sealed record ZgwZaak(
[property: JsonPropertyName("registratiedatum")] DateOnly? Registratiedatum); [property: JsonPropertyName("registratiedatum")] DateOnly? Registratiedatum);
/// <summary> /// <summary>
/// Anti-corruption map: ZGW Zaak → the existing <see cref="ApplicationSummaryDto"/> the FE /// Anti-corruption map: ZGW Zaak → the existing <see cref="AanvraagSummaryDto"/> the FE
/// already renders (WP-49). This is where "URL as identity" and the cross-service zaaktype /// already renders (WP-49). This is where "URL as identity" and the cross-service zaaktype
/// join get flattened away, so nothing downstream (the FE) sees ZGW shapes. /// join get flattened away, so nothing downstream (the FE) sees ZGW shapes.
/// </summary> /// </summary>
@@ -29,7 +29,7 @@ public static class ZgwZaakMapper
/// <summary>Last path segment of a ZGW resource URL — the uuid that identifies it.</summary> /// <summary>Last path segment of a ZGW resource URL — the uuid that identifies it.</summary>
public static string Uuid(string url) => url.TrimEnd('/').Split('/').Last(); public static string Uuid(string url) => url.TrimEnd('/').Split('/').Last();
public static ApplicationSummaryDto ToSummaryDto(ZgwZaak z, string zaaktypeLabel) public static AanvraagSummaryDto ToSummaryDto(ZgwZaak z, string zaaktypeLabel)
{ {
// ponytail: coarse status map — an open zaak (no einddatum) is In behandeling, a closed // ponytail: coarse status map — an open zaak (no einddatum) is In behandeling, a closed
// one is Goedgekeurd. Real fidelity (statustype/resultaat lookups) is a later slice; the // one is Goedgekeurd. Real fidelity (statustype/resultaat lookups) is a later slice; the
@@ -41,7 +41,7 @@ public static class ZgwZaakMapper
var created = Iso(z.Registratiedatum ?? z.Startdatum); var created = Iso(z.Registratiedatum ?? z.Startdatum);
var updated = Iso(z.Einddatum ?? z.Registratiedatum ?? z.Startdatum); var updated = Iso(z.Einddatum ?? z.Registratiedatum ?? z.Startdatum);
return new ApplicationSummaryDto( return new AanvraagSummaryDto(
Id: Uuid(z.Url), Id: Uuid(z.Url),
Type: zaaktypeLabel, Type: zaaktypeLabel,
Status: status, Status: status,
+109 -109
View File
@@ -425,7 +425,7 @@
} }
} }
}, },
"/api/v1/applications": { "/api/v1/aanvragen": {
"get": { "get": {
"tags": [ "tags": [
"BigRegister.Api, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" "BigRegister.Api, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
@@ -438,7 +438,7 @@
"schema": { "schema": {
"type": "array", "type": "array",
"items": { "items": {
"$ref": "#/components/schemas/ApplicationSummaryDto" "$ref": "#/components/schemas/AanvraagSummaryDto"
} }
} }
} }
@@ -454,7 +454,7 @@
"content": { "content": {
"application/json": { "application/json": {
"schema": { "schema": {
"$ref": "#/components/schemas/CreateApplicationRequest" "$ref": "#/components/schemas/CreateAanvraagRequest"
} }
} }
}, },
@@ -466,7 +466,7 @@
"content": { "content": {
"application/json": { "application/json": {
"schema": { "schema": {
"$ref": "#/components/schemas/ApplicationDetailDto" "$ref": "#/components/schemas/AanvraagDetailDto"
} }
} }
} }
@@ -484,7 +484,7 @@
} }
} }
}, },
"/api/v1/applications/{id}": { "/api/v1/aanvragen/{id}": {
"get": { "get": {
"tags": [ "tags": [
"BigRegister.Api, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" "BigRegister.Api, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
@@ -505,7 +505,7 @@
"content": { "content": {
"application/json": { "application/json": {
"schema": { "schema": {
"$ref": "#/components/schemas/ApplicationDetailDto" "$ref": "#/components/schemas/AanvraagDetailDto"
} }
} }
} }
@@ -592,7 +592,7 @@
} }
} }
}, },
"/api/v1/applications/{id}/submit": { "/api/v1/aanvragen/{id}/submit": {
"post": { "post": {
"tags": [ "tags": [
"BigRegister.Api, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" "BigRegister.Api, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
@@ -611,7 +611,7 @@
"content": { "content": {
"application/json": { "application/json": {
"schema": { "schema": {
"$ref": "#/components/schemas/SubmitApplicationRequest" "$ref": "#/components/schemas/AanvraagIndienenRequest"
} }
} }
}, },
@@ -623,7 +623,7 @@
"content": { "content": {
"application/json": { "application/json": {
"schema": { "schema": {
"$ref": "#/components/schemas/SubmitApplicationResponse" "$ref": "#/components/schemas/AanvraagIndienenResponse"
} }
} }
} }
@@ -667,7 +667,7 @@
"schema": { "schema": {
"type": "array", "type": "array",
"items": { "items": {
"$ref": "#/components/schemas/ApplicationSummaryDto" "$ref": "#/components/schemas/AanvraagSummaryDto"
} }
} }
} }
@@ -766,7 +766,7 @@
"schema": { "schema": {
"type": "array", "type": "array",
"items": { "items": {
"$ref": "#/components/schemas/ApplicationSummaryDto" "$ref": "#/components/schemas/AanvraagSummaryDto"
} }
} }
} }
@@ -1484,57 +1484,7 @@
}, },
"additionalProperties": false "additionalProperties": false
}, },
"AanvraagStatusDto": { "AanvraagDetailDto": {
"type": "object",
"properties": {
"tag": {
"type": "string",
"nullable": true
},
"stepIndex": {
"type": "integer",
"format": "int32",
"nullable": true
},
"stepCount": {
"type": "integer",
"format": "int32",
"nullable": true
},
"referentie": {
"type": "string",
"nullable": true
},
"manual": {
"type": "boolean",
"nullable": true
},
"reden": {
"type": "string",
"nullable": true
}
},
"additionalProperties": false
},
"AdresDto": {
"type": "object",
"properties": {
"straat": {
"type": "string",
"nullable": true
},
"postcode": {
"type": "string",
"nullable": true
},
"woonplaats": {
"type": "string",
"nullable": true
}
},
"additionalProperties": false
},
"ApplicationDetailDto": {
"type": "object", "type": "object",
"properties": { "properties": {
"id": { "id": {
@@ -1573,7 +1523,83 @@
}, },
"additionalProperties": false "additionalProperties": false
}, },
"ApplicationSummaryDto": { "AanvraagIndienenRequest": {
"type": "object",
"properties": {
"diplomaHerkomst": {
"type": "string",
"nullable": true
},
"uren": {
"type": "integer",
"format": "int32",
"nullable": true
},
"documents": {
"type": "array",
"items": {
"$ref": "#/components/schemas/DocumentRefDto"
},
"nullable": true
},
"aanvullendeScholing": {
"type": "boolean",
"nullable": true
},
"scholingPunten": {
"type": "integer",
"format": "int32",
"nullable": true
}
},
"additionalProperties": false
},
"AanvraagIndienenResponse": {
"type": "object",
"properties": {
"referentie": {
"type": "string",
"nullable": true
},
"status": {
"$ref": "#/components/schemas/AanvraagStatusDto"
}
},
"additionalProperties": false
},
"AanvraagStatusDto": {
"type": "object",
"properties": {
"tag": {
"type": "string",
"nullable": true
},
"stepIndex": {
"type": "integer",
"format": "int32",
"nullable": true
},
"stepCount": {
"type": "integer",
"format": "int32",
"nullable": true
},
"referentie": {
"type": "string",
"nullable": true
},
"manual": {
"type": "boolean",
"nullable": true
},
"reden": {
"type": "string",
"nullable": true
}
},
"additionalProperties": false
},
"AanvraagSummaryDto": {
"type": "object", "type": "object",
"properties": { "properties": {
"id": { "id": {
@@ -1613,6 +1639,24 @@
}, },
"additionalProperties": false "additionalProperties": false
}, },
"AdresDto": {
"type": "object",
"properties": {
"straat": {
"type": "string",
"nullable": true
},
"postcode": {
"type": "string",
"nullable": true
},
"woonplaats": {
"type": "string",
"nullable": true
}
},
"additionalProperties": false
},
"AuthzAuditDto": { "AuthzAuditDto": {
"type": "object", "type": "object",
"properties": { "properties": {
@@ -1674,7 +1718,7 @@
"type": "object", "type": "object",
"properties": { "properties": {
"aanvraag": { "aanvraag": {
"$ref": "#/components/schemas/ApplicationSummaryDto" "$ref": "#/components/schemas/AanvraagSummaryDto"
}, },
"documenten": { "documenten": {
"type": "array", "type": "array",
@@ -1860,7 +1904,7 @@
}, },
"additionalProperties": false "additionalProperties": false
}, },
"CreateApplicationRequest": { "CreateAanvraagRequest": {
"type": "object", "type": "object",
"properties": { "properties": {
"type": { "type": {
@@ -2678,50 +2722,6 @@
}, },
"additionalProperties": false "additionalProperties": false
}, },
"SubmitApplicationRequest": {
"type": "object",
"properties": {
"diplomaHerkomst": {
"type": "string",
"nullable": true
},
"uren": {
"type": "integer",
"format": "int32",
"nullable": true
},
"documents": {
"type": "array",
"items": {
"$ref": "#/components/schemas/DocumentRefDto"
},
"nullable": true
},
"aanvullendeScholing": {
"type": "boolean",
"nullable": true
},
"scholingPunten": {
"type": "integer",
"format": "int32",
"nullable": true
}
},
"additionalProperties": false
},
"SubmitApplicationResponse": {
"type": "object",
"properties": {
"referentie": {
"type": "string",
"nullable": true
},
"status": {
"$ref": "#/components/schemas/AanvraagStatusDto"
}
},
"additionalProperties": false
},
"UploadCategoriesDto": { "UploadCategoriesDto": {
"type": "object", "type": "object",
"properties": { "properties": {
@@ -8,27 +8,27 @@ using Microsoft.AspNetCore.Mvc.Testing;
namespace BigRegister.Tests; namespace BigRegister.Tests;
public class ApplicationTests(TestWebApplicationFactory factory) : IClassFixture<TestWebApplicationFactory> public class AanvraagTests(TestWebApplicationFactory factory) : IClassFixture<TestWebApplicationFactory>
{ {
private readonly HttpClient _client = factory.CreateClient(); private readonly HttpClient _client = factory.CreateClient();
private async Task<ApplicationDetailDto> Create(string type = "registratie") private async Task<AanvraagDetailDto> Create(string type = "registratie")
{ {
// WP-35: one Concept per type is now server-enforced, and these tests share one DB // WP-35: one Concept per type is now server-enforced, and these tests share one DB
// (IClassFixture). Clear any leftover Concept so each test starts from a clean slate. // (IClassFixture). Clear any leftover Concept so each test starts from a clean slate.
var existing = await List(); var existing = await List();
Assert.NotNull(existing); Assert.NotNull(existing);
foreach (var s in existing.Where(x => x.Status.Tag == "Concept")) foreach (var s in existing.Where(x => x.Status.Tag == "Concept"))
await _client.DeleteAsync($"/api/v1/applications/{s.Id}"); await _client.DeleteAsync($"/api/v1/aanvragen/{s.Id}");
var res = await _client.PostAsJsonAsync("/api/v1/applications", new { type }); var res = await _client.PostAsJsonAsync("/api/v1/aanvragen", new { type });
Assert.Equal(HttpStatusCode.Created, res.StatusCode); Assert.Equal(HttpStatusCode.Created, res.StatusCode);
var created = await res.Content.ReadFromJsonAsync<ApplicationDetailDto>(); var created = await res.Content.ReadFromJsonAsync<AanvraagDetailDto>();
Assert.NotNull(created); Assert.NotNull(created);
return created; return created;
} }
private Task<List<ApplicationSummaryDto>?> List() => private Task<List<AanvraagSummaryDto>?> List() =>
_client.GetFromJsonAsync<List<ApplicationSummaryDto>>("/api/v1/applications"); _client.GetFromJsonAsync<List<AanvraagSummaryDto>>("/api/v1/aanvragen");
// --- Lifecycle over HTTP --- // --- Lifecycle over HTTP ---
@@ -36,7 +36,7 @@ public class ApplicationTests(TestWebApplicationFactory factory) : IClassFixture
public async Task Create_then_list_shows_a_concept_with_step_progress() public async Task Create_then_list_shows_a_concept_with_step_progress()
{ {
var a = await Create(); var a = await Create();
await _client.PutAsJsonAsync($"/api/v1/applications/{a.Id}", await _client.PutAsJsonAsync($"/api/v1/aanvragen/{a.Id}",
new { draft = new { beroep = "arts" }, stepIndex = 1, stepCount = 4 }); new { draft = new { beroep = "arts" }, stepIndex = 1, stepCount = 4 });
var list = await List(); var list = await List();
@@ -51,10 +51,10 @@ public class ApplicationTests(TestWebApplicationFactory factory) : IClassFixture
public async Task Draft_sync_is_readable_back_from_detail() public async Task Draft_sync_is_readable_back_from_detail()
{ {
var a = await Create(); var a = await Create();
await _client.PutAsJsonAsync($"/api/v1/applications/{a.Id}", await _client.PutAsJsonAsync($"/api/v1/aanvragen/{a.Id}",
new { draft = new { beroep = "verpleegkundige" }, stepIndex = 2, stepCount = 4 }); new { draft = new { beroep = "verpleegkundige" }, stepIndex = 2, stepCount = 4 });
var detail = await _client.GetFromJsonAsync<ApplicationDetailDto>($"/api/v1/applications/{a.Id}"); var detail = await _client.GetFromJsonAsync<AanvraagDetailDto>($"/api/v1/aanvragen/{a.Id}");
Assert.NotNull(detail); Assert.NotNull(detail);
Assert.NotNull(detail.Draft); Assert.NotNull(detail.Draft);
Assert.Equal("verpleegkundige", detail.Draft.Value.GetProperty("beroep").GetString()); Assert.Equal("verpleegkundige", detail.Draft.Value.GetProperty("beroep").GetString());
@@ -64,9 +64,9 @@ public class ApplicationTests(TestWebApplicationFactory factory) : IClassFixture
public async Task Submit_duo_registratie_is_in_behandeling_and_auto() public async Task Submit_duo_registratie_is_in_behandeling_and_auto()
{ {
var a = await Create("registratie"); var a = await Create("registratie");
var res = await _client.PostAsJsonAsync($"/api/v1/applications/{a.Id}/submit", new { diplomaHerkomst = "duo" }); var res = await _client.PostAsJsonAsync($"/api/v1/aanvragen/{a.Id}/submit", new { diplomaHerkomst = "duo" });
res.EnsureSuccessStatusCode(); res.EnsureSuccessStatusCode();
var body = (await res.Content.ReadFromJsonAsync<SubmitApplicationResponse>())!; var body = (await res.Content.ReadFromJsonAsync<AanvraagIndienenResponse>())!;
Assert.StartsWith("BIG-2026-", body.Referentie); Assert.StartsWith("BIG-2026-", body.Referentie);
Assert.Equal("InBehandeling", body.Status.Tag); Assert.Equal("InBehandeling", body.Status.Tag);
Assert.False(body.Status.Manual); // auto-approvable → not a manual case Assert.False(body.Status.Manual); // auto-approvable → not a manual case
@@ -76,9 +76,9 @@ public class ApplicationTests(TestWebApplicationFactory factory) : IClassFixture
public async Task Submit_handmatig_registratie_succeeds_as_manual_case() public async Task Submit_handmatig_registratie_succeeds_as_manual_case()
{ {
var a = await Create("registratie"); var a = await Create("registratie");
var res = await _client.PostAsJsonAsync($"/api/v1/applications/{a.Id}/submit", new { diplomaHerkomst = "handmatig" }); var res = await _client.PostAsJsonAsync($"/api/v1/aanvragen/{a.Id}/submit", new { diplomaHerkomst = "handmatig" });
res.EnsureSuccessStatusCode(); // no longer a 422 res.EnsureSuccessStatusCode(); // no longer a 422
var body = (await res.Content.ReadFromJsonAsync<SubmitApplicationResponse>())!; var body = (await res.Content.ReadFromJsonAsync<AanvraagIndienenResponse>())!;
Assert.Equal("InBehandeling", body.Status.Tag); Assert.Equal("InBehandeling", body.Status.Tag);
Assert.True(body.Status.Manual); Assert.True(body.Status.Manual);
} }
@@ -87,9 +87,9 @@ public class ApplicationTests(TestWebApplicationFactory factory) : IClassFixture
public async Task Submit_herregistratie_with_zero_uren_is_afgewezen() public async Task Submit_herregistratie_with_zero_uren_is_afgewezen()
{ {
var a = await Create("herregistratie"); var a = await Create("herregistratie");
var res = await _client.PostAsJsonAsync($"/api/v1/applications/{a.Id}/submit", new { uren = 0 }); var res = await _client.PostAsJsonAsync($"/api/v1/aanvragen/{a.Id}/submit", new { uren = 0 });
res.EnsureSuccessStatusCode(); // the submission is accepted... res.EnsureSuccessStatusCode(); // the submission is accepted...
var body = (await res.Content.ReadFromJsonAsync<SubmitApplicationResponse>())!; var body = (await res.Content.ReadFromJsonAsync<AanvraagIndienenResponse>())!;
Assert.Equal("Afgewezen", body.Status.Tag); // ...but resolves to rejected Assert.Equal("Afgewezen", body.Status.Tag); // ...but resolves to rejected
Assert.NotNull(body.Status.Reden); Assert.NotNull(body.Status.Reden);
} }
@@ -98,8 +98,8 @@ public class ApplicationTests(TestWebApplicationFactory factory) : IClassFixture
public async Task Submitting_twice_conflicts() public async Task Submitting_twice_conflicts()
{ {
var a = await Create("registratie"); var a = await Create("registratie");
(await _client.PostAsJsonAsync($"/api/v1/applications/{a.Id}/submit", new { diplomaHerkomst = "duo" })).EnsureSuccessStatusCode(); (await _client.PostAsJsonAsync($"/api/v1/aanvragen/{a.Id}/submit", new { diplomaHerkomst = "duo" })).EnsureSuccessStatusCode();
var again = await _client.PostAsJsonAsync($"/api/v1/applications/{a.Id}/submit", new { diplomaHerkomst = "duo" }); var again = await _client.PostAsJsonAsync($"/api/v1/aanvragen/{a.Id}/submit", new { diplomaHerkomst = "duo" });
Assert.Equal(HttpStatusCode.Conflict, again.StatusCode); Assert.Equal(HttpStatusCode.Conflict, again.StatusCode);
} }
@@ -109,7 +109,7 @@ public class ApplicationTests(TestWebApplicationFactory factory) : IClassFixture
public async Task Creating_a_second_concept_of_the_same_type_conflicts() public async Task Creating_a_second_concept_of_the_same_type_conflicts()
{ {
await Create("herregistratie"); await Create("herregistratie");
var dup = await _client.PostAsJsonAsync("/api/v1/applications", new { type = "herregistratie" }); var dup = await _client.PostAsJsonAsync("/api/v1/aanvragen", new { type = "herregistratie" });
Assert.Equal(HttpStatusCode.Conflict, dup.StatusCode); Assert.Equal(HttpStatusCode.Conflict, dup.StatusCode);
} }
@@ -117,7 +117,7 @@ public class ApplicationTests(TestWebApplicationFactory factory) : IClassFixture
public async Task A_concept_of_a_different_type_is_allowed() public async Task A_concept_of_a_different_type_is_allowed()
{ {
await Create("registratie"); await Create("registratie");
var other = await _client.PostAsJsonAsync("/api/v1/applications", new { type = "herregistratie" }); var other = await _client.PostAsJsonAsync("/api/v1/aanvragen", new { type = "herregistratie" });
Assert.Equal(HttpStatusCode.Created, other.StatusCode); Assert.Equal(HttpStatusCode.Created, other.StatusCode);
} }
@@ -125,8 +125,8 @@ public class ApplicationTests(TestWebApplicationFactory factory) : IClassFixture
public async Task A_new_concept_is_allowed_once_the_previous_one_is_submitted() public async Task A_new_concept_is_allowed_once_the_previous_one_is_submitted()
{ {
var a = await Create("registratie"); var a = await Create("registratie");
(await _client.PostAsJsonAsync($"/api/v1/applications/{a.Id}/submit", new { diplomaHerkomst = "duo" })).EnsureSuccessStatusCode(); (await _client.PostAsJsonAsync($"/api/v1/aanvragen/{a.Id}/submit", new { diplomaHerkomst = "duo" })).EnsureSuccessStatusCode();
var next = await _client.PostAsJsonAsync("/api/v1/applications", new { type = "registratie" }); var next = await _client.PostAsJsonAsync("/api/v1/aanvragen", new { type = "registratie" });
Assert.Equal(HttpStatusCode.Created, next.StatusCode); Assert.Equal(HttpStatusCode.Created, next.StatusCode);
} }
@@ -134,38 +134,38 @@ public class ApplicationTests(TestWebApplicationFactory factory) : IClassFixture
public async Task Cancel_concept_removes_it() public async Task Cancel_concept_removes_it()
{ {
var a = await Create(); var a = await Create();
Assert.Equal(HttpStatusCode.NoContent, (await _client.DeleteAsync($"/api/v1/applications/{a.Id}")).StatusCode); Assert.Equal(HttpStatusCode.NoContent, (await _client.DeleteAsync($"/api/v1/aanvragen/{a.Id}")).StatusCode);
Assert.Equal(HttpStatusCode.NotFound, (await _client.GetAsync($"/api/v1/applications/{a.Id}")).StatusCode); Assert.Equal(HttpStatusCode.NotFound, (await _client.GetAsync($"/api/v1/aanvragen/{a.Id}")).StatusCode);
} }
[Fact] [Fact]
public async Task Cancel_submitted_aanvraag_conflicts() public async Task Cancel_submitted_aanvraag_conflicts()
{ {
var a = await Create("registratie"); var a = await Create("registratie");
(await _client.PostAsJsonAsync($"/api/v1/applications/{a.Id}/submit", new { diplomaHerkomst = "duo" })).EnsureSuccessStatusCode(); (await _client.PostAsJsonAsync($"/api/v1/aanvragen/{a.Id}/submit", new { diplomaHerkomst = "duo" })).EnsureSuccessStatusCode();
Assert.Equal(HttpStatusCode.Conflict, (await _client.DeleteAsync($"/api/v1/applications/{a.Id}")).StatusCode); Assert.Equal(HttpStatusCode.Conflict, (await _client.DeleteAsync($"/api/v1/aanvragen/{a.Id}")).StatusCode);
} }
// --- WP-53: citizen-scoping — GET /applications must never leak across identities. --- // --- WP-53: citizen-scoping — GET /aanvragen must never leak across identities. ---
[Fact] [Fact]
public async Task Applications_are_scoped_to_the_caller_bsn() public async Task Applications_are_scoped_to_the_caller_bsn()
{ {
var mine = await Create("intake"); var mine = await Create("intake");
var createOther = new HttpRequestMessage(HttpMethod.Post, "/api/v1/applications") var createOther = new HttpRequestMessage(HttpMethod.Post, "/api/v1/aanvragen")
{ {
Content = JsonContent.Create(new { type = "intake" }), Content = JsonContent.Create(new { type = "intake" }),
Headers = { { "X-Subject", "999888777" } }, Headers = { { "X-Subject", "999888777" } },
}; };
var otherRes = await _client.SendAsync(createOther); var otherRes = await _client.SendAsync(createOther);
Assert.Equal(HttpStatusCode.Created, otherRes.StatusCode); Assert.Equal(HttpStatusCode.Created, otherRes.StatusCode);
var other = (await otherRes.Content.ReadFromJsonAsync<ApplicationDetailDto>())!; var other = (await otherRes.Content.ReadFromJsonAsync<AanvraagDetailDto>())!;
try try
{ {
var listOther = new HttpRequestMessage(HttpMethod.Get, "/api/v1/applications") { Headers = { { "X-Subject", "999888777" } } }; var listOther = new HttpRequestMessage(HttpMethod.Get, "/api/v1/aanvragen") { Headers = { { "X-Subject", "999888777" } } };
var theirCases = (await (await _client.SendAsync(listOther)).Content.ReadFromJsonAsync<List<ApplicationSummaryDto>>())!; var theirCases = (await (await _client.SendAsync(listOther)).Content.ReadFromJsonAsync<List<AanvraagSummaryDto>>())!;
Assert.Contains(theirCases, c => c.Id == other.Id); Assert.Contains(theirCases, c => c.Id == other.Id);
Assert.DoesNotContain(theirCases, c => c.Id == mine.Id); Assert.DoesNotContain(theirCases, c => c.Id == mine.Id);
@@ -175,9 +175,9 @@ public class ApplicationTests(TestWebApplicationFactory factory) : IClassFixture
} }
finally finally
{ {
var deleteOther = new HttpRequestMessage(HttpMethod.Delete, $"/api/v1/applications/{other.Id}") { Headers = { { "X-Subject", "999888777" } } }; var deleteOther = new HttpRequestMessage(HttpMethod.Delete, $"/api/v1/aanvragen/{other.Id}") { Headers = { { "X-Subject", "999888777" } } };
await _client.SendAsync(deleteOther); await _client.SendAsync(deleteOther);
await _client.DeleteAsync($"/api/v1/applications/{mine.Id}"); await _client.DeleteAsync($"/api/v1/aanvragen/{mine.Id}");
} }
} }
@@ -205,7 +205,7 @@ public class ApplicationTests(TestWebApplicationFactory factory) : IClassFixture
var foreignDoc = await UploadAs(_client, "999888777", Guid.NewGuid().ToString()); var foreignDoc = await UploadAs(_client, "999888777", Guid.NewGuid().ToString());
var a = await Create("registratie"); var a = await Create("registratie");
var res = await _client.PostAsJsonAsync($"/api/v1/applications/{a.Id}/submit", var res = await _client.PostAsJsonAsync($"/api/v1/aanvragen/{a.Id}/submit",
new { diplomaHerkomst = "duo", documents = new[] { new { categoryId = "diploma", channel = "digital", documentId = foreignDoc.DocumentId } } }); new { diplomaHerkomst = "duo", documents = new[] { new { categoryId = "diploma", channel = "digital", documentId = foreignDoc.DocumentId } } });
Assert.Equal(HttpStatusCode.BadRequest, res.StatusCode); Assert.Equal(HttpStatusCode.BadRequest, res.StatusCode);
@@ -221,7 +221,7 @@ public class ApplicationTests(TestWebApplicationFactory factory) : IClassFixture
var foreignDoc = await UploadAs(_client, "999888777", Guid.NewGuid().ToString()); var foreignDoc = await UploadAs(_client, "999888777", Guid.NewGuid().ToString());
var a = await Create("registratie"); var a = await Create("registratie");
var res = await _client.PutAsJsonAsync($"/api/v1/applications/{a.Id}", var res = await _client.PutAsJsonAsync($"/api/v1/aanvragen/{a.Id}",
new { draft = new { }, stepIndex = 0, stepCount = 1, documentIds = new[] { foreignDoc.DocumentId } }); new { draft = new { }, stepIndex = 0, stepCount = 1, documentIds = new[] { foreignDoc.DocumentId } });
Assert.Equal(HttpStatusCode.BadRequest, res.StatusCode); Assert.Equal(HttpStatusCode.BadRequest, res.StatusCode);
} }
@@ -9,7 +9,7 @@ namespace BigRegister.Tests.Acceptance;
/// <summary> /// <summary>
/// Behaviour-level tests for the scholing-threshold enforcement (WP-69) over /// Behaviour-level tests for the scholing-threshold enforcement (WP-69) over
/// <c>POST /applications/{id}/submit</c> (the wizard's real path — WP-72 deleted the legacy /// <c>POST /aanvragen/{id}/submit</c> (the wizard's real path — WP-72 deleted the legacy
/// <c>POST /intakes</c> endpoint this once also covered). Built through the <see /// <c>POST /intakes</c> endpoint this once also covered). Built through the <see
/// cref="Given"/> type-state builder, mirroring <see cref="BesluitLifecycleTests"/> rather /// cref="Given"/> type-state builder, mirroring <see cref="BesluitLifecycleTests"/> rather
/// than the full wizard/upload dance — the builder's default owner IS <see /// than the full wizard/upload dance — the builder's default owner IS <see
@@ -28,7 +28,7 @@ public class IntakeSubmissionTests(TestWebApplicationFactory factory) : IClassFi
} }
private Task<HttpResponseMessage> Submit(string id, object body) => private Task<HttpResponseMessage> Submit(string id, object body) =>
_client.PostAsJsonAsync($"/api/v1/applications/{id}/submit", body); _client.PostAsJsonAsync($"/api/v1/aanvragen/{id}/submit", body);
[Fact] [Fact]
public async Task Below_threshold_without_an_answer_is_rejected_and_stays_a_concept() public async Task Below_threshold_without_an_answer_is_rejected_and_stays_a_concept()
@@ -117,7 +117,7 @@ public class IntakeSubmissionTests(TestWebApplicationFactory factory) : IClassFi
// Then the submission is accepted and resolves to Afgewezen — not a 400. // Then the submission is accepted and resolves to Afgewezen — not a 400.
res.EnsureSuccessStatusCode(); res.EnsureSuccessStatusCode();
var body = (await res.Content.ReadFromJsonAsync<SubmitApplicationResponse>())!; var body = (await res.Content.ReadFromJsonAsync<AanvraagIndienenResponse>())!;
Assert.Equal("Afgewezen", body.Status.Tag); Assert.Equal("Afgewezen", body.Status.Tag);
} }
} }
@@ -18,11 +18,11 @@ public class AdminCasesTests(TestWebApplicationFactory factory) : IClassFixture<
return req; return req;
} }
private async Task<ApplicationDetailDto> Create(string type) private async Task<AanvraagDetailDto> Create(string type)
{ {
var res = await _client.PostAsJsonAsync("/api/v1/applications", new { type }); var res = await _client.PostAsJsonAsync("/api/v1/aanvragen", new { type });
Assert.Equal(HttpStatusCode.Created, res.StatusCode); Assert.Equal(HttpStatusCode.Created, res.StatusCode);
return (await res.Content.ReadFromJsonAsync<ApplicationDetailDto>())!; return (await res.Content.ReadFromJsonAsync<AanvraagDetailDto>())!;
} }
[Fact] [Fact]
@@ -33,7 +33,7 @@ public class AdminCasesTests(TestWebApplicationFactory factory) : IClassFixture<
{ {
var list = await _client.SendAsync(Admin(HttpMethod.Get, "/api/v1/admin/cases")); var list = await _client.SendAsync(Admin(HttpMethod.Get, "/api/v1/admin/cases"));
list.EnsureSuccessStatusCode(); list.EnsureSuccessStatusCode();
var cases = (await list.Content.ReadFromJsonAsync<List<ApplicationSummaryDto>>())!; var cases = (await list.Content.ReadFromJsonAsync<List<AanvraagSummaryDto>>())!;
var mine = cases.Single(x => x.Id == a.Id); var mine = cases.Single(x => x.Id == a.Id);
// RB-03/BIO-003: the owner is carried, but masked — it is a BSN, and this list is // RB-03/BIO-003: the owner is carried, but masked — it is a BSN, and this list is
// read by someone who is not the subject. // read by someone who is not the subject.
@@ -57,13 +57,13 @@ public class AdminCasesTests(TestWebApplicationFactory factory) : IClassFixture<
public async Task Admin_can_delete_a_submitted_case() public async Task Admin_can_delete_a_submitted_case()
{ {
var a = await Create("registratie"); var a = await Create("registratie");
(await _client.PostAsJsonAsync($"/api/v1/applications/{a.Id}/submit", new { diplomaHerkomst = "duo" })) (await _client.PostAsJsonAsync($"/api/v1/aanvragen/{a.Id}/submit", new { diplomaHerkomst = "duo" }))
.EnsureSuccessStatusCode(); .EnsureSuccessStatusCode();
// The user-facing DELETE refuses a submitted case (409); admin delete removes it. // The user-facing DELETE refuses a submitted case (409); admin delete removes it.
var del = await _client.SendAsync(Admin(HttpMethod.Delete, $"/api/v1/admin/cases/{a.Id}")); var del = await _client.SendAsync(Admin(HttpMethod.Delete, $"/api/v1/admin/cases/{a.Id}"));
Assert.Equal(HttpStatusCode.NoContent, del.StatusCode); Assert.Equal(HttpStatusCode.NoContent, del.StatusCode);
Assert.Equal(HttpStatusCode.NotFound, (await _client.GetAsync($"/api/v1/applications/{a.Id}")).StatusCode); Assert.Equal(HttpStatusCode.NotFound, (await _client.GetAsync($"/api/v1/aanvragen/{a.Id}")).StatusCode);
} }
[Fact] [Fact]
@@ -17,12 +17,12 @@ namespace BigRegister.Tests;
file sealed class IdMismatchZaakSource : IZaakSource file sealed class IdMismatchZaakSource : IZaakSource
{ {
private readonly LocalZaakSource inner = new(); private readonly LocalZaakSource inner = new();
private static ApplicationSummaryDto Rekey(ApplicationSummaryDto dto) => dto with { Id = $"zaak-{dto.Id}" }; private static AanvraagSummaryDto Rekey(AanvraagSummaryDto dto) => dto with { Id = $"zaak-{dto.Id}" };
public IReadOnlyList<ApplicationSummaryDto> ListCases(DateTimeOffset now) => public IReadOnlyList<AanvraagSummaryDto> ListCases(DateTimeOffset now) =>
inner.ListCases(now).Select(Rekey).ToList(); inner.ListCases(now).Select(Rekey).ToList();
public IReadOnlyList<ApplicationSummaryDto> ListMyCases(ZorgverlenerCaller caller, DateTimeOffset now) => public IReadOnlyList<AanvraagSummaryDto> ListMyCases(ZorgverlenerCaller caller, DateTimeOffset now) =>
inner.ListMyCases(caller, now).Select(Rekey).ToList(); inner.ListMyCases(caller, now).Select(Rekey).ToList();
public (string Referentie, AanvraagStatusDto Status, string? ZaakUrl) CreateZaak( public (string Referentie, AanvraagStatusDto Status, string? ZaakUrl) CreateZaak(
@@ -67,13 +67,13 @@ public class BeoordelingIdMismatchTests
using var factory = Factory(); using var factory = Factory();
using var client = factory.CreateClient(); using var client = factory.CreateClient();
var created = await client.PostAsJsonAsync("/api/v1/applications", new { type = "registratie" }); var created = await client.PostAsJsonAsync("/api/v1/aanvragen", new { type = "registratie" });
var app = (await created.Content.ReadFromJsonAsync<ApplicationDetailDto>())!; var app = (await created.Content.ReadFromJsonAsync<AanvraagDetailDto>())!;
var submit = await client.PostAsJsonAsync($"/api/v1/applications/{app.Id}/submit", new { diplomaHerkomst = "handmatig" }); var submit = await client.PostAsJsonAsync($"/api/v1/aanvragen/{app.Id}/submit", new { diplomaHerkomst = "handmatig" });
submit.EnsureSuccessStatusCode(); submit.EnsureSuccessStatusCode();
var werkvoorraad = await client.SendAsync(Behandelaar(HttpMethod.Get, "/api/v1/werkvoorraad")); var werkvoorraad = await client.SendAsync(Behandelaar(HttpMethod.Get, "/api/v1/werkvoorraad"));
var items = (await werkvoorraad.Content.ReadFromJsonAsync<List<ApplicationSummaryDto>>())!; var items = (await werkvoorraad.Content.ReadFromJsonAsync<List<AanvraagSummaryDto>>())!;
var caseId = Assert.Single(items).Id; var caseId = Assert.Single(items).Id;
// Sanity: the id divergence this test exists for is real, not accidentally absent. // Sanity: the id divergence this test exists for is real, not accidentally absent.
Assert.NotEqual(app.Id, caseId); Assert.NotEqual(app.Id, caseId);
@@ -35,17 +35,17 @@ public class BeoordelingTests(TestWebApplicationFactory factory) : IClassFixture
/// A manual (never auto-approved) case with one linked document, so it stays /// A manual (never auto-approved) case with one linked document, so it stays
/// InBehandeling/decidable regardless of test timing (the 8s auto-approval window /// InBehandeling/decidable regardless of test timing (the 8s auto-approval window
/// would otherwise make a duo-registratie/herregistratie fixture flaky). /// would otherwise make a duo-registratie/herregistratie fixture flaky).
private async Task<(ApplicationDetailDto App, string DocumentId)> CreateManualCaseWithDocument() private async Task<(AanvraagDetailDto App, string DocumentId)> CreateManualCaseWithDocument()
{ {
var created = await _client.PostAsJsonAsync("/api/v1/applications", new { type = "registratie" }); var created = await _client.PostAsJsonAsync("/api/v1/aanvragen", new { type = "registratie" });
var a = (await created.Content.ReadFromJsonAsync<ApplicationDetailDto>())!; var a = (await created.Content.ReadFromJsonAsync<AanvraagDetailDto>())!;
var localId = Guid.NewGuid().ToString(); var localId = Guid.NewGuid().ToString();
var upload = await _client.PostAsync("/api/v1/uploads", UploadForm(localId, "diploma", "diploma.pdf")); var upload = await _client.PostAsync("/api/v1/uploads", UploadForm(localId, "diploma", "diploma.pdf"));
upload.EnsureSuccessStatusCode(); upload.EnsureSuccessStatusCode();
var doc = (await upload.Content.ReadFromJsonAsync<UploadResponse>())!; var doc = (await upload.Content.ReadFromJsonAsync<UploadResponse>())!;
var submit = await _client.PostAsJsonAsync($"/api/v1/applications/{a.Id}/submit", new var submit = await _client.PostAsJsonAsync($"/api/v1/aanvragen/{a.Id}/submit", new
{ {
diplomaHerkomst = "handmatig", diplomaHerkomst = "handmatig",
documents = new[] { new { categoryId = "diploma", channel = "digital", documentId = doc.DocumentId } }, documents = new[] { new { categoryId = "diploma", channel = "digital", documentId = doc.DocumentId } },
@@ -88,8 +88,8 @@ public class BeoordelingTests(TestWebApplicationFactory factory) : IClassFixture
[Fact] [Fact]
public async Task Concept_and_unknown_id_are_not_found() public async Task Concept_and_unknown_id_are_not_found()
{ {
var created = await _client.PostAsJsonAsync("/api/v1/applications", new { type = "registratie" }); var created = await _client.PostAsJsonAsync("/api/v1/aanvragen", new { type = "registratie" });
var concept = (await created.Content.ReadFromJsonAsync<ApplicationDetailDto>())!; var concept = (await created.Content.ReadFromJsonAsync<AanvraagDetailDto>())!;
try try
{ {
var conceptRes = await _client.SendAsync(AsBehandelaar(HttpMethod.Get, $"/api/v1/beoordeling/{concept.Id}")); var conceptRes = await _client.SendAsync(AsBehandelaar(HttpMethod.Get, $"/api/v1/beoordeling/{concept.Id}"));
@@ -100,7 +100,7 @@ public class BeoordelingTests(TestWebApplicationFactory factory) : IClassFixture
} }
finally finally
{ {
await _client.DeleteAsync($"/api/v1/applications/{concept.Id}"); await _client.DeleteAsync($"/api/v1/aanvragen/{concept.Id}");
} }
} }
@@ -202,9 +202,9 @@ public class EndpointTests(TestWebApplicationFactory factory) : IClassFixture<Te
var doc = await Upload(Guid.NewGuid().ToString()); var doc = await Upload(Guid.NewGuid().ToString());
// Through the real submit path (RB-06 deleted POST /registrations, which was the only // Through the real submit path (RB-06 deleted POST /registrations, which was the only
// other caller of DocumentStore.Link and had no ownership guard on it). // other caller of DocumentStore.Link and had no ownership guard on it).
var created = await _client.PostAsJsonAsync("/api/v1/applications", new { type = "registratie" }); var created = await _client.PostAsJsonAsync("/api/v1/aanvragen", new { type = "registratie" });
var aanvraag = (await created.Content.ReadFromJsonAsync<ApplicationDetailDto>())!; var aanvraag = (await created.Content.ReadFromJsonAsync<AanvraagDetailDto>())!;
var submit = await _client.PostAsJsonAsync($"/api/v1/applications/{aanvraag.Id}/submit", var submit = await _client.PostAsJsonAsync($"/api/v1/aanvragen/{aanvraag.Id}/submit",
new { diplomaHerkomst = "duo", documents = new[] { new DocumentRefDto("diploma", "digital", doc.DocumentId) } }); new { diplomaHerkomst = "duo", documents = new[] { new DocumentRefDto("diploma", "digital", doc.DocumentId) } });
submit.EnsureSuccessStatusCode(); submit.EnsureSuccessStatusCode();
Assert.Equal(HttpStatusCode.Conflict, (await _client.DeleteAsync($"/api/v1/uploads/{doc.DocumentId}")).StatusCode); Assert.Equal(HttpStatusCode.Conflict, (await _client.DeleteAsync($"/api/v1/uploads/{doc.DocumentId}")).StatusCode);
@@ -51,16 +51,16 @@ public class FeatureFlagTests(TestWebApplicationFactory factory) : IClassFixture
{ {
try try
{ {
// Off → POST /applications for a registratie is refused. // Off → POST /aanvragen for a registratie is refused.
(await _client.SendAsync(Admin(HttpMethod.Put, $"/api/v1/admin/flags/{FeatureFlags.InschrijvingOpen}", new { enabled = false }))) (await _client.SendAsync(Admin(HttpMethod.Put, $"/api/v1/admin/flags/{FeatureFlags.InschrijvingOpen}", new { enabled = false })))
.EnsureSuccessStatusCode(); .EnsureSuccessStatusCode();
var blocked = await _client.PostAsJsonAsync("/api/v1/applications", new { type = "registratie" }); var blocked = await _client.PostAsJsonAsync("/api/v1/aanvragen", new { type = "registratie" });
Assert.Equal(HttpStatusCode.Forbidden, blocked.StatusCode); Assert.Equal(HttpStatusCode.Forbidden, blocked.StatusCode);
// On → allowed again. // On → allowed again.
(await _client.SendAsync(Admin(HttpMethod.Put, $"/api/v1/admin/flags/{FeatureFlags.InschrijvingOpen}", new { enabled = true }))) (await _client.SendAsync(Admin(HttpMethod.Put, $"/api/v1/admin/flags/{FeatureFlags.InschrijvingOpen}", new { enabled = true })))
.EnsureSuccessStatusCode(); .EnsureSuccessStatusCode();
var ok = await _client.PostAsJsonAsync("/api/v1/applications", new { type = "registratie" }); var ok = await _client.PostAsJsonAsync("/api/v1/aanvragen", new { type = "registratie" });
Assert.Equal(HttpStatusCode.Created, ok.StatusCode); Assert.Equal(HttpStatusCode.Created, ok.StatusCode);
} }
finally finally
@@ -63,7 +63,7 @@ public class OpenZaakIntegrationTests
using var client = factory.CreateClient(); using var client = factory.CreateClient();
client.DefaultRequestHeaders.Add("X-Role", "admin"); // CasesAdmin gate (cases:manage) client.DefaultRequestHeaders.Add("X-Role", "admin"); // CasesAdmin gate (cases:manage)
var cases = await client.GetFromJsonAsync<List<ApplicationSummaryDto>>("/api/v1/admin/cases"); var cases = await client.GetFromJsonAsync<List<AanvraagSummaryDto>>("/api/v1/admin/cases");
Assert.NotNull(cases); Assert.NotNull(cases);
// bootstrap-catalogus.sh seeds exactly one zaak, identificatie BIG-2026-000123. // bootstrap-catalogus.sh seeds exactly one zaak, identificatie BIG-2026-000123.
@@ -17,7 +17,7 @@ namespace BigRegister.Tests;
/// - it is named, with a reason, in <see cref="AllowList"/> below. /// - it is named, with a reason, in <see cref="AllowList"/> below.
/// ///
/// The allow-list is deliberately not "public routes" — most of its entries are NOT public. /// The allow-list is deliberately not "public routes" — most of its entries are NOT public.
/// `GET /applications/{id}` requires a caller identity and is scoped to that caller's own BSN /// `GET /aanvragen/{id}` requires a caller identity and is scoped to that caller's own BSN
/// inline (`ctx.Zorgverlener()`), not through one of the five wrappers, which only gate the /// inline (`ctx.Zorgverlener()`), not through one of the five wrappers, which only gate the
/// coarse admin/behandelaar surfaces. Recording that here, with the actual reason, is the point /// coarse admin/behandelaar surfaces. Recording that here, with the actual reason, is the point
/// of BIO-016's remediation ("makes 'this endpoint is public' a decision someone wrote down") /// of BIO-016's remediation ("makes 'this endpoint is public' a decision someone wrote down")
@@ -58,12 +58,12 @@ public class RouteInventoryTests(TestWebApplicationFactory factory) : IClassFixt
new("GET", "/api/v1/uploads/{documentId}/content", "Ownership-scoped inline (RB-01/BIO-004): owning citizen, or a behandelaar via Authz.CanBeoordelen."), new("GET", "/api/v1/uploads/{documentId}/content", "Ownership-scoped inline (RB-01/BIO-004): owning citizen, or a behandelaar via Authz.CanBeoordelen."),
new("GET", "/api/v1/uploads/status", "Ownership-scoped inline: DocumentStore.ByLocalIds filtered to ctx.Zorgverlener().Bsn."), new("GET", "/api/v1/uploads/status", "Ownership-scoped inline: DocumentStore.ByLocalIds filtered to ctx.Zorgverlener().Bsn."),
new("DELETE", "/api/v1/uploads/{documentId}", "Ownership-scoped inline: DocumentStore.DeleteOwned keyed by ctx.Zorgverlener().Bsn."), new("DELETE", "/api/v1/uploads/{documentId}", "Ownership-scoped inline: DocumentStore.DeleteOwned keyed by ctx.Zorgverlener().Bsn."),
new("GET", "/api/v1/applications", "Ownership-scoped inline: IZaakSource.ListMyCases(ctx.Zorgverlener(), ...)."), new("GET", "/api/v1/aanvragen", "Ownership-scoped inline: IZaakSource.ListMyCases(ctx.Zorgverlener(), ...)."),
new("GET", "/api/v1/applications/{id}", "Ownership-scoped inline: ApplicationStore.Get(id, ctx.Zorgverlener().Bsn)."), new("GET", "/api/v1/aanvragen/{id}", "Ownership-scoped inline: ApplicationStore.Get(id, ctx.Zorgverlener().Bsn)."),
new("POST", "/api/v1/applications", "Ownership-scoped inline: created under ctx.Zorgverlener().Bsn."), new("POST", "/api/v1/aanvragen", "Ownership-scoped inline: created under ctx.Zorgverlener().Bsn."),
new("PUT", "/api/v1/applications/{id}", "Ownership-scoped inline: ApplicationStore.SyncDraft keyed by ctx.Zorgverlener().Bsn."), new("PUT", "/api/v1/aanvragen/{id}", "Ownership-scoped inline: ApplicationStore.SyncDraft keyed by ctx.Zorgverlener().Bsn."),
new("DELETE", "/api/v1/applications/{id}", "Ownership-scoped inline: ApplicationStore.Get/.Delete keyed by ctx.Zorgverlener().Bsn."), new("DELETE", "/api/v1/aanvragen/{id}", "Ownership-scoped inline: ApplicationStore.Get/.Delete keyed by ctx.Zorgverlener().Bsn."),
new("POST", "/api/v1/applications/{id}/submit", "Ownership-scoped inline: ApplicationStore.Submit keyed by ctx.Zorgverlener().Bsn."), new("POST", "/api/v1/aanvragen/{id}/submit", "Ownership-scoped inline: ApplicationStore.Submit keyed by ctx.Zorgverlener().Bsn."),
// --- External caller, not a Principal at all. --- // --- External caller, not a Principal at all. ---
new("POST", "/api/v1/zgw/notificaties", "OpenZaak's NRC, not a user: gated by a fixed-time shared-secret comparison, audited directly."), new("POST", "/api/v1/zgw/notificaties", "OpenZaak's NRC, not a user: gated by a fixed-time shared-secret comparison, audited directly."),
@@ -18,11 +18,11 @@ public class WerkvoorraadTests(TestWebApplicationFactory factory) : IClassFixtur
return req; return req;
} }
private async Task<ApplicationDetailDto> CreateAndSubmitHerregistratie() private async Task<AanvraagDetailDto> CreateAndSubmitHerregistratie()
{ {
var created = await _client.PostAsJsonAsync("/api/v1/applications", new { type = "herregistratie" }); var created = await _client.PostAsJsonAsync("/api/v1/aanvragen", new { type = "herregistratie" });
var a = (await created.Content.ReadFromJsonAsync<ApplicationDetailDto>())!; var a = (await created.Content.ReadFromJsonAsync<AanvraagDetailDto>())!;
(await _client.PostAsJsonAsync($"/api/v1/applications/{a.Id}/submit", new { uren = 200 })) (await _client.PostAsJsonAsync($"/api/v1/aanvragen/{a.Id}/submit", new { uren = 200 }))
.EnsureSuccessStatusCode(); .EnsureSuccessStatusCode();
return a; return a;
} }
@@ -35,7 +35,7 @@ public class WerkvoorraadTests(TestWebApplicationFactory factory) : IClassFixtur
{ {
var res = await _client.SendAsync(AsBehandelaar("/api/v1/werkvoorraad")); var res = await _client.SendAsync(AsBehandelaar("/api/v1/werkvoorraad"));
res.EnsureSuccessStatusCode(); res.EnsureSuccessStatusCode();
var queue = (await res.Content.ReadFromJsonAsync<List<ApplicationSummaryDto>>())!; var queue = (await res.Content.ReadFromJsonAsync<List<AanvraagSummaryDto>>())!;
var mine = queue.Single(x => x.Id == a.Id); var mine = queue.Single(x => x.Id == a.Id);
Assert.Equal("InBehandeling", mine.Status.Tag); Assert.Equal("InBehandeling", mine.Status.Tag);
// RB-03/BIO-003: masked, like /admin/cases — both inherit ToAdminSummaryDto. // RB-03/BIO-003: masked, like /admin/cases — both inherit ToAdminSummaryDto.
@@ -53,18 +53,18 @@ public class WerkvoorraadTests(TestWebApplicationFactory factory) : IClassFixtur
[Fact] [Fact]
public async Task Queue_excludes_concepts() public async Task Queue_excludes_concepts()
{ {
var created = await _client.PostAsJsonAsync("/api/v1/applications", new { type = "herregistratie" }); var created = await _client.PostAsJsonAsync("/api/v1/aanvragen", new { type = "herregistratie" });
var a = (await created.Content.ReadFromJsonAsync<ApplicationDetailDto>())!; var a = (await created.Content.ReadFromJsonAsync<AanvraagDetailDto>())!;
try try
{ {
var res = await _client.SendAsync(AsBehandelaar("/api/v1/werkvoorraad")); var res = await _client.SendAsync(AsBehandelaar("/api/v1/werkvoorraad"));
res.EnsureSuccessStatusCode(); res.EnsureSuccessStatusCode();
var queue = (await res.Content.ReadFromJsonAsync<List<ApplicationSummaryDto>>())!; var queue = (await res.Content.ReadFromJsonAsync<List<AanvraagSummaryDto>>())!;
Assert.DoesNotContain(queue, x => x.Id == a.Id); Assert.DoesNotContain(queue, x => x.Id == a.Id);
} }
finally finally
{ {
await _client.DeleteAsync($"/api/v1/applications/{a.Id}"); await _client.DeleteAsync($"/api/v1/aanvragen/{a.Id}");
} }
} }
@@ -39,14 +39,14 @@ public class ZgwDivergenceTests
b.ConfigurePrimaryHttpMessageHandler(() => stub)))); b.ConfigurePrimaryHttpMessageHandler(() => stub))));
} }
/// <summary>Doesn't call GET /applications first (unlike ApplicationTests.Create) — under /// <summary>Doesn't call GET /aanvragen first (unlike ApplicationTests.Create) — under
/// Zgw:Enabled=true that route goes through IZaakSource too, which this test's stub doesn't /// Zgw:Enabled=true that route goes through IZaakSource too, which this test's stub doesn't
/// need to answer since every test here uses a fresh db and creates exactly one aanvraag.</summary> /// need to answer since every test here uses a fresh db and creates exactly one aanvraag.</summary>
private static async Task<string> CreateConcept(HttpClient client, string type = "registratie") private static async Task<string> CreateConcept(HttpClient client, string type = "registratie")
{ {
var res = await client.PostAsJsonAsync("/api/v1/applications", new { type }); var res = await client.PostAsJsonAsync("/api/v1/aanvragen", new { type });
res.EnsureSuccessStatusCode(); res.EnsureSuccessStatusCode();
var body = (await res.Content.ReadFromJsonAsync<ApplicationDetailDto>())!; var body = (await res.Content.ReadFromJsonAsync<AanvraagDetailDto>())!;
return body.Id; return body.Id;
} }
@@ -78,11 +78,11 @@ public class ZgwDivergenceTests
using var client = factory.CreateClient(); using var client = factory.CreateClient();
var id = await CreateConcept(client); var id = await CreateConcept(client);
var res = await client.PostAsJsonAsync($"/api/v1/applications/{id}/submit", new { diplomaHerkomst = "duo" }); var res = await client.PostAsJsonAsync($"/api/v1/aanvragen/{id}/submit", new { diplomaHerkomst = "duo" });
// The local write is still authoritative: 200 with a real reference, not a 500. // The local write is still authoritative: 200 with a real reference, not a 500.
res.EnsureSuccessStatusCode(); res.EnsureSuccessStatusCode();
var body = (await res.Content.ReadFromJsonAsync<SubmitApplicationResponse>())!; var body = (await res.Content.ReadFromJsonAsync<AanvraagIndienenResponse>())!;
Assert.NotEmpty(body.Referentie); Assert.NotEmpty(body.Referentie);
var stored = ApplicationStore.ListAll().Single(a => a.Id == id); var stored = ApplicationStore.ListAll().Single(a => a.Id == id);
@@ -103,7 +103,7 @@ public class ZgwDivergenceTests
using var client = factory.CreateClient(); using var client = factory.CreateClient();
var id = await CreateConcept(client); var id = await CreateConcept(client);
var res = await client.PostAsJsonAsync($"/api/v1/applications/{id}/submit", new { diplomaHerkomst = "duo" }); var res = await client.PostAsJsonAsync($"/api/v1/aanvragen/{id}/submit", new { diplomaHerkomst = "duo" });
res.EnsureSuccessStatusCode(); res.EnsureSuccessStatusCode();
var stored = ApplicationStore.ListAll().Single(a => a.Id == id); var stored = ApplicationStore.ListAll().Single(a => a.Id == id);
@@ -126,7 +126,7 @@ public class ZgwDivergenceTests
using var client = factory.CreateClient(); using var client = factory.CreateClient();
var id = await CreateConcept(client); var id = await CreateConcept(client);
(await client.PostAsJsonAsync($"/api/v1/applications/{id}/submit", new { diplomaHerkomst = "duo" })) (await client.PostAsJsonAsync($"/api/v1/aanvragen/{id}/submit", new { diplomaHerkomst = "duo" }))
.EnsureSuccessStatusCode(); .EnsureSuccessStatusCode();
var error = ApplicationStore.ListAll().Single(a => a.Id == id).ZgwError; var error = ApplicationStore.ListAll().Single(a => a.Id == id).ZgwError;
+71 -71
View File
@@ -637,8 +637,8 @@ export class ApiClient {
/** /**
* @return OK * @return OK
*/ */
applicationsAll(): Promise<ApplicationSummaryDto[]> { aanvragenAll(): Promise<AanvraagSummaryDto[]> {
let url_ = this.baseUrl + "/api/v1/applications"; let url_ = this.baseUrl + "/api/v1/aanvragen";
url_ = url_.replace(/[?&]$/, ""); url_ = url_.replace(/[?&]$/, "");
let options_: RequestInit = { let options_: RequestInit = {
@@ -649,17 +649,17 @@ export class ApiClient {
}; };
return this.http.fetch(url_, options_).then((_response: Response) => { return this.http.fetch(url_, options_).then((_response: Response) => {
return this.processApplicationsAll(_response); return this.processAanvragenAll(_response);
}); });
} }
protected processApplicationsAll(response: Response): Promise<ApplicationSummaryDto[]> { protected processAanvragenAll(response: Response): Promise<AanvraagSummaryDto[]> {
const status = response.status; const status = response.status;
let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); }; let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
if (status === 200) { if (status === 200) {
return response.text().then((_responseText) => { return response.text().then((_responseText) => {
let result200: any = null; let result200: any = null;
result200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ApplicationSummaryDto[]; result200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as AanvraagSummaryDto[];
return result200; return result200;
}); });
} else if (status !== 200 && status !== 204) { } else if (status !== 200 && status !== 204) {
@@ -667,14 +667,14 @@ export class ApiClient {
return throwException("An unexpected server error occurred.", status, _responseText, _headers); return throwException("An unexpected server error occurred.", status, _responseText, _headers);
}); });
} }
return Promise.resolve<ApplicationSummaryDto[]>(null as any); return Promise.resolve<AanvraagSummaryDto[]>(null as any);
} }
/** /**
* @return Created * @return Created
*/ */
applicationsPOST(body: CreateApplicationRequest): Promise<ApplicationDetailDto> { aanvragenPOST(body: CreateAanvraagRequest): Promise<AanvraagDetailDto> {
let url_ = this.baseUrl + "/api/v1/applications"; let url_ = this.baseUrl + "/api/v1/aanvragen";
url_ = url_.replace(/[?&]$/, ""); url_ = url_.replace(/[?&]$/, "");
const content_ = JSON.stringify(body); const content_ = JSON.stringify(body);
@@ -689,17 +689,17 @@ export class ApiClient {
}; };
return this.http.fetch(url_, options_).then((_response: Response) => { return this.http.fetch(url_, options_).then((_response: Response) => {
return this.processApplicationsPOST(_response); return this.processAanvragenPOST(_response);
}); });
} }
protected processApplicationsPOST(response: Response): Promise<ApplicationDetailDto> { protected processAanvragenPOST(response: Response): Promise<AanvraagDetailDto> {
const status = response.status; const status = response.status;
let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); }; let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
if (status === 201) { if (status === 201) {
return response.text().then((_responseText) => { return response.text().then((_responseText) => {
let result201: any = null; let result201: any = null;
result201 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ApplicationDetailDto; result201 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as AanvraagDetailDto;
return result201; return result201;
}); });
} else if (status === 409) { } else if (status === 409) {
@@ -713,14 +713,14 @@ export class ApiClient {
return throwException("An unexpected server error occurred.", status, _responseText, _headers); return throwException("An unexpected server error occurred.", status, _responseText, _headers);
}); });
} }
return Promise.resolve<ApplicationDetailDto>(null as any); return Promise.resolve<AanvraagDetailDto>(null as any);
} }
/** /**
* @return OK * @return OK
*/ */
applicationsGET(id: string): Promise<ApplicationDetailDto> { aanvragenGET(id: string): Promise<AanvraagDetailDto> {
let url_ = this.baseUrl + "/api/v1/applications/{id}"; let url_ = this.baseUrl + "/api/v1/aanvragen/{id}";
if (id === undefined || id === null) if (id === undefined || id === null)
throw new globalThis.Error("The parameter 'id' must be defined."); throw new globalThis.Error("The parameter 'id' must be defined.");
url_ = url_.replace("{id}", encodeURIComponent("" + id)); url_ = url_.replace("{id}", encodeURIComponent("" + id));
@@ -734,17 +734,17 @@ export class ApiClient {
}; };
return this.http.fetch(url_, options_).then((_response: Response) => { return this.http.fetch(url_, options_).then((_response: Response) => {
return this.processApplicationsGET(_response); return this.processAanvragenGET(_response);
}); });
} }
protected processApplicationsGET(response: Response): Promise<ApplicationDetailDto> { protected processAanvragenGET(response: Response): Promise<AanvraagDetailDto> {
const status = response.status; const status = response.status;
let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); }; let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
if (status === 200) { if (status === 200) {
return response.text().then((_responseText) => { return response.text().then((_responseText) => {
let result200: any = null; let result200: any = null;
result200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ApplicationDetailDto; result200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as AanvraagDetailDto;
return result200; return result200;
}); });
} else if (status === 404) { } else if (status === 404) {
@@ -756,14 +756,14 @@ export class ApiClient {
return throwException("An unexpected server error occurred.", status, _responseText, _headers); return throwException("An unexpected server error occurred.", status, _responseText, _headers);
}); });
} }
return Promise.resolve<ApplicationDetailDto>(null as any); return Promise.resolve<AanvraagDetailDto>(null as any);
} }
/** /**
* @return No Content * @return No Content
*/ */
applicationsPUT(id: string, body: DraftSyncRequest): Promise<void> { aanvragenPUT(id: string, body: DraftSyncRequest): Promise<void> {
let url_ = this.baseUrl + "/api/v1/applications/{id}"; let url_ = this.baseUrl + "/api/v1/aanvragen/{id}";
if (id === undefined || id === null) if (id === undefined || id === null)
throw new globalThis.Error("The parameter 'id' must be defined."); throw new globalThis.Error("The parameter 'id' must be defined.");
url_ = url_.replace("{id}", encodeURIComponent("" + id)); url_ = url_.replace("{id}", encodeURIComponent("" + id));
@@ -780,11 +780,11 @@ export class ApiClient {
}; };
return this.http.fetch(url_, options_).then((_response: Response) => { return this.http.fetch(url_, options_).then((_response: Response) => {
return this.processApplicationsPUT(_response); return this.processAanvragenPUT(_response);
}); });
} }
protected processApplicationsPUT(response: Response): Promise<void> { protected processAanvragenPUT(response: Response): Promise<void> {
const status = response.status; const status = response.status;
let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); }; let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
if (status === 204) { if (status === 204) {
@@ -812,8 +812,8 @@ export class ApiClient {
/** /**
* @return No Content * @return No Content
*/ */
applicationsDELETE(id: string): Promise<void> { aanvragenDELETE(id: string): Promise<void> {
let url_ = this.baseUrl + "/api/v1/applications/{id}"; let url_ = this.baseUrl + "/api/v1/aanvragen/{id}";
if (id === undefined || id === null) if (id === undefined || id === null)
throw new globalThis.Error("The parameter 'id' must be defined."); throw new globalThis.Error("The parameter 'id' must be defined.");
url_ = url_.replace("{id}", encodeURIComponent("" + id)); url_ = url_.replace("{id}", encodeURIComponent("" + id));
@@ -826,11 +826,11 @@ export class ApiClient {
}; };
return this.http.fetch(url_, options_).then((_response: Response) => { return this.http.fetch(url_, options_).then((_response: Response) => {
return this.processApplicationsDELETE(_response); return this.processAanvragenDELETE(_response);
}); });
} }
protected processApplicationsDELETE(response: Response): Promise<void> { protected processAanvragenDELETE(response: Response): Promise<void> {
const status = response.status; const status = response.status;
let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); }; let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
if (status === 204) { if (status === 204) {
@@ -858,8 +858,8 @@ export class ApiClient {
/** /**
* @return OK * @return OK
*/ */
submit(id: string, body: SubmitApplicationRequest): Promise<SubmitApplicationResponse> { submit(id: string, body: AanvraagIndienenRequest): Promise<AanvraagIndienenResponse> {
let url_ = this.baseUrl + "/api/v1/applications/{id}/submit"; let url_ = this.baseUrl + "/api/v1/aanvragen/{id}/submit";
if (id === undefined || id === null) if (id === undefined || id === null)
throw new globalThis.Error("The parameter 'id' must be defined."); throw new globalThis.Error("The parameter 'id' must be defined.");
url_ = url_.replace("{id}", encodeURIComponent("" + id)); url_ = url_.replace("{id}", encodeURIComponent("" + id));
@@ -881,13 +881,13 @@ export class ApiClient {
}); });
} }
protected processSubmit(response: Response): Promise<SubmitApplicationResponse> { protected processSubmit(response: Response): Promise<AanvraagIndienenResponse> {
const status = response.status; const status = response.status;
let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); }; let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
if (status === 200) { if (status === 200) {
return response.text().then((_responseText) => { return response.text().then((_responseText) => {
let result200: any = null; let result200: any = null;
result200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as SubmitApplicationResponse; result200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as AanvraagIndienenResponse;
return result200; return result200;
}); });
} else if (status === 400) { } else if (status === 400) {
@@ -911,13 +911,13 @@ export class ApiClient {
return throwException("An unexpected server error occurred.", status, _responseText, _headers); return throwException("An unexpected server error occurred.", status, _responseText, _headers);
}); });
} }
return Promise.resolve<SubmitApplicationResponse>(null as any); return Promise.resolve<AanvraagIndienenResponse>(null as any);
} }
/** /**
* @return OK * @return OK
*/ */
casesAll(): Promise<ApplicationSummaryDto[]> { casesAll(): Promise<AanvraagSummaryDto[]> {
let url_ = this.baseUrl + "/api/v1/admin/cases"; let url_ = this.baseUrl + "/api/v1/admin/cases";
url_ = url_.replace(/[?&]$/, ""); url_ = url_.replace(/[?&]$/, "");
@@ -933,13 +933,13 @@ export class ApiClient {
}); });
} }
protected processCasesAll(response: Response): Promise<ApplicationSummaryDto[]> { protected processCasesAll(response: Response): Promise<AanvraagSummaryDto[]> {
const status = response.status; const status = response.status;
let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); }; let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
if (status === 200) { if (status === 200) {
return response.text().then((_responseText) => { return response.text().then((_responseText) => {
let result200: any = null; let result200: any = null;
result200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ApplicationSummaryDto[]; result200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as AanvraagSummaryDto[];
return result200; return result200;
}); });
} else if (status === 403) { } else if (status === 403) {
@@ -953,7 +953,7 @@ export class ApiClient {
return throwException("An unexpected server error occurred.", status, _responseText, _headers); return throwException("An unexpected server error occurred.", status, _responseText, _headers);
}); });
} }
return Promise.resolve<ApplicationSummaryDto[]>(null as any); return Promise.resolve<AanvraagSummaryDto[]>(null as any);
} }
/** /**
@@ -1047,7 +1047,7 @@ export class ApiClient {
/** /**
* @return OK * @return OK
*/ */
werkvoorraad(): Promise<ApplicationSummaryDto[]> { werkvoorraad(): Promise<AanvraagSummaryDto[]> {
let url_ = this.baseUrl + "/api/v1/werkvoorraad"; let url_ = this.baseUrl + "/api/v1/werkvoorraad";
url_ = url_.replace(/[?&]$/, ""); url_ = url_.replace(/[?&]$/, "");
@@ -1063,13 +1063,13 @@ export class ApiClient {
}); });
} }
protected processWerkvoorraad(response: Response): Promise<ApplicationSummaryDto[]> { protected processWerkvoorraad(response: Response): Promise<AanvraagSummaryDto[]> {
const status = response.status; const status = response.status;
let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); }; let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
if (status === 200) { if (status === 200) {
return response.text().then((_responseText) => { return response.text().then((_responseText) => {
let result200: any = null; let result200: any = null;
result200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ApplicationSummaryDto[]; result200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as AanvraagSummaryDto[];
return result200; return result200;
}); });
} else if (status === 403) { } else if (status === 403) {
@@ -1083,7 +1083,7 @@ export class ApiClient {
return throwException("An unexpected server error occurred.", status, _responseText, _headers); return throwException("An unexpected server error occurred.", status, _responseText, _headers);
}); });
} }
return Promise.resolve<ApplicationSummaryDto[]>(null as any); return Promise.resolve<AanvraagSummaryDto[]>(null as any);
} }
/** /**
@@ -1898,22 +1898,7 @@ export interface AantekeningDto {
datum?: string | undefined; datum?: string | undefined;
} }
export interface AanvraagStatusDto { export interface AanvraagDetailDto {
tag?: string | undefined;
stepIndex?: number | undefined;
stepCount?: number | undefined;
referentie?: string | undefined;
manual?: boolean | undefined;
reden?: string | undefined;
}
export interface AdresDto {
straat?: string | undefined;
postcode?: string | undefined;
woonplaats?: string | undefined;
}
export interface ApplicationDetailDto {
id?: string | undefined; id?: string | undefined;
type?: string | undefined; type?: string | undefined;
status?: AanvraagStatusDto; status?: AanvraagStatusDto;
@@ -1924,7 +1909,29 @@ export interface ApplicationDetailDto {
submittedAt?: string | undefined; submittedAt?: string | undefined;
} }
export interface ApplicationSummaryDto { export interface AanvraagIndienenRequest {
diplomaHerkomst?: string | undefined;
uren?: number | undefined;
documents?: DocumentRefDto[] | undefined;
aanvullendeScholing?: boolean | undefined;
scholingPunten?: number | undefined;
}
export interface AanvraagIndienenResponse {
referentie?: string | undefined;
status?: AanvraagStatusDto;
}
export interface AanvraagStatusDto {
tag?: string | undefined;
stepIndex?: number | undefined;
stepCount?: number | undefined;
referentie?: string | undefined;
manual?: boolean | undefined;
reden?: string | undefined;
}
export interface AanvraagSummaryDto {
id?: string | undefined; id?: string | undefined;
type?: string | undefined; type?: string | undefined;
status?: AanvraagStatusDto; status?: AanvraagStatusDto;
@@ -1935,6 +1942,12 @@ export interface ApplicationSummaryDto {
owner?: string | undefined; owner?: string | undefined;
} }
export interface AdresDto {
straat?: string | undefined;
postcode?: string | undefined;
woonplaats?: string | undefined;
}
export interface AuthzAuditDto { export interface AuthzAuditDto {
at?: string | undefined; at?: string | undefined;
action?: string | undefined; action?: string | undefined;
@@ -1955,7 +1968,7 @@ export interface BeoordelingDocumentDto {
} }
export interface BeoordelingViewDto { export interface BeoordelingViewDto {
aanvraag?: ApplicationSummaryDto; aanvraag?: AanvraagSummaryDto;
documenten?: BeoordelingDocumentDto[] | undefined; documenten?: BeoordelingDocumentDto[] | undefined;
decisions?: BeoordelingDecisionsDto; decisions?: BeoordelingDecisionsDto;
} }
@@ -2014,7 +2027,7 @@ export interface ChangeRequestRequest {
telefoon?: string | undefined; telefoon?: string | undefined;
} }
export interface CreateApplicationRequest { export interface CreateAanvraagRequest {
type?: string | undefined; type?: string | undefined;
} }
@@ -2275,19 +2288,6 @@ export interface SubOrgSummaryDto {
publishedVersion?: number; publishedVersion?: number;
} }
export interface SubmitApplicationRequest {
diplomaHerkomst?: string | undefined;
uren?: number | undefined;
documents?: DocumentRefDto[] | undefined;
aanvullendeScholing?: boolean | undefined;
scholingPunten?: number | undefined;
}
export interface SubmitApplicationResponse {
referentie?: string | undefined;
status?: AanvraagStatusDto;
}
export interface UploadCategoriesDto { export interface UploadCategoriesDto {
categories?: DocumentCategoryDto[] | undefined; categories?: DocumentCategoryDto[] | undefined;
} }