diff --git a/services/domain/Big.Application/Ports.cs b/services/domain/Big.Application/Ports.cs
index 08fdbde..70ec270 100644
--- a/services/domain/Big.Application/Ports.cs
+++ b/services/domain/Big.Application/Ports.cs
@@ -107,6 +107,13 @@ public interface IRegistrationStore
/// registration, or null if they have none in flight. Lets the self-service portal resume
/// an existing registration after a refresh (S-26); terminal registrations are not resumed.
Task FindOpenByBsnAsync(string bsn, CancellationToken ct = default);
+
+ /// The inscriptions whose herregistratie reminder is due as of and
+ /// not yet sent — the herregistratie reminder sweep's candidate set (S-17). The predicate is the
+ /// aggregate's own rule, so the store never
+ /// duplicates the herregistratie policy.
+ Task> FindDueForHerregistratieReminderAsync(
+ DateTimeOffset asOf, CancellationToken ct = default);
}
///
diff --git a/services/domain/Big.Infrastructure/InMemoryRegistrationStore.cs b/services/domain/Big.Infrastructure/InMemoryRegistrationStore.cs
index 086dc81..75e630c 100644
--- a/services/domain/Big.Infrastructure/InMemoryRegistrationStore.cs
+++ b/services/domain/Big.Infrastructure/InMemoryRegistrationStore.cs
@@ -26,4 +26,8 @@ public sealed class InMemoryRegistrationStore : IRegistrationStore
public Task FindOpenByBsnAsync(string bsn, CancellationToken ct = default)
=> Task.FromResult(_byId.Values.FirstOrDefault(r =>
r.Bsn == bsn && r.Status is RegistrationStatus.Ingediend or RegistrationStatus.InBehandeling));
+
+ public Task> FindDueForHerregistratieReminderAsync(
+ DateTimeOffset asOf, CancellationToken ct = default)
+ => Task.FromResult>([]); // RED stub
}
diff --git a/services/domain/Big.Tests/Fakes.cs b/services/domain/Big.Tests/Fakes.cs
index c9ff30f..623c331 100644
--- a/services/domain/Big.Tests/Fakes.cs
+++ b/services/domain/Big.Tests/Fakes.cs
@@ -26,6 +26,11 @@ internal sealed class FakeRegistrationStore : IRegistrationStore
=> Task.FromResult(_byId.Values.FirstOrDefault(r =>
r.Bsn == bsn && r.Status is RegistrationStatus.Ingediend or RegistrationStatus.InBehandeling));
+ public Task> FindDueForHerregistratieReminderAsync(
+ DateTimeOffset asOf, CancellationToken ct = default)
+ => Task.FromResult>(
+ _byId.Values.Where(r => r.HerregistratieReminderDue(asOf)).ToList());
+
public void Seed(Registration registration) => _byId[registration.Id] = registration;
}
diff --git a/services/domain/Big.Tests/InMemoryRegistrationStoreTests.cs b/services/domain/Big.Tests/InMemoryRegistrationStoreTests.cs
index 284da9f..dc21fa7 100644
--- a/services/domain/Big.Tests/InMemoryRegistrationStoreTests.cs
+++ b/services/domain/Big.Tests/InMemoryRegistrationStoreTests.cs
@@ -96,4 +96,27 @@ public class InMemoryRegistrationStoreTests
Assert.Null(await store.FindOpenByBsnAsync("123456782"));
}
+
+ [Fact]
+ public async Task Finds_only_the_inscriptions_due_for_a_herregistratie_reminder()
+ {
+ var now = new DateTimeOffset(2026, 7, 23, 0, 0, 0, TimeSpan.Zero);
+ var store = new InMemoryRegistrationStore();
+
+ var due = Registration.Submit("123456782");
+ due.AttachZaak(new Uri("http://openzaak/zaken/api/v1/zaken/abc"));
+ due.Approve(now - Registration.HerregistratieGeldigheid + Registration.Herinneringstermijn);
+ await store.SaveAsync(due);
+
+ var freshlyInscribed = Registration.Submit("111111110");
+ freshlyInscribed.AttachZaak(new Uri("http://openzaak/zaken/api/v1/zaken/def"));
+ freshlyInscribed.Approve(now);
+ await store.SaveAsync(freshlyInscribed);
+
+ await store.SaveAsync(Registration.Submit("222222222")); // still INGEDIEND — never inscribed
+
+ var result = await store.FindDueForHerregistratieReminderAsync(now);
+
+ Assert.Equal([due.Id], result.Select(r => r.Id).ToArray());
+ }
}