STATUS-MODEL: Active→Breeding + Pet enum + derived status logic (P0)

GerbilStatus: Active→Breeding, add Pet; Deceased/GivenAway are now DERIVED.
GerbilStatusService.Derive(): central precedence: DateOfDeath→Deceased (1),
ReceiverContactId→GivenAway (2), age>7y→Deceased presumed (3), user choice (4).
All write paths (gerbil CRUD, contracts, importers) call GerbilStatusService.Apply().
Startup sweep flips >7y gerbils to Deceased at next app restart.
Migration StatusModel: Active→Breeding rename + backfill derived statuses.
10 new tests; 200/200 green; has-pending=No.

Enum string values: Breeding (was Active), Pet (new), Deceased, GivenAway, ForSale.
FE contract: status field values updated (see Done-Report).
This commit is contained in:
2026-06-07 03:27:11 +02:00
parent c1b215d06f
commit 1a5f3218fb
16 changed files with 1698 additions and 19 deletions

View File

@@ -1,5 +1,7 @@
using System.Text.Json.Serialization;
using GerbilManagerWebAPI.Endpoints;
using GerbilManagerWebAPI.Models;
using GerbilManagerWebAPI.Services;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.EntityFrameworkCore;
using Scalar.AspNetCore;
@@ -84,7 +86,23 @@ app.MapScalarApiReference();
if (!app.Environment.IsEnvironment("Testing"))
{
using var scope = app.Services.CreateScope();
scope.ServiceProvider.GetRequiredService<ApplicationContext>().Database.Migrate();
var db = scope.ServiceProvider.GetRequiredService<ApplicationContext>();
db.Database.Migrate();
// Startup sweep: derive status for animals that silently crossed the 7-year threshold
// since the last write. No-op if all statuses are already current.
var today = DateOnly.FromDateTime(DateTime.UtcNow);
var candidates = await db.Gerbils
.Where(g => g.Status != GerbilStatus.Deceased && g.Status != GerbilStatus.GivenAway
&& g.DateOfDeath == null && g.ReceiverContactId == null
&& g.DateOfBirth != null
&& g.DateOfBirth < today.AddYears(-GerbilStatusService.MaxAgeYears))
.ToListAsync();
if (candidates.Count > 0)
{
foreach (var g in candidates) g.Status = GerbilStatus.Deceased;
await db.SaveChangesAsync();
}
}
app.UseCors(LanCorsPolicy);