feat(ruecknahmen): zurückgenommene Tiere erfassen
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
104
GerbilManagerWebAPI/Endpoints/ReturnRecordEndpoints.cs
Normal file
104
GerbilManagerWebAPI/Endpoints/ReturnRecordEndpoints.cs
Normal file
@@ -0,0 +1,104 @@
|
||||
using GerbilManagerWebAPI.Dtos;
|
||||
using GerbilManagerWebAPI.Models;
|
||||
using Microsoft.AspNetCore.Http.HttpResults;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace GerbilManagerWebAPI.Endpoints
|
||||
{
|
||||
/// <summary>
|
||||
/// RÜCKNAHMEN (RPRO3 getback_tb): zurückgekommene/zurückgenommene Tiere.
|
||||
/// GET /returns[?gerbilId=] -> Liste, neueste zuerst (optional nach Tier gefiltert).
|
||||
/// POST /returns -> Rücknahme erfassen, 201.
|
||||
/// PUT /returns/{id} -> Rücknahme aktualisieren, 200.
|
||||
/// DELETE /returns/{id} -> Rücknahme löschen, 204.
|
||||
/// Entkoppelt von Gerbils/Contacts (lose nullable Guid-Spalten, kein FK), daher
|
||||
/// überleben Zeilen den Import-Re-Ingest-Wipe — wie Feedback.
|
||||
/// </summary>
|
||||
public static class ReturnRecordEndpoints
|
||||
{
|
||||
public static IEndpointRouteBuilder MapReturnRecordEndpoints(this IEndpointRouteBuilder app)
|
||||
{
|
||||
var group = app.MapGroup("/returns").WithTags("Returns");
|
||||
|
||||
group.MapGet("/", async (ApplicationContext db, Guid? gerbilId) =>
|
||||
{
|
||||
var query = db.ReturnRecords.AsNoTracking();
|
||||
if (gerbilId is { } gid)
|
||||
query = query.Where(r => r.GerbilId == gid);
|
||||
// In-Memory sortieren: SQLite (Test-Host) kann nicht nach DateTimeOffset ORDER BY-en.
|
||||
var rows = await query.ToListAsync();
|
||||
return TypedResults.Ok(rows
|
||||
.OrderByDescending(r => r.ReturnDate ?? DateTime.MinValue)
|
||||
.ThenByDescending(r => r.CreatedAt)
|
||||
.Select(ToDto)
|
||||
.ToList());
|
||||
});
|
||||
|
||||
group.MapPost("/", async Task<Results<Created<ReturnRecordDto>, BadRequest<string>>> (
|
||||
ReturnRecordInput input, ApplicationContext db) =>
|
||||
{
|
||||
if (input.GerbilId is null && string.IsNullOrWhiteSpace(input.GerbilName))
|
||||
return TypedResults.BadRequest("Es muss ein Tier (GerbilId oder GerbilName) angegeben werden.");
|
||||
|
||||
var entity = new ReturnRecord
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
GerbilId = input.GerbilId,
|
||||
GerbilName = Trimmed(input.GerbilName),
|
||||
ReturnDate = input.ReturnDate,
|
||||
ReturnPrice = input.ReturnPrice,
|
||||
OriginalPrice = input.OriginalPrice,
|
||||
OriginalSaleDate = input.OriginalSaleDate,
|
||||
FromContactId = input.FromContactId,
|
||||
FromContactName = Trimmed(input.FromContactName),
|
||||
Note = Trimmed(input.Note),
|
||||
CreatedAt = DateTimeOffset.UtcNow,
|
||||
};
|
||||
db.ReturnRecords.Add(entity);
|
||||
await db.SaveChangesAsync();
|
||||
return TypedResults.Created($"/returns/{entity.Id}", ToDto(entity));
|
||||
});
|
||||
|
||||
group.MapPut("/{id:guid}", async Task<Results<Ok<ReturnRecordDto>, NotFound, BadRequest<string>>> (
|
||||
Guid id, ReturnRecordInput input, ApplicationContext db) =>
|
||||
{
|
||||
var entity = await db.ReturnRecords.FirstOrDefaultAsync(r => r.Id == id);
|
||||
if (entity is null) return TypedResults.NotFound();
|
||||
|
||||
if (input.GerbilId is null && string.IsNullOrWhiteSpace(input.GerbilName))
|
||||
return TypedResults.BadRequest("Es muss ein Tier (GerbilId oder GerbilName) angegeben werden.");
|
||||
|
||||
entity.GerbilId = input.GerbilId;
|
||||
entity.GerbilName = Trimmed(input.GerbilName);
|
||||
entity.ReturnDate = input.ReturnDate;
|
||||
entity.ReturnPrice = input.ReturnPrice;
|
||||
entity.OriginalPrice = input.OriginalPrice;
|
||||
entity.OriginalSaleDate = input.OriginalSaleDate;
|
||||
entity.FromContactId = input.FromContactId;
|
||||
entity.FromContactName = Trimmed(input.FromContactName);
|
||||
entity.Note = Trimmed(input.Note);
|
||||
await db.SaveChangesAsync();
|
||||
return TypedResults.Ok(ToDto(entity));
|
||||
});
|
||||
|
||||
group.MapDelete("/{id:guid}", async Task<Results<NoContent, NotFound>> (
|
||||
Guid id, ApplicationContext db) =>
|
||||
{
|
||||
var entity = await db.ReturnRecords.FirstOrDefaultAsync(r => r.Id == id);
|
||||
if (entity is null) return TypedResults.NotFound();
|
||||
db.ReturnRecords.Remove(entity);
|
||||
await db.SaveChangesAsync();
|
||||
return TypedResults.NoContent();
|
||||
});
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
private static string? Trimmed(string? s) =>
|
||||
string.IsNullOrWhiteSpace(s) ? null : s.Trim();
|
||||
|
||||
private static ReturnRecordDto ToDto(ReturnRecord r) =>
|
||||
new(r.Id, r.GerbilId, r.GerbilName, r.ReturnDate, r.ReturnPrice, r.OriginalPrice,
|
||||
r.OriginalSaleDate, r.FromContactId, r.FromContactName, r.Note, r.CreatedAt);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user