Added endpoints to update verspakketten and add beoordeling. Added some validations and other tweaks

This commit is contained in:
moarten
2026-04-18 20:07:24 +02:00
parent a10bcfa1d6
commit 11bff0de63
23 changed files with 628 additions and 8 deletions

View File

@@ -6,6 +6,8 @@ public interface ILutraDbContext
{
DbSet<Supermarkt> Supermarkten { get; }
DbSet<Beoordeling> Beoordelingen { get; }
DbSet<Verspakket> Verspaketten { get; }
Task<int> SaveChangesAsync(CancellationToken cancellationToken);

View File

@@ -10,6 +10,12 @@ namespace Lutra.Application.Models.Verspakketten
public int? PrijsInCenten { get; init; }
public int AantalPersonen { get; init; }
public double? AverageCijferSmaak { get; init; }
public double? AverageCijferBereiden { get; init; }
public Beoordeling[]? Beoordelingen { get; init; }
public Supermarkt? Supermarkt { get; init; }

View File

@@ -0,0 +1,13 @@
using Cortex.Mediator.Commands;
namespace Lutra.Application.Verspakketten;
public sealed partial class AddBeoordeling
{
public sealed record Command(
Guid VerspakketId,
int CijferSmaak,
int CijferBereiden,
bool Aanbevolen,
string? Tekst) : ICommand<Response>;
}

View File

@@ -0,0 +1,41 @@
using Cortex.Mediator.Commands;
using Lutra.Application.Interfaces;
using Microsoft.EntityFrameworkCore;
namespace Lutra.Application.Verspakketten;
public sealed partial class AddBeoordeling
{
public sealed class Handler(ILutraDbContext context) : ICommandHandler<Command, Response>
{
public async Task<Response> Handle(Command request, CancellationToken cancellationToken)
{
var verspakketExists = await context.Verspaketten
.AsNoTracking()
.AnyAsync(v => v.Id == request.VerspakketId && v.DeletedAt == null, cancellationToken);
if (!verspakketExists)
{
throw new InvalidOperationException($"Verspakket with id '{request.VerspakketId}' was not found.");
}
var now = DateTime.UtcNow;
var beoordeling = new Domain.Entities.Beoordeling
{
Id = Guid.NewGuid(),
CijferSmaak = request.CijferSmaak,
CijferBereiden = request.CijferBereiden,
Aanbevolen = request.Aanbevolen,
Tekst = request.Tekst,
VerspakketId = request.VerspakketId,
CreatedAt = now,
ModifiedAt = now
};
await context.Beoordelingen.AddAsync(beoordeling, cancellationToken);
await context.SaveChangesAsync(cancellationToken);
return new Response { Id = beoordeling.Id };
}
}
}

View File

@@ -0,0 +1,9 @@
namespace Lutra.Application.Verspakketten;
public sealed partial class AddBeoordeling
{
public sealed class Response
{
public required Guid Id { get; set; }
}
}

View File

@@ -0,0 +1,3 @@
namespace Lutra.Application.Verspakketten;
public sealed partial class AddBeoordeling { }

View File

@@ -1,4 +1,5 @@
using Cortex.Mediator.Commands;
using System.ComponentModel.DataAnnotations;
namespace Lutra.Application.Verspakketten;
@@ -7,6 +8,6 @@ public sealed partial class CreateVerspakket
public sealed record Command(
string Naam,
int? PrijsInCenten,
int AantalPersonen,
[Range(1, 10)] int AantalPersonen,
Guid SupermarktId) : ICommand<Response>;
}

View File

@@ -20,6 +20,9 @@ namespace Lutra.Application.Verspakketten
Id = v.Id,
Naam = v.Naam,
PrijsInCenten = v.PrijsInCenten,
AantalPersonen = v.AantalPersonen,
AverageCijferSmaak = v.Beoordelingen.Any() ? v.Beoordelingen.Average(b => (double)b.CijferSmaak) : null,
AverageCijferBereiden = v.Beoordelingen.Any() ? v.Beoordelingen.Average(b => (double)b.CijferBereiden) : null,
Beoordelingen = v.Beoordelingen
.Select(b => new Beoordeling
{

View File

@@ -45,6 +45,9 @@ namespace Lutra.Application.Verspakketten
Id = v.Id,
Naam = v.Naam,
PrijsInCenten = v.PrijsInCenten,
AantalPersonen = v.AantalPersonen,
AverageCijferSmaak = v.Beoordelingen.Any() ? v.Beoordelingen.Average(b => (double)b.CijferSmaak) : null,
AverageCijferBereiden = v.Beoordelingen.Any() ? v.Beoordelingen.Average(b => (double)b.CijferBereiden) : null,
Beoordelingen = v.Beoordelingen
.Select(b => new Beoordeling
{

View File

@@ -0,0 +1,11 @@
using Cortex.Mediator.Commands;
namespace Lutra.Application.Verspakketten;
public sealed partial class UpdateVerspakket
{
/// <summary>
/// Updates an existing verspakket.
/// </summary>
public sealed record Command(Guid Id, string Naam, int PrijsInCenten, int AantalPersonen, Guid SupermarktId) : ICommand<Response>;
}

View File

@@ -0,0 +1,62 @@
using Cortex.Mediator.Commands;
using Lutra.Application.Interfaces;
using Microsoft.EntityFrameworkCore;
namespace Lutra.Application.Verspakketten;
public sealed partial class UpdateVerspakket
{
/// <summary>
/// Handles update requests for verspakketten.
/// </summary>
public sealed class Handler(ILutraDbContext context) : ICommandHandler<Command, Response>
{
/// <summary>
/// Updates an existing verspakket.
/// </summary>
/// <param name="request">The update command.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>An empty response.</returns>
public async Task<Response> Handle(Command request, CancellationToken cancellationToken)
{
if (string.IsNullOrWhiteSpace(request.Naam))
throw new ArgumentException("Naam mag niet leeg zijn.", nameof(request.Naam));
if (request.Naam.Length > 50)
throw new ArgumentException("Naam mag maximaal 50 tekens bevatten.", nameof(request.Naam));
if (request.PrijsInCenten < 0)
throw new ArgumentException("PrijsInCenten mag niet negatief zijn.", nameof(request.PrijsInCenten));
if (request.AantalPersonen is < 1 or > 10)
throw new ArgumentException("AantalPersonen moet tussen 1 en 10 liggen.", nameof(request.AantalPersonen));
var verspakket = await context.Verspaketten
.FirstOrDefaultAsync(v => v.Id == request.Id && v.DeletedAt == null, cancellationToken);
if (verspakket is null)
{
throw new InvalidOperationException($"Verspakket with id '{request.Id}' was not found.");
}
var supermarktExists = await context.Supermarkten
.AsNoTracking()
.AnyAsync(s => s.Id == request.SupermarktId && s.DeletedAt == null, cancellationToken);
if (!supermarktExists)
{
throw new InvalidOperationException($"Supermarkt with id '{request.SupermarktId}' was not found.");
}
verspakket.Naam = request.Naam;
verspakket.PrijsInCenten = request.PrijsInCenten;
verspakket.AantalPersonen = request.AantalPersonen;
verspakket.SupermarktId = request.SupermarktId;
verspakket.ModifiedAt = DateTime.UtcNow;
await context.SaveChangesAsync(cancellationToken);
return new Response();
}
}
}

View File

@@ -0,0 +1,9 @@
namespace Lutra.Application.Verspakketten;
public sealed partial class UpdateVerspakket
{
/// <summary>
/// Represents the result of an update verspakket operation.
/// </summary>
public sealed record Response;
}

View File

@@ -0,0 +1,3 @@
namespace Lutra.Application.Verspakketten;
public sealed partial class UpdateVerspakket;