diff --git a/services/domain/Big.Application/Ports.cs b/services/domain/Big.Application/Ports.cs index 8e74b72..b89ac82 100644 --- a/services/domain/Big.Application/Ports.cs +++ b/services/domain/Big.Application/Ports.cs @@ -11,10 +11,12 @@ public interface IWorkflowClient { /// /// Start one registratie process instance for the given registration, carrying the - /// registration id so the OpenZaakAanmaken external task can be correlated back to its - /// aggregate. Returns the process instance id. + /// registration id (so the OpenZaakAanmaken external task can be correlated back to its + /// aggregate) and the diploma origin (so the workflow's DMN can route foreign diplomas through + /// CBGV-advies, S-13). Returns the process instance id. /// - Task StartRegistrationProcessAsync(RegistrationId registrationId, CancellationToken ct = default); + Task StartRegistrationProcessAsync( + RegistrationId registrationId, DiplomaOrigin diplomaOrigin, CancellationToken ct = default); /// /// Cancel a running registratie process on withdrawal (S-11): correlate the diff --git a/services/domain/Big.Application/SubmitRegistration.cs b/services/domain/Big.Application/SubmitRegistration.cs index 3e2f55f..1f70e9e 100644 --- a/services/domain/Big.Application/SubmitRegistration.cs +++ b/services/domain/Big.Application/SubmitRegistration.cs @@ -2,8 +2,10 @@ using Big.Domain; namespace Big.Application; -/// A zorgprofessional's request to register, in domain language. No ZGW concepts. -public sealed record SubmitRegistrationCommand(string Bsn); +/// A zorgprofessional's request to register, in domain language. No ZGW concepts. The +/// diploma origin defaults to domestic (the DigiD path); a foreign (eIDAS) submission sets it to +/// so the workflow's DMN routes it through CBGV-advies (S-13). +public sealed record SubmitRegistrationCommand(string Bsn, DiplomaOrigin DiplomaOrigin = DiplomaOrigin.Binnenlands); /// /// The submit use case: create the aggregate (INGEDIEND), persist it, @@ -18,13 +20,14 @@ public sealed class SubmitRegistration(IRegistrationStore store, IWorkflowClient { ArgumentNullException.ThrowIfNull(command); - var registration = Registration.Submit(command.Bsn); + var registration = Registration.Submit(command.Bsn, command.DiplomaOrigin); // Persist before starting the process so the worker can correlate the OpenZaakAanmaken // job back to an aggregate that already exists (ADR-0009). await store.SaveAsync(registration, ct); - var processInstanceId = await workflow.StartRegistrationProcessAsync(registration.Id, ct); + var processInstanceId = await workflow.StartRegistrationProcessAsync( + registration.Id, registration.DiplomaOrigin, ct); registration.RecordProcessStarted(processInstanceId); await store.SaveAsync(registration, ct); diff --git a/services/domain/Big.Domain/DiplomaOrigin.cs b/services/domain/Big.Domain/DiplomaOrigin.cs new file mode 100644 index 0000000..74812dc --- /dev/null +++ b/services/domain/Big.Domain/DiplomaOrigin.cs @@ -0,0 +1,17 @@ +namespace Big.Domain; + +/// +/// Where a zorgprofessional's diploma was issued. It is the input to the diploma-eligibility decision +/// (S-13): a (foreign) diploma routes the registratie through an extra +/// CBGV-advies assessment step, a (domestic) one goes straight to beoordeling. +/// The decision itself lives in the workflow's DMN, not here (ADR-0016); the domain only carries the +/// origin and hands it to the process as a start variable. +/// +public enum DiplomaOrigin +{ + /// A Dutch (domestic) diploma. Default for a registration submitted via DigiD. + Binnenlands, + + /// A foreign diploma (e.g. an eIDAS submission). Triggers the CBGV-advies step. + Buitenlands, +} diff --git a/services/domain/Big.Domain/Registration.cs b/services/domain/Big.Domain/Registration.cs index 4b29eab..51f20cf 100644 --- a/services/domain/Big.Domain/Registration.cs +++ b/services/domain/Big.Domain/Registration.cs @@ -7,10 +7,11 @@ namespace Big.Domain; /// public sealed class Registration { - private Registration(RegistrationId id, string bsn) + private Registration(RegistrationId id, string bsn, DiplomaOrigin diplomaOrigin) { Id = id; Bsn = bsn; + DiplomaOrigin = diplomaOrigin; Status = RegistrationStatus.Ingediend; } @@ -20,6 +21,10 @@ public sealed class Registration /// as the domain payload; the domain never constructs ZGW concepts from it (§8.1). public string Bsn { get; } + /// Where the diploma was issued. Rides along to the process as a start variable and + /// drives the diploma-eligibility DMN's foreign→CBGV-advies routing (S-13, ADR-0016). + public DiplomaOrigin DiplomaOrigin { get; } + public RegistrationStatus Status { get; private set; } /// The Flowable process instance driving this registration, once started. @@ -28,11 +33,13 @@ public sealed class Registration /// The zaak the ACL opened for this registration, once the external task has run. public Uri? ZaakUrl { get; private set; } - /// Submit a new registration. It begins in . - public static Registration Submit(string bsn) + /// Submit a new registration. It begins in . + /// The diploma origin defaults to — the common DigiD path; + /// a foreign (eIDAS) submission passes . + public static Registration Submit(string bsn, DiplomaOrigin diplomaOrigin = DiplomaOrigin.Binnenlands) { ArgumentException.ThrowIfNullOrWhiteSpace(bsn); - return new Registration(RegistrationId.New(), bsn); + return new Registration(RegistrationId.New(), bsn, diplomaOrigin); } /// Record that the registratie workflow process has been started for this registration. diff --git a/services/domain/Big.Infrastructure/FlowableWorkflowClient.cs b/services/domain/Big.Infrastructure/FlowableWorkflowClient.cs index d8933e2..1d825c8 100644 --- a/services/domain/Big.Infrastructure/FlowableWorkflowClient.cs +++ b/services/domain/Big.Infrastructure/FlowableWorkflowClient.cs @@ -28,7 +28,8 @@ public sealed class FlowableWorkflowClient(HttpClient http, FlowableOptions opti private const string BesluitVariable = "besluit"; private const string IngetrokkenMessage = "RegistratieIngetrokken"; - public async Task StartRegistrationProcessAsync(RegistrationId registrationId, CancellationToken ct = default) + public async Task StartRegistrationProcessAsync( + RegistrationId registrationId, DiplomaOrigin diplomaOrigin, CancellationToken ct = default) { var request = new StartProcessRequest( ProcessDefinitionKey, diff --git a/services/domain/Big.Tests/Fakes.cs b/services/domain/Big.Tests/Fakes.cs index 539f554..e9c6953 100644 --- a/services/domain/Big.Tests/Fakes.cs +++ b/services/domain/Big.Tests/Fakes.cs @@ -32,12 +32,15 @@ internal sealed class FakeWorkflowClient(string processInstanceId = "proc-1", Ac : IWorkflowClient { public RegistrationId? StartedFor { get; private set; } + public DiplomaOrigin? StartedWithOrigin { get; private set; } public string? WithdrawnProcessInstanceId { get; private set; } - public Task StartRegistrationProcessAsync(RegistrationId registrationId, CancellationToken ct = default) + public Task StartRegistrationProcessAsync( + RegistrationId registrationId, DiplomaOrigin diplomaOrigin, CancellationToken ct = default) { onStart?.Invoke(registrationId); StartedFor = registrationId; + StartedWithOrigin = diplomaOrigin; return Task.FromResult(processInstanceId); } diff --git a/services/domain/Big.Tests/FlowableWorkflowClientTests.cs b/services/domain/Big.Tests/FlowableWorkflowClientTests.cs index d852e11..b67d9d7 100644 --- a/services/domain/Big.Tests/FlowableWorkflowClientTests.cs +++ b/services/domain/Big.Tests/FlowableWorkflowClientTests.cs @@ -24,7 +24,7 @@ public class FlowableWorkflowClientTests var client = Client(capture.Responds(HttpStatusCode.Created, """{"id":"pi-1"}""")); var rid = RegistrationId.New(); - var pid = await client.StartRegistrationProcessAsync(rid); + var pid = await client.StartRegistrationProcessAsync(rid, DiplomaOrigin.Binnenlands); Assert.Equal("pi-1", pid); Assert.Equal(HttpMethod.Post, capture.Seen!.Method); @@ -38,6 +38,22 @@ public class FlowableWorkflowClientTests Assert.Contains($"\"value\":\"{rid}\"", capture.Body); } + [Theory] + [InlineData(DiplomaOrigin.Binnenlands, "Binnenlands")] + [InlineData(DiplomaOrigin.Buitenlands, "Buitenlands")] + public async Task Start_posts_the_diploma_origin_as_a_process_variable(DiplomaOrigin origin, string expected) + { + // The diploma origin rides along as a start variable so the workflow's DMN can route foreign + // diplomas through CBGV-advies (S-13, ADR-0016). + var capture = new RequestCapture(); + var client = Client(capture.Responds(HttpStatusCode.Created, """{"id":"pi-1"}""")); + + await client.StartRegistrationProcessAsync(RegistrationId.New(), origin); + + Assert.Contains("\"name\":\"diplomaOrigin\"", capture.Body); + Assert.Contains($"\"value\":\"{expected}\"", capture.Body); + } + [Fact] public async Task Start_uses_the_configured_worker_credentials_and_defaults() { @@ -125,7 +141,7 @@ public class FlowableWorkflowClientTests var client = Client(capture.Responds(HttpStatusCode.InternalServerError)); await Assert.ThrowsAsync( - () => client.StartRegistrationProcessAsync(RegistrationId.New())); + () => client.StartRegistrationProcessAsync(RegistrationId.New(), DiplomaOrigin.Binnenlands)); } [Fact] @@ -135,7 +151,7 @@ public class FlowableWorkflowClientTests var client = Client(capture.Responds(HttpStatusCode.Created, "null")); var ex = await Assert.ThrowsAsync( - () => client.StartRegistrationProcessAsync(RegistrationId.New())); + () => client.StartRegistrationProcessAsync(RegistrationId.New(), DiplomaOrigin.Binnenlands)); Assert.Contains("empty process-instance", ex.Message); } diff --git a/tests/acceptance/Support/InMemoryDomainPorts.cs b/tests/acceptance/Support/InMemoryDomainPorts.cs index 4926b21..af07de0 100644 --- a/tests/acceptance/Support/InMemoryDomainPorts.cs +++ b/tests/acceptance/Support/InMemoryDomainPorts.cs @@ -12,11 +12,14 @@ public sealed class InMemoryWorkflowClient : IWorkflowClient public const string StartedProcessInstanceId = "proc-acc-1"; public RegistrationId? StartedFor { get; private set; } + public DiplomaOrigin? StartedWithOrigin { get; private set; } public string? WithdrawnProcessInstanceId { get; private set; } - public Task StartRegistrationProcessAsync(RegistrationId registrationId, CancellationToken ct = default) + public Task StartRegistrationProcessAsync( + RegistrationId registrationId, DiplomaOrigin diplomaOrigin, CancellationToken ct = default) { StartedFor = registrationId; + StartedWithOrigin = diplomaOrigin; return Task.FromResult(StartedProcessInstanceId); }