diff --git a/Lutra/Lutra.API/Controllers/VerspakkettenController.cs b/Lutra/Lutra.API/Controllers/VerspakkettenController.cs
index 37a2a52..7c5db45 100644
--- a/Lutra/Lutra.API/Controllers/VerspakkettenController.cs
+++ b/Lutra/Lutra.API/Controllers/VerspakkettenController.cs
@@ -1,4 +1,5 @@
using Cortex.Mediator;
+using Lutra.API.Requests;
using Lutra.Application.Verspakketten;
using Microsoft.AspNetCore.Mvc;
@@ -72,5 +73,48 @@ namespace Lutra.API.Controllers
return BadRequest(ex.Message);
}
}
+
+ ///
+ /// Updates an existing verspakket with the provided values.
+ ///
+ /// The verspakket identifier.
+ /// The updated verspakket values.
+ ///
+ /// Returns 204 No Content when the update succeeds.
+ /// Returns 404 Not Found when the specified verspakket does not exist.
+ ///
+ [HttpPut("{id:guid}")]
+ [ProducesResponseType(StatusCodes.Status204NoContent)]
+ [ProducesResponseType(StatusCodes.Status404NotFound)]
+ public async Task Update(Guid id, [FromBody] UpdateVerspakketRequest request)
+ {
+ var command = new UpdateVerspakket.Command(id, request.Naam, request.PrijsInCenten, request.AantalPersonen, request.SupermarktId);
+ await mediator.SendCommandAsync(command);
+ return NoContent();
+ }
+
+ ///
+ /// Adds a beoordeling to an existing verspakket.
+ ///
+ /// The verspakket ID.
+ /// The beoordeling values to add.
+ /// Returns 201 Created with the created beoordeling identifier.
+ [HttpPost("{id:guid}/beoordelingen")]
+ [ProducesResponseType(typeof(AddBeoordeling.Response), StatusCodes.Status201Created)]
+ [ProducesResponseType(StatusCodes.Status400BadRequest)]
+ public async Task> AddBeoordeling(Guid id, [FromBody] AddBeoordelingRequest request)
+ {
+ try
+ {
+ var command = new AddBeoordeling.Command(id, request.CijferSmaak, request.CijferBereiden, request.Aanbevolen, request.Tekst);
+ var result = await mediator.SendCommandAsync(command);
+
+ return CreatedAtAction(nameof(GetById), new { id }, result);
+ }
+ catch (InvalidOperationException ex)
+ {
+ return BadRequest(ex.Message);
+ }
+ }
}
}
\ No newline at end of file
diff --git a/Lutra/Lutra.API/Requests/AddBeoordelingRequest.cs b/Lutra/Lutra.API/Requests/AddBeoordelingRequest.cs
new file mode 100644
index 0000000..ecceb3a
--- /dev/null
+++ b/Lutra/Lutra.API/Requests/AddBeoordelingRequest.cs
@@ -0,0 +1,12 @@
+using System.ComponentModel.DataAnnotations;
+
+namespace Lutra.API.Requests;
+
+///
+/// Represents the data required to add a beoordeling to a verspakket.
+///
+public sealed record AddBeoordelingRequest(
+ [Range(1, 10)] int CijferSmaak,
+ [Range(1, 10)] int CijferBereiden,
+ bool Aanbevolen,
+ [MaxLength(1024)] string? Tekst);
\ No newline at end of file
diff --git a/Lutra/Lutra.API/Requests/UpdateVerspakketRequest.cs b/Lutra/Lutra.API/Requests/UpdateVerspakketRequest.cs
new file mode 100644
index 0000000..375ff52
--- /dev/null
+++ b/Lutra/Lutra.API/Requests/UpdateVerspakketRequest.cs
@@ -0,0 +1,12 @@
+using System.ComponentModel.DataAnnotations;
+
+namespace Lutra.API.Requests;
+
+///
+/// Represents the data required to update a verspakket.
+///
+public sealed record UpdateVerspakketRequest(
+ [Required, MaxLength(50)] string Naam,
+ [Range(0, int.MaxValue)] int PrijsInCenten,
+ [Range(1, 10)] int AantalPersonen,
+ [Required] Guid SupermarktId);
diff --git a/Lutra/Lutra.Application/Interfaces/ILutraDbContext.cs b/Lutra/Lutra.Application/Interfaces/ILutraDbContext.cs
index 1e5a811..e9807db 100644
--- a/Lutra/Lutra.Application/Interfaces/ILutraDbContext.cs
+++ b/Lutra/Lutra.Application/Interfaces/ILutraDbContext.cs
@@ -6,6 +6,8 @@ public interface ILutraDbContext
{
DbSet Supermarkten { get; }
+ DbSet Beoordelingen { get; }
+
DbSet Verspaketten { get; }
Task SaveChangesAsync(CancellationToken cancellationToken);
diff --git a/Lutra/Lutra.Application/Models/Verspakketten/Verspakket.cs b/Lutra/Lutra.Application/Models/Verspakketten/Verspakket.cs
index 46a7d78..e07938a 100644
--- a/Lutra/Lutra.Application/Models/Verspakketten/Verspakket.cs
+++ b/Lutra/Lutra.Application/Models/Verspakketten/Verspakket.cs
@@ -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; }
diff --git a/Lutra/Lutra.Application/Verspakketten/AddBeoordeling.Command.cs b/Lutra/Lutra.Application/Verspakketten/AddBeoordeling.Command.cs
new file mode 100644
index 0000000..d9a235d
--- /dev/null
+++ b/Lutra/Lutra.Application/Verspakketten/AddBeoordeling.Command.cs
@@ -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;
+}
\ No newline at end of file
diff --git a/Lutra/Lutra.Application/Verspakketten/AddBeoordeling.Handler.cs b/Lutra/Lutra.Application/Verspakketten/AddBeoordeling.Handler.cs
new file mode 100644
index 0000000..c733366
--- /dev/null
+++ b/Lutra/Lutra.Application/Verspakketten/AddBeoordeling.Handler.cs
@@ -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
+ {
+ public async Task 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 };
+ }
+ }
+}
\ No newline at end of file
diff --git a/Lutra/Lutra.Application/Verspakketten/AddBeoordeling.Response.cs b/Lutra/Lutra.Application/Verspakketten/AddBeoordeling.Response.cs
new file mode 100644
index 0000000..c039e06
--- /dev/null
+++ b/Lutra/Lutra.Application/Verspakketten/AddBeoordeling.Response.cs
@@ -0,0 +1,9 @@
+namespace Lutra.Application.Verspakketten;
+
+public sealed partial class AddBeoordeling
+{
+ public sealed class Response
+ {
+ public required Guid Id { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/Lutra/Lutra.Application/Verspakketten/AddBeoordeling.cs b/Lutra/Lutra.Application/Verspakketten/AddBeoordeling.cs
new file mode 100644
index 0000000..20e491d
--- /dev/null
+++ b/Lutra/Lutra.Application/Verspakketten/AddBeoordeling.cs
@@ -0,0 +1,3 @@
+namespace Lutra.Application.Verspakketten;
+
+public sealed partial class AddBeoordeling { }
\ No newline at end of file
diff --git a/Lutra/Lutra.Application/Verspakketten/CreateVerspakket.Command.cs b/Lutra/Lutra.Application/Verspakketten/CreateVerspakket.Command.cs
index 9a35b7c..3c81545 100644
--- a/Lutra/Lutra.Application/Verspakketten/CreateVerspakket.Command.cs
+++ b/Lutra/Lutra.Application/Verspakketten/CreateVerspakket.Command.cs
@@ -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;
}
diff --git a/Lutra/Lutra.Application/Verspakketten/GetVerspakket.Handler.cs b/Lutra/Lutra.Application/Verspakketten/GetVerspakket.Handler.cs
index 2e0b010..8beaa20 100644
--- a/Lutra/Lutra.Application/Verspakketten/GetVerspakket.Handler.cs
+++ b/Lutra/Lutra.Application/Verspakketten/GetVerspakket.Handler.cs
@@ -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
{
diff --git a/Lutra/Lutra.Application/Verspakketten/GetVerspakketten.Handler.cs b/Lutra/Lutra.Application/Verspakketten/GetVerspakketten.Handler.cs
index 1f073f9..fae33d9 100644
--- a/Lutra/Lutra.Application/Verspakketten/GetVerspakketten.Handler.cs
+++ b/Lutra/Lutra.Application/Verspakketten/GetVerspakketten.Handler.cs
@@ -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
{
diff --git a/Lutra/Lutra.Application/Verspakketten/UpdateVerspakket.Command.cs b/Lutra/Lutra.Application/Verspakketten/UpdateVerspakket.Command.cs
new file mode 100644
index 0000000..08aa023
--- /dev/null
+++ b/Lutra/Lutra.Application/Verspakketten/UpdateVerspakket.Command.cs
@@ -0,0 +1,11 @@
+using Cortex.Mediator.Commands;
+
+namespace Lutra.Application.Verspakketten;
+
+public sealed partial class UpdateVerspakket
+{
+ ///
+ /// Updates an existing verspakket.
+ ///
+ public sealed record Command(Guid Id, string Naam, int PrijsInCenten, int AantalPersonen, Guid SupermarktId) : ICommand;
+}
diff --git a/Lutra/Lutra.Application/Verspakketten/UpdateVerspakket.Handler.cs b/Lutra/Lutra.Application/Verspakketten/UpdateVerspakket.Handler.cs
new file mode 100644
index 0000000..a6d997d
--- /dev/null
+++ b/Lutra/Lutra.Application/Verspakketten/UpdateVerspakket.Handler.cs
@@ -0,0 +1,62 @@
+using Cortex.Mediator.Commands;
+using Lutra.Application.Interfaces;
+using Microsoft.EntityFrameworkCore;
+
+namespace Lutra.Application.Verspakketten;
+
+public sealed partial class UpdateVerspakket
+{
+ ///
+ /// Handles update requests for verspakketten.
+ ///
+ public sealed class Handler(ILutraDbContext context) : ICommandHandler
+ {
+ ///
+ /// Updates an existing verspakket.
+ ///
+ /// The update command.
+ /// The cancellation token.
+ /// An empty response.
+ public async Task 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();
+ }
+ }
+}
diff --git a/Lutra/Lutra.Application/Verspakketten/UpdateVerspakket.Response.cs b/Lutra/Lutra.Application/Verspakketten/UpdateVerspakket.Response.cs
new file mode 100644
index 0000000..0516519
--- /dev/null
+++ b/Lutra/Lutra.Application/Verspakketten/UpdateVerspakket.Response.cs
@@ -0,0 +1,9 @@
+namespace Lutra.Application.Verspakketten;
+
+public sealed partial class UpdateVerspakket
+{
+ ///
+ /// Represents the result of an update verspakket operation.
+ ///
+ public sealed record Response;
+}
diff --git a/Lutra/Lutra.Application/Verspakketten/UpdateVerspakket.cs b/Lutra/Lutra.Application/Verspakketten/UpdateVerspakket.cs
new file mode 100644
index 0000000..724591a
--- /dev/null
+++ b/Lutra/Lutra.Application/Verspakketten/UpdateVerspakket.cs
@@ -0,0 +1,3 @@
+namespace Lutra.Application.Verspakketten;
+
+public sealed partial class UpdateVerspakket;
diff --git a/Lutra/Lutra.Domain/Entities/Verspakket.cs b/Lutra/Lutra.Domain/Entities/Verspakket.cs
index d31a98e..66f018f 100644
--- a/Lutra/Lutra.Domain/Entities/Verspakket.cs
+++ b/Lutra/Lutra.Domain/Entities/Verspakket.cs
@@ -9,6 +9,7 @@ public class Verspakket : BaseEntity
[MaxLength(50)]
public required string Naam { get; set; }
+ [Range(0, int.MaxValue)]
public int? PrijsInCenten { get; set; }
[Range(1, 10)]
diff --git a/Lutra/Lutra.Infrastructure/LutraDbContext.cs b/Lutra/Lutra.Infrastructure/LutraDbContext.cs
index 5dba703..403df50 100644
--- a/Lutra/Lutra.Infrastructure/LutraDbContext.cs
+++ b/Lutra/Lutra.Infrastructure/LutraDbContext.cs
@@ -13,6 +13,8 @@ public class LutraDbContext : DbContext, ILutraDbContext
public DbSet Supermarkten => Set();
+ public DbSet Beoordelingen => Set();
+
public DbSet Verspaketten => Set();
protected override void OnModelCreating(ModelBuilder modelBuilder)
@@ -22,11 +24,19 @@ public class LutraDbContext : DbContext, ILutraDbContext
modelBuilder.Entity()
.ToTable("Beoordelingen");
- modelBuilder.Entity()
- .HasMany(v => v.Beoordelingen)
- .WithOne()
- .HasForeignKey(b => b.VerspakketId)
- .IsRequired();
+ modelBuilder.Entity(b =>
+ {
+ b.HasMany(v => v.Beoordelingen)
+ .WithOne()
+ .HasForeignKey(beo => beo.VerspakketId)
+ .IsRequired();
+
+ b.ToTable(t =>
+ {
+ t.HasCheckConstraint("CK_Verspaketten_AantalPersonen", "\"AantalPersonen\" BETWEEN 1 AND 10");
+ t.HasCheckConstraint("CK_Verspaketten_PrijsInCenten", "\"PrijsInCenten\" IS NULL OR \"PrijsInCenten\" >= 0");
+ });
+ });
}
public override Task SaveChangesAsync(CancellationToken cancellationToken = default)
diff --git a/Lutra/Lutra.Infrastructure/Migrations/20260418173428_AddAantalPersonenCheckConstraint.Designer.cs b/Lutra/Lutra.Infrastructure/Migrations/20260418173428_AddAantalPersonenCheckConstraint.Designer.cs
new file mode 100644
index 0000000..d0b44fe
--- /dev/null
+++ b/Lutra/Lutra.Infrastructure/Migrations/20260418173428_AddAantalPersonenCheckConstraint.Designer.cs
@@ -0,0 +1,157 @@
+//
+using System;
+using Lutra.Infrastructure.Sql;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
+
+#nullable disable
+
+namespace Lutra.Infrastructure.Sql.Migrations
+{
+ [DbContext(typeof(LutraDbContext))]
+ [Migration("20260418173428_AddAantalPersonenCheckConstraint")]
+ partial class AddAantalPersonenCheckConstraint
+ {
+ ///
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "10.0.6")
+ .HasAnnotation("Relational:MaxIdentifierLength", 63);
+
+ NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
+
+ modelBuilder.Entity("Lutra.Domain.Entities.Beoordeling", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("Aanbevolen")
+ .HasColumnType("boolean");
+
+ b.Property("CijferBereiden")
+ .HasColumnType("integer");
+
+ b.Property("CijferSmaak")
+ .HasColumnType("integer");
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DeletedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("ModifiedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Tekst")
+ .HasMaxLength(1024)
+ .HasColumnType("character varying(1024)");
+
+ b.Property("VerspakketId")
+ .HasColumnType("uuid");
+
+ b.HasKey("Id");
+
+ b.HasIndex("VerspakketId");
+
+ b.ToTable("Beoordelingen", (string)null);
+ });
+
+ modelBuilder.Entity("Lutra.Domain.Entities.Supermarkt", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DeletedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("ModifiedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Naam")
+ .IsRequired()
+ .HasMaxLength(50)
+ .HasColumnType("character varying(50)");
+
+ b.HasKey("Id");
+
+ b.ToTable("Supermarkten");
+ });
+
+ modelBuilder.Entity("Lutra.Domain.Entities.Verspakket", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("AantalPersonen")
+ .HasColumnType("integer");
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DeletedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("ModifiedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Naam")
+ .IsRequired()
+ .HasMaxLength(50)
+ .HasColumnType("character varying(50)");
+
+ b.Property("PrijsInCenten")
+ .HasColumnType("integer");
+
+ b.Property("SupermarktId")
+ .HasColumnType("uuid");
+
+ b.HasKey("Id");
+
+ b.HasIndex("SupermarktId");
+
+ b.ToTable("Verspaketten", t =>
+ {
+ t.HasCheckConstraint("CK_Verspaketten_AantalPersonen", "\"AantalPersonen\" BETWEEN 1 AND 10");
+ });
+ });
+
+ modelBuilder.Entity("Lutra.Domain.Entities.Beoordeling", b =>
+ {
+ b.HasOne("Lutra.Domain.Entities.Verspakket", null)
+ .WithMany("Beoordelingen")
+ .HasForeignKey("VerspakketId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Lutra.Domain.Entities.Verspakket", b =>
+ {
+ b.HasOne("Lutra.Domain.Entities.Supermarkt", "Supermarkt")
+ .WithMany()
+ .HasForeignKey("SupermarktId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Supermarkt");
+ });
+
+ modelBuilder.Entity("Lutra.Domain.Entities.Verspakket", b =>
+ {
+ b.Navigation("Beoordelingen");
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/Lutra/Lutra.Infrastructure/Migrations/20260418173428_AddAantalPersonenCheckConstraint.cs b/Lutra/Lutra.Infrastructure/Migrations/20260418173428_AddAantalPersonenCheckConstraint.cs
new file mode 100644
index 0000000..5dc2237
--- /dev/null
+++ b/Lutra/Lutra.Infrastructure/Migrations/20260418173428_AddAantalPersonenCheckConstraint.cs
@@ -0,0 +1,27 @@
+using Microsoft.EntityFrameworkCore.Migrations;
+
+#nullable disable
+
+namespace Lutra.Infrastructure.Sql.Migrations
+{
+ ///
+ public partial class AddAantalPersonenCheckConstraint : Migration
+ {
+ ///
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.AddCheckConstraint(
+ name: "CK_Verspaketten_AantalPersonen",
+ table: "Verspaketten",
+ sql: "\"AantalPersonen\" BETWEEN 1 AND 10");
+ }
+
+ ///
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropCheckConstraint(
+ name: "CK_Verspaketten_AantalPersonen",
+ table: "Verspaketten");
+ }
+ }
+}
diff --git a/Lutra/Lutra.Infrastructure/Migrations/20260418174149_AddPrijsInCentenCheckConstraint.Designer.cs b/Lutra/Lutra.Infrastructure/Migrations/20260418174149_AddPrijsInCentenCheckConstraint.Designer.cs
new file mode 100644
index 0000000..8705816
--- /dev/null
+++ b/Lutra/Lutra.Infrastructure/Migrations/20260418174149_AddPrijsInCentenCheckConstraint.Designer.cs
@@ -0,0 +1,159 @@
+//
+using System;
+using Lutra.Infrastructure.Sql;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
+
+#nullable disable
+
+namespace Lutra.Infrastructure.Sql.Migrations
+{
+ [DbContext(typeof(LutraDbContext))]
+ [Migration("20260418174149_AddPrijsInCentenCheckConstraint")]
+ partial class AddPrijsInCentenCheckConstraint
+ {
+ ///
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "10.0.6")
+ .HasAnnotation("Relational:MaxIdentifierLength", 63);
+
+ NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
+
+ modelBuilder.Entity("Lutra.Domain.Entities.Beoordeling", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("Aanbevolen")
+ .HasColumnType("boolean");
+
+ b.Property("CijferBereiden")
+ .HasColumnType("integer");
+
+ b.Property("CijferSmaak")
+ .HasColumnType("integer");
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DeletedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("ModifiedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Tekst")
+ .HasMaxLength(1024)
+ .HasColumnType("character varying(1024)");
+
+ b.Property("VerspakketId")
+ .HasColumnType("uuid");
+
+ b.HasKey("Id");
+
+ b.HasIndex("VerspakketId");
+
+ b.ToTable("Beoordelingen", (string)null);
+ });
+
+ modelBuilder.Entity("Lutra.Domain.Entities.Supermarkt", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DeletedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("ModifiedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Naam")
+ .IsRequired()
+ .HasMaxLength(50)
+ .HasColumnType("character varying(50)");
+
+ b.HasKey("Id");
+
+ b.ToTable("Supermarkten");
+ });
+
+ modelBuilder.Entity("Lutra.Domain.Entities.Verspakket", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("AantalPersonen")
+ .HasColumnType("integer");
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DeletedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("ModifiedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Naam")
+ .IsRequired()
+ .HasMaxLength(50)
+ .HasColumnType("character varying(50)");
+
+ b.Property("PrijsInCenten")
+ .HasColumnType("integer");
+
+ b.Property("SupermarktId")
+ .HasColumnType("uuid");
+
+ b.HasKey("Id");
+
+ b.HasIndex("SupermarktId");
+
+ b.ToTable("Verspaketten", t =>
+ {
+ t.HasCheckConstraint("CK_Verspaketten_AantalPersonen", "\"AantalPersonen\" BETWEEN 1 AND 10");
+
+ t.HasCheckConstraint("CK_Verspaketten_PrijsInCenten", "\"PrijsInCenten\" IS NULL OR \"PrijsInCenten\" >= 0");
+ });
+ });
+
+ modelBuilder.Entity("Lutra.Domain.Entities.Beoordeling", b =>
+ {
+ b.HasOne("Lutra.Domain.Entities.Verspakket", null)
+ .WithMany("Beoordelingen")
+ .HasForeignKey("VerspakketId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Lutra.Domain.Entities.Verspakket", b =>
+ {
+ b.HasOne("Lutra.Domain.Entities.Supermarkt", "Supermarkt")
+ .WithMany()
+ .HasForeignKey("SupermarktId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Supermarkt");
+ });
+
+ modelBuilder.Entity("Lutra.Domain.Entities.Verspakket", b =>
+ {
+ b.Navigation("Beoordelingen");
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/Lutra/Lutra.Infrastructure/Migrations/20260418174149_AddPrijsInCentenCheckConstraint.cs b/Lutra/Lutra.Infrastructure/Migrations/20260418174149_AddPrijsInCentenCheckConstraint.cs
new file mode 100644
index 0000000..b0bf25a
--- /dev/null
+++ b/Lutra/Lutra.Infrastructure/Migrations/20260418174149_AddPrijsInCentenCheckConstraint.cs
@@ -0,0 +1,27 @@
+using Microsoft.EntityFrameworkCore.Migrations;
+
+#nullable disable
+
+namespace Lutra.Infrastructure.Sql.Migrations
+{
+ ///
+ public partial class AddPrijsInCentenCheckConstraint : Migration
+ {
+ ///
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.AddCheckConstraint(
+ name: "CK_Verspaketten_PrijsInCenten",
+ table: "Verspaketten",
+ sql: "\"PrijsInCenten\" IS NULL OR \"PrijsInCenten\" >= 0");
+ }
+
+ ///
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropCheckConstraint(
+ name: "CK_Verspaketten_PrijsInCenten",
+ table: "Verspaketten");
+ }
+ }
+}
diff --git a/Lutra/Lutra.Infrastructure/Migrations/LutraDbContextModelSnapshot.cs b/Lutra/Lutra.Infrastructure/Migrations/LutraDbContextModelSnapshot.cs
index 8dd149f..1498258 100644
--- a/Lutra/Lutra.Infrastructure/Migrations/LutraDbContextModelSnapshot.cs
+++ b/Lutra/Lutra.Infrastructure/Migrations/LutraDbContextModelSnapshot.cs
@@ -17,7 +17,7 @@ namespace Lutra.Infrastructure.Sql.Migrations
{
#pragma warning disable 612, 618
modelBuilder
- .HasAnnotation("ProductVersion", "10.0.5")
+ .HasAnnotation("ProductVersion", "10.0.6")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
@@ -118,7 +118,12 @@ namespace Lutra.Infrastructure.Sql.Migrations
b.HasIndex("SupermarktId");
- b.ToTable("Verspaketten");
+ b.ToTable("Verspaketten", t =>
+ {
+ t.HasCheckConstraint("CK_Verspaketten_AantalPersonen", "\"AantalPersonen\" BETWEEN 1 AND 10");
+
+ t.HasCheckConstraint("CK_Verspaketten_PrijsInCenten", "\"PrijsInCenten\" IS NULL OR \"PrijsInCenten\" >= 0");
+ });
});
modelBuilder.Entity("Lutra.Domain.Entities.Beoordeling", b =>