feat(stamdata): extract policy-question text into Stamdata
CI / frontend (push) Failing after 1m21s
CI / storybook-a11y (push) Failing after 5m27s
CI / backend (push) Successful in 1m35s
CI / codeql (csharp) (push) Failing after 1m50s
CI / codeql (javascript-typescript) (push) Failing after 1m30s
CI / api-client-drift (push) Successful in 2m1s
CI / e2e (push) Failing after 3h14m48s

Move the geldigheidsvragen wording out of DiplomaRules into
Stamdata.PolicyQuestions (business-editable text, config-as-code); DiplomaRules
keeps only the rule of which questions apply. Extend StamdataValidationTests
(no blank id/wording, distinct ids in the manual set) and update ADR-0004.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
eho
2026-07-21 07:46:38 +02:00
co-authored by Claude Opus 4.8
parent a7f737e18c
commit c459fa0a60
4 changed files with 71 additions and 32 deletions
@@ -19,44 +19,20 @@ public static class DiplomaRules
/// <summary>Professions a user may declare for a manual (unlisted) diploma.</summary> /// <summary>Professions a user may declare for a manual (unlisted) diploma.</summary>
public static IReadOnlyList<string> ManualProfessions() => Professions.All(); public static IReadOnlyList<string> ManualProfessions() => Professions.All();
// --- Policy questions (geldigheidsvragen) ---
private static readonly PolicyQuestion NlTaalEngelstalig = new(
"nl-taalvaardigheid",
"Uw opleiding was Engelstalig. Beheerst u de Nederlandse taal op het vereiste niveau (B2)?",
QuestionType.JaNee);
private static readonly PolicyQuestion NlTaalManual = new(
"nl-taalvaardigheid",
"Beheerst u de Nederlandse taal op het vereiste niveau (B2)?",
QuestionType.JaNee);
private static readonly PolicyQuestion DiplomaErkend = new(
"diploma-erkend",
"Is uw diploma erkend door de Nederlandse overheid (bijv. via Nuffic)?",
QuestionType.JaNee);
private static readonly PolicyQuestion Toelichting = new(
"toelichting",
"Geef een korte toelichting op uw diploma en opleiding.",
QuestionType.Tekst);
/// <summary> /// <summary>
/// RULE: an English-language diploma requires proof of Dutch proficiency (B2). /// RULE: an English-language diploma requires proof of Dutch proficiency (B2). Which
/// Add a question here to apply it to a (set of) diploma(s) — a single backend /// question applies is the rule; its wording is stamdata in <see cref="PolicyQuestions"/>.
/// change, no frontend change.
/// </summary> /// </summary>
public static IReadOnlyList<PolicyQuestion> QuestionsFor(Diploma d) public static IReadOnlyList<PolicyQuestion> QuestionsFor(Diploma d)
{ {
var questions = new List<PolicyQuestion>(); var questions = new List<PolicyQuestion>();
if (d.Engelstalig) if (d.Engelstalig)
questions.Add(NlTaalEngelstalig); questions.Add(PolicyQuestions.NlTaalEngelstalig);
return questions; return questions;
} }
/// <summary> /// <summary>
/// RULE: a manual diploma is unverified, so the strictest (maximal) set applies. /// RULE: a manual diploma is unverified, so the strictest (maximal) set applies.
/// </summary> /// </summary>
public static IReadOnlyList<PolicyQuestion> ManualQuestions() => public static IReadOnlyList<PolicyQuestion> ManualQuestions() => PolicyQuestions.ManualSet;
new[] { NlTaalManual, DiplomaErkend, Toelichting };
} }
@@ -0,0 +1,45 @@
using BigRegister.Domain.Diplomas;
namespace BigRegister.Stamdata;
/// <summary>
/// BUSINESS-EDITABLE STAMDATA (config-as-code). The geldigheidsvragen (policy questions)
/// shown for a diploma, and their exact wording. This is the text the business tunes.
///
/// Change it by editing this file and opening a PR — NOT via a production database. The
/// compiler catches shape/type mistakes; <c>StamdataValidationTests</c> catches the rest
/// (blank ids/wording, duplicate ids). See ADR-0004.
///
/// This is DATA, not logic: WHICH questions apply to which diploma (English → B2, manual →
/// maximal set) is a rule and stays in <c>DiplomaRules</c>.
/// </summary>
public static class PolicyQuestions
{
public static readonly PolicyQuestion NlTaalEngelstalig = new(
"nl-taalvaardigheid",
"Uw opleiding was Engelstalig. Beheerst u de Nederlandse taal op het vereiste niveau (B2)?",
QuestionType.JaNee);
public static readonly PolicyQuestion NlTaalManual = new(
"nl-taalvaardigheid",
"Beheerst u de Nederlandse taal op het vereiste niveau (B2)?",
QuestionType.JaNee);
public static readonly PolicyQuestion DiplomaErkend = new(
"diploma-erkend",
"Is uw diploma erkend door de Nederlandse overheid (bijv. via Nuffic)?",
QuestionType.JaNee);
public static readonly PolicyQuestion Toelichting = new(
"toelichting",
"Geef een korte toelichting op uw diploma en opleiding.",
QuestionType.Tekst);
/// <summary>The maximal set applied to an unverified (manual) diploma.</summary>
public static readonly IReadOnlyList<PolicyQuestion> ManualSet =
new[] { NlTaalManual, DiplomaErkend, Toelichting };
/// <summary>Every question defined here — used by the stamdata validation gate.</summary>
public static readonly IReadOnlyList<PolicyQuestion> All =
new[] { NlTaalEngelstalig, NlTaalManual, DiplomaErkend, Toelichting };
}
@@ -38,4 +38,21 @@ public class StamdataValidationTests
Assert.NotEmpty(professions); Assert.NotEmpty(professions);
Assert.Equal(professions.Count, professions.Distinct().Count()); Assert.Equal(professions.Count, professions.Distinct().Count());
} }
[Fact]
public void Every_policy_question_has_an_id_and_wording()
{
Assert.All(PolicyQuestions.All, q =>
{
Assert.False(string.IsNullOrWhiteSpace(q.Id), "A policy question has a blank id.");
Assert.False(string.IsNullOrWhiteSpace(q.Vraag), $"Policy question '{q.Id}' has blank wording.");
});
}
[Fact]
public void Manual_question_set_has_distinct_ids() // no field is asked twice
{
var ids = PolicyQuestions.ManualSet.Select(q => q.Id).ToList();
Assert.Equal(ids.Count, ids.Distinct().Count());
}
} }
@@ -75,7 +75,8 @@ everyone. Stamdata (the rules and reference tables the whole register runs on) s
- **** A change needs the PR pipeline — not instant, and a non-developer may need dev - **** A change needs the PR pipeline — not instant, and a non-developer may need dev
assistance to edit C# (mitigated later by a low-code editor that emits a PR, or by a assistance to edit C# (mitigated later by a low-code editor that emits a PR, or by a
data-file format if hand-editing ergonomics ever outweigh maximal compile-time safety). data-file format if hand-editing ergonomics ever outweigh maximal compile-time safety).
- **Pilot shipped with this ADR:** the profession↔diploma map extracted to - **Shipped with this ADR:** the profession↔diploma map (`Stamdata/Professions.cs`) and the
`Stamdata/Professions.cs`, `DiplomaRules` refactored to consume it (behaviour unchanged), policy-question wording (`Stamdata/PolicyQuestions.cs`) extracted from `DiplomaRules`,
and `StamdataValidationTests` added. Policy-question text and document-category which now consumes both (behaviour unchanged), guarded by `StamdataValidationTests`.
definitions follow the same pattern as obvious next steps; not moved yet. Document-category definitions follow the same pattern as the obvious next step; not moved
yet.