feat(registratie): WP-34 — phone field + BRP address read-only
Reshape the adreswijziging form into a contact-change form: the BRP address is
authoritative and shown read-only (you change it at the gemeente), and the phone
number becomes the editable/submittable field. New Telefoonnummer value object
(parse-don't-validate); backend RejectPhoneChange re-validates as authority.
POST /change-requests now carries { telefoon } (typed client regenerated).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -77,7 +77,7 @@ public sealed record DocumentRefDto(string CategoryId, string Channel, string? D
|
||||
public sealed record RegistratieRequest(string DiplomaHerkomst, IReadOnlyList<DocumentRefDto>? Documents = null);
|
||||
public sealed record IntakeRequest(int Uren);
|
||||
public sealed record HerregistratieRequest(int Uren, IReadOnlyList<DocumentRefDto>? Documents = null);
|
||||
public sealed record ChangeRequestRequest(string Straat, string Postcode, string Woonplaats);
|
||||
public sealed record ChangeRequestRequest(string Telefoon);
|
||||
|
||||
public sealed record ReferentieResponse(string Referentie);
|
||||
|
||||
|
||||
@@ -19,15 +19,17 @@ public static class SubmissionRules
|
||||
public static string? RejectZeroUren(int uren) =>
|
||||
uren == 0 ? "Aanvraag afgewezen: geen gewerkte uren geregistreerd." : null;
|
||||
|
||||
private static readonly Regex PostcodePattern =
|
||||
new(@"^[1-9]\d{3}\s?[A-Z]{2}$", RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||
private static readonly Regex PhonePattern =
|
||||
new(@"^0\d{9}$", RegexOptions.Compiled);
|
||||
|
||||
// RULE: a change request needs a street and a well-formed Dutch postcode. The
|
||||
// server re-validates format authoritatively (the FE check is UX-only).
|
||||
public static string? RejectChangeRequest(string straat, string postcode)
|
||||
// RULE: a contact change needs a well-formed Dutch phone number (10 digits, leading
|
||||
// 0, formatting stripped). The BRP address is authoritative and cannot be changed
|
||||
// here (WP-34), so only the phone is submitted. The server re-validates format
|
||||
// authoritatively (the FE check is UX-only).
|
||||
public static string? RejectPhoneChange(string telefoon)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(straat)) return "Vul straat en huisnummer in.";
|
||||
if (!PostcodePattern.IsMatch(postcode?.Trim() ?? "")) return "Voer een geldige postcode in, bijv. 1234 AB.";
|
||||
var digits = (telefoon ?? "").Trim().Replace(" ", "").Replace("-", "");
|
||||
if (!PhonePattern.IsMatch(digits)) return "Voer een geldig telefoonnummer in, bijv. 0612345678.";
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -146,7 +146,7 @@ api.MapPost("/intakes", (IntakeRequest req, HttpContext ctx) =>
|
||||
.ProducesProblem(StatusCodes.Status422UnprocessableEntity);
|
||||
|
||||
api.MapPost("/change-requests", (ChangeRequestRequest req, HttpContext ctx) =>
|
||||
Submit(ctx, "adreswijziging", SubmissionRules.RejectChangeRequest(req.Straat, req.Postcode)))
|
||||
Submit(ctx, "telefoonwijziging", SubmissionRules.RejectPhoneChange(req.Telefoon)))
|
||||
.Produces<ReferentieResponse>()
|
||||
.ProducesProblem(StatusCodes.Status422UnprocessableEntity);
|
||||
|
||||
|
||||
@@ -1528,15 +1528,7 @@
|
||||
"ChangeRequestRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"straat": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"postcode": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"woonplaats": {
|
||||
"telefoon": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
}
|
||||
|
||||
@@ -101,20 +101,20 @@ public class EndpointTests(TestWebApplicationFactory factory) : IClassFixture<Te
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Change_request_with_valid_address_succeeds()
|
||||
public async Task Change_request_with_valid_phone_succeeds()
|
||||
{
|
||||
var res = await _client.PostAsJsonAsync("/api/v1/change-requests",
|
||||
new { straat = "Lange Voorhout 9", postcode = "2514 EA", woonplaats = "Den Haag" });
|
||||
new { telefoon = "0612345678" });
|
||||
res.EnsureSuccessStatusCode();
|
||||
var body = await res.Content.ReadFromJsonAsync<ReferentieResponse>();
|
||||
Assert.StartsWith("BIG-2026-", body!.Referentie);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Change_request_with_bad_postcode_is_rejected()
|
||||
public async Task Change_request_with_bad_phone_is_rejected()
|
||||
{
|
||||
var res = await _client.PostAsJsonAsync("/api/v1/change-requests",
|
||||
new { straat = "Straat 1", postcode = "nope", woonplaats = "Den Haag" });
|
||||
new { telefoon = "nope" });
|
||||
Assert.Equal(HttpStatusCode.UnprocessableEntity, res.StatusCode);
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ public class IdempotencyTests(TestWebApplicationFactory factory) : IClassFixture
|
||||
{
|
||||
var req = new HttpRequestMessage(HttpMethod.Post, "/api/v1/change-requests")
|
||||
{
|
||||
Content = JsonContent.Create(new { straat = "Lange Voorhout 9", postcode = "2514 EA", woonplaats = "Den Haag" }),
|
||||
Content = JsonContent.Create(new { telefoon = "0612345678" }),
|
||||
};
|
||||
req.Headers.Add("Idempotency-Key", key);
|
||||
return req;
|
||||
@@ -51,7 +51,7 @@ public class IdempotencyTests(TestWebApplicationFactory factory) : IClassFixture
|
||||
var key = Guid.NewGuid().ToString();
|
||||
var badRequest = new HttpRequestMessage(HttpMethod.Post, "/api/v1/change-requests")
|
||||
{
|
||||
Content = JsonContent.Create(new { straat = "Straat 1", postcode = "nope", woonplaats = "Den Haag" }),
|
||||
Content = JsonContent.Create(new { telefoon = "nope" }),
|
||||
};
|
||||
badRequest.Headers.Add("Idempotency-Key", key);
|
||||
|
||||
@@ -60,7 +60,7 @@ public class IdempotencyTests(TestWebApplicationFactory factory) : IClassFixture
|
||||
|
||||
var replayRequest = new HttpRequestMessage(HttpMethod.Post, "/api/v1/change-requests")
|
||||
{
|
||||
Content = JsonContent.Create(new { straat = "Straat 1", postcode = "nope", woonplaats = "Den Haag" }),
|
||||
Content = JsonContent.Create(new { telefoon = "nope" }),
|
||||
};
|
||||
replayRequest.Headers.Add("Idempotency-Key", key);
|
||||
var replay = await _client.SendAsync(replayRequest);
|
||||
|
||||
@@ -172,9 +172,10 @@ public class SubmissionRuleTests
|
||||
Assert.Null(SubmissionRules.RejectZeroUren(40));
|
||||
|
||||
[Theory]
|
||||
[InlineData("Lange Voorhout 9", "2514 EA", null)] // valid
|
||||
[InlineData("", "2514 EA", "Vul straat en huisnummer in.")]
|
||||
[InlineData("Straat 1", "nope", "Voer een geldige postcode in, bijv. 1234 AB.")]
|
||||
public void Change_request_is_validated(string straat, string postcode, string? expected) =>
|
||||
Assert.Equal(expected, SubmissionRules.RejectChangeRequest(straat, postcode));
|
||||
[InlineData("0612345678", null)] // valid mobile
|
||||
[InlineData("070 123 45 67", null)] // valid landline, formatting stripped
|
||||
[InlineData("nope", "Voer een geldig telefoonnummer in, bijv. 0612345678.")]
|
||||
[InlineData("12345", "Voer een geldig telefoonnummer in, bijv. 0612345678.")]
|
||||
public void Phone_change_is_validated(string telefoon, string? expected) =>
|
||||
Assert.Equal(expected, SubmissionRules.RejectPhoneChange(telefoon));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user