Flexibele kluissoorten: soorten in de database, beheer op de website

- Tabellen vault_categories en vault_category_fields (db/21), startrijen handmatig (db/handmatig/21b)
- VaultCategoryService met regels voor zichtbaarheid, gebruik en eigen velden
- Beheer > Kluissoorten (/beheer/soorten) met gebruikstellers; soort in gebruik niet te verwijderen
- Kluis en Nalatenschap (web en app) tonen soorten en eigen velden uit de database
- Socketprotocol: vault.categories; GRANTs voor mijnbesluit_app in db/02
- db/20 herhaalbaar gemaakt
- App versie 1.1 (versiecode 3)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014MGLqt1uKfNARQHut9ifcx
This commit is contained in:
jf
2026-10-20 08:15:40 +00:00
co-authored by Claude Sonnet 5.5
parent a3a0bcfc05
commit af13d0e533
36 changed files with 2540 additions and 94 deletions
+65
View File
@@ -25,6 +25,8 @@ public class Store
public List<Adverteerder> Adverteerders = new();
public List<Campagne> Campagnes = new();
public List<CampagneUur> CampagneUren = new();
public List<VaultCategoryDefinition> Soorten = VaultCategorySaatgoed.Maak();
public List<VaultCategoryField> SoortVelden = new();
private long _seq;
public long NextId() => ++_seq;
@@ -528,3 +530,66 @@ public class TestPush : IPushSender
return Task.FromResult(true);
}
}
/// <summary>De startrijen uit db/21_kluissoorten.sql, zodat de tests met dezelfde soorten beginnen als de echte database.</summary>
public static class VaultCategorySaatgoed
{
public static List<VaultCategoryDefinition> Maak()
{
VaultCategoryDefinition S(int id, string naam, int volgorde, bool bespreken = false) => new()
{
Id = id, Naam = naam, Actief = true, Volgorde = volgorde, BesprekenMet = bespreken, Ingebouwd = true
};
return new List<VaultCategoryDefinition>
{
S(3, "Wachtwoord", 10), S(8, "Sociale media", 20), S(4, "Apparaat", 30), S(5, "Pincode", 40),
S(2, "Polis", 50), S(1, "Uitvaartwens", 60, true), S(9, "Donorcodicil", 70, true),
S(10, "Rijbewijs", 80), S(11, "Paspoort", 90), S(6, "Document", 100), S(7, "Notitie", 110),
S(99, "Overig", 120)
};
}
}
public class VaultCategoryRepo : IVaultCategoryRepository
{
private readonly Store _s; public VaultCategoryRepo(Store s) => _s = s;
private VaultCategoryDefinition Met(VaultCategoryDefinition d)
{
d.Velden = _s.SoortVelden.Where(v => v.CategoryId == d.Id).ToList();
return d;
}
public Task<IReadOnlyList<VaultCategoryDefinition>> ListAsync(CancellationToken ct = default) =>
Task.FromResult<IReadOnlyList<VaultCategoryDefinition>>(
_s.Soorten.OrderBy(c => c.Volgorde).ThenBy(c => c.Naam).Select(Met).ToList());
public Task<VaultCategoryDefinition?> FindAsync(int id, CancellationToken ct = default) =>
Task.FromResult(_s.Soorten.Where(c => c.Id == id).Select(Met).FirstOrDefault());
public Task<VaultCategoryField?> FindFieldAsync(long fieldId, CancellationToken ct = default) =>
Task.FromResult(_s.SoortVelden.FirstOrDefault(f => f.Id == fieldId));
public Task<IReadOnlyList<MijnBesluit.Application.Models.SoortGebruik>> GebruikAsync(CancellationToken ct = default) =>
Task.FromResult<IReadOnlyList<MijnBesluit.Application.Models.SoortGebruik>>(
_s.VaultItems.Where(v => v.DeletedAt == null).GroupBy(v => v.Category)
.Select(g => new MijnBesluit.Application.Models.SoortGebruik(
(int)g.Key, g.Count(), g.Select(v => v.OwnerPersonId).Distinct().Count())).ToList());
public Task<IReadOnlySet<int>> InGebruikDoorAsync(long personId, CancellationToken ct = default) =>
Task.FromResult<IReadOnlySet<int>>(
_s.VaultItems.Where(v => v.OwnerPersonId == personId && v.DeletedAt == null)
.Select(v => (int)v.Category).ToHashSet());
public Task<int> AantalItemsAsync(int categoryId, CancellationToken ct = default) =>
Task.FromResult(_s.VaultItems.Count(v => (int)v.Category == categoryId && v.DeletedAt == null));
private int _volgende = VaultCategoryDefinition.EersteEigenNummer;
public void Add(VaultCategoryDefinition soort) { if (soort.Id == 0) soort.Id = _volgende++; _s.Soorten.Add(soort); }
public void Remove(VaultCategoryDefinition soort) { _s.Soorten.Remove(soort); _s.SoortVelden.RemoveAll(v => v.CategoryId == soort.Id); }
public void Add(VaultCategoryField veld) { if (veld.Id == 0) veld.Id = _s.NextId(); _s.SoortVelden.Add(veld); }
public void Remove(VaultCategoryField veld) => _s.SoortVelden.Remove(veld);
}