feat: Fehler-melden + Datenherkunft auf Kontakte & Würfe erweitern
- Kontakt-Detailseite: „Fehler melden"- und „Datenherkunft"-Button. - Wurf-Ansicht: „Datenherkunft"-Button (Feedback war bereits vorhanden). - Feedback-Entity um loses, nullable ContactId erweitert (kein FK → übersteht Ingest-Wipe); Migration AddFeedbackContactId. - Contact.Provenance + Litter.Provenance (nullable text); Migration AddContactLitterProvenance; im Ingest gemappt und in den DTOs zurückgegeben. - Import: build_entity_provenance() generalisiert; Kontakte (sourceFiles, Züchter/Abnehmer-Hinweise) und Würfe (Wurfchronik vs. Diagramm-rekonstruiert, Geschwister-Merge) erhalten Herkunftsdaten in resolved_import.json. - Frontend: ProvenanceDialog generalisiert (EntityProvenance + entityLabel). Tests erweitert (Ingest-Round-trip Kontakt/Wurf, contact-scoped Feedback übersteht Wipe). dotnet(212)/vitest(129)/playwright(36)/tsc/eslint grün. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -75,7 +75,7 @@ public class FeedbackEndpointTests : IClassFixture<ApiFactory>
|
||||
{
|
||||
Contacts = new[]
|
||||
{
|
||||
new { Id = contactId, Name = "Test Breeder", Email = "t@e.de", Phone = "", Address = "", Notes = (string?)null, IsBreeder = true, IsReceiver = false }
|
||||
new { Id = contactId, Name = "Test Breeder", Email = "t@e.de", Phone = "", Address = "", Notes = (string?)null, IsBreeder = true, IsReceiver = false, NameSuffix = (string?)null, Provenance = (string?)null }
|
||||
},
|
||||
Litters = new[]
|
||||
{
|
||||
@@ -109,6 +109,19 @@ public class FeedbackEndpointTests : IClassFixture<ApiFactory>
|
||||
Url = "http://localhost/rennmaeuse/papa",
|
||||
CreatedAt = DateTimeOffset.UtcNow,
|
||||
});
|
||||
// A contact-scoped feedback report — the ContactId is a loose (FK-free) id,
|
||||
// so it must survive the contact-table wipe just like gerbil/litter ids.
|
||||
var contactFeedbackId = Guid.NewGuid();
|
||||
db.Feedback.Add(new Feedback
|
||||
{
|
||||
Id = contactFeedbackId,
|
||||
Message = "Adresse stimmt nicht.",
|
||||
Context = "contact-detail",
|
||||
ContactId = contactId,
|
||||
EntityName = "Test Breeder",
|
||||
Url = "http://localhost/kontakte/test-breeder",
|
||||
CreatedAt = DateTimeOffset.UtcNow,
|
||||
});
|
||||
await db.SaveChangesAsync();
|
||||
|
||||
var config = new ConfigurationBuilder()
|
||||
@@ -119,12 +132,18 @@ public class FeedbackEndpointTests : IClassFixture<ApiFactory>
|
||||
var result = await new IngestResolvedService(db, config, null!).RunAsync();
|
||||
Assert.Contains("Ingestion successful!", result);
|
||||
|
||||
// Gerbils/litters were wiped & re-created, but feedback is untouched.
|
||||
var survivor = await db.Feedback.SingleAsync();
|
||||
Assert.Equal(feedbackId, survivor.Id);
|
||||
// Gerbils/litters/contacts were wiped & re-created, but feedback is untouched.
|
||||
var survivor = await db.Feedback.SingleAsync(f => f.Id == feedbackId);
|
||||
Assert.Equal(fatherId, survivor.GerbilId); // loose id preserved even though the gerbil row was deleted/recreated
|
||||
Assert.Equal(litterId, survivor.LitterId);
|
||||
Assert.Equal("Papa", survivor.EntityName);
|
||||
|
||||
// The contact-scoped report also survives the contacts wipe (loose ContactId).
|
||||
var contactSurvivor = await db.Feedback.SingleAsync(f => f.Id == contactFeedbackId);
|
||||
Assert.Equal(contactId, contactSurvivor.ContactId);
|
||||
Assert.Equal("contact-detail", contactSurvivor.Context);
|
||||
Assert.Equal("Test Breeder", contactSurvivor.EntityName);
|
||||
Assert.Equal(2, await db.Feedback.CountAsync());
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user