diff --git a/Directory.Packages.props b/Directory.Packages.props
index 0bac94a..bd1bc40 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -9,6 +9,7 @@
+
@@ -51,4 +52,4 @@
-
\ No newline at end of file
+
diff --git a/EstateReportingAPI.BusinessLogic.UnitTests/EstateReportingAPI.BusinessLogic.UnitTests.csproj b/EstateReportingAPI.BusinessLogic.UnitTests/EstateReportingAPI.BusinessLogic.UnitTests.csproj
index 99f848e..8233714 100644
--- a/EstateReportingAPI.BusinessLogic.UnitTests/EstateReportingAPI.BusinessLogic.UnitTests.csproj
+++ b/EstateReportingAPI.BusinessLogic.UnitTests/EstateReportingAPI.BusinessLogic.UnitTests.csproj
@@ -17,6 +17,7 @@
runtime; build; native; contentfiles; analyzers; buildtransitive
+
diff --git a/EstateReportingAPI.BusinessLogic.UnitTests/ReportingManagerExtensionsTests.cs b/EstateReportingAPI.BusinessLogic.UnitTests/ReportingManagerExtensionsTests.cs
index e492ff5..27c7716 100644
--- a/EstateReportingAPI.BusinessLogic.UnitTests/ReportingManagerExtensionsTests.cs
+++ b/EstateReportingAPI.BusinessLogic.UnitTests/ReportingManagerExtensionsTests.cs
@@ -1,5 +1,6 @@
using EstateReportingAPI.BusinessLogic;
using EstateReportingAPI.Models;
+using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
using Shouldly;
using TransactionProcessor.Database.Contexts;
@@ -11,6 +12,42 @@ namespace EstateReportingAPI.BusinessLogic.UnitTests;
public sealed class ReportingQueryExtensionsTests
{
+ [Fact]
+ public void TodayTransactionGroupings_ExecuteAgainstRelationalKeylessProjection()
+ {
+ using var connection = new SqliteConnection("Data Source=:memory:");
+ connection.Open();
+ var options = new DbContextOptionsBuilder()
+ .UseSqlite(connection)
+ .Options;
+ using var context = new EstateManagementContext(options);
+ context.Database.ExecuteSqlRaw("CREATE TABLE TodayTransactions (MerchantReportingId INTEGER NOT NULL, OperatorReportingId INTEGER NOT NULL, ContractProductReportingId INTEGER NOT NULL, TransactionAmount TEXT NOT NULL)");
+ context.Database.ExecuteSqlRaw("CREATE TABLE Merchant (MerchantReportingId INTEGER NOT NULL, Name TEXT NOT NULL)");
+ context.Database.ExecuteSqlRaw("CREATE TABLE Operator (OperatorReportingId INTEGER NOT NULL, Name TEXT NOT NULL)");
+ context.Database.ExecuteSqlRaw("CREATE TABLE ContractProduct (ContractProductReportingId INTEGER NOT NULL, ProductName TEXT NOT NULL)");
+ context.Database.ExecuteSqlRaw("INSERT INTO Merchant VALUES (20, 'Merchant A'), (21, 'Merchant B')");
+ context.Database.ExecuteSqlRaw("INSERT INTO Operator VALUES (10, 'Operator A'), (11, 'Operator B')");
+ context.Database.ExecuteSqlRaw("INSERT INTO ContractProduct VALUES (40, 'Product A'), (41, 'Product B')");
+
+ context.Database.ExecuteSqlRaw("""
+ INSERT INTO TodayTransactions (MerchantReportingId, OperatorReportingId, ContractProductReportingId, TransactionAmount)
+ VALUES (20, 10, 40, 10.00), (20, 10, 40, 15.50), (21, 11, 41, 7.25)
+ """);
+
+ var transactions = context.TodayTransactions;
+ transactions.ApplyMerchantGrouping(context).ToList().OrderBy(x => x.DimensionName).Select(x => (x.DimensionName, x.SalesValue))
+ .ShouldBe(new[] { ("Merchant A", 25.5m), ("Merchant B", 7.25m) });
+ transactions.ApplyOperatorGrouping(context).ToList().OrderBy(x => x.DimensionName).Select(x => (x.DimensionName, x.SalesValue))
+ .ShouldBe(new[] { ("Operator A", 25.5m), ("Operator B", 7.25m) });
+ transactions.ApplyProductGrouping(context).ToList().OrderBy(x => x.DimensionName).Select(x => (x.DimensionName, x.SalesValue))
+ .ShouldBe(new[] { ("Product A", 25.5m), ("Product B", 7.25m) });
+
+ context.Database.ExecuteSqlRaw("DELETE FROM TodayTransactions");
+ transactions.ApplyMerchantGrouping(context).ShouldBeEmpty();
+ transactions.ApplyOperatorGrouping(context).ShouldBeEmpty();
+ transactions.ApplyProductGrouping(context).ShouldBeEmpty();
+ }
+
[Fact]
public void TodayTransactionFilters_HandleEmptyAndSelectedValues()
{