From 8e765d3c43a593c8b760d9d103eb3739ee11c5c9 Mon Sep 17 00:00:00 2001 From: Stuart Ferguson Date: Wed, 11 Mar 2026 16:13:26 +0000 Subject: [PATCH] Return merchant balance in API responses Merchant endpoints now return the correct balance by querying MerchantBalanceProjectionState. Updated AddMerchant helper and all related tests to support and verify merchant balances. Added necessary usings for new logic. --- .../ReportingManager.cs | 21 ++++++++++--- .../DatabaseHelper.cs | 9 ++++++ .../MerchantEndpointTests.cs | 30 ++++++++++--------- .../TransactionsEndpointTests.cs | 16 +++++----- 4 files changed, 50 insertions(+), 26 deletions(-) diff --git a/EstateReportingAPI.BusinessLogic/ReportingManager.cs b/EstateReportingAPI.BusinessLogic/ReportingManager.cs index 0cfcca3..cbb39ae 100644 --- a/EstateReportingAPI.BusinessLogic/ReportingManager.cs +++ b/EstateReportingAPI.BusinessLogic/ReportingManager.cs @@ -960,12 +960,20 @@ public async Task>> GetMerchants(MerchantQueries.GetMercha return ResultHelpers.CreateFailure(queryResults); var merchants = queryResults.Data; + + var merchantBalancesQuery = context.MerchantBalanceProjectionState.Where(mb => merchants.Select(m => m.Merchant.MerchantId).Contains(mb.MerchantId)); + + var balanceQueryResults = await ExecuteQuerySafeToList(merchantBalancesQuery, cancellationToken, "Error retrieving merchant balances"); + + if (balanceQueryResults.IsFailed) + return ResultHelpers.CreateFailure(balanceQueryResults); // Ok now enumerate the results List response = new(); foreach (var queryResult in merchants) { - response.Add(new Merchant { - Balance = 0, + response.Add(new Merchant + { + Balance = balanceQueryResults.Data.Single(b => b.MerchantId == queryResult.Merchant.MerchantId).Balance, CreatedDateTime = queryResult.Merchant.CreatedDateTime, Name = queryResult.Merchant.Name, Region = queryResult.Address.Region, @@ -1017,9 +1025,14 @@ public async Task> GetMerchant(MerchantQueries.GetMerchantQuery var merchant = merchantQueryResult.Data; + // Get the merchant state to get the balance + var merchantStateQueryResult = await ExecuteQuerySafeSingleOrDefault(context.MerchantBalanceProjectionState.Where(ms => ms.MerchantId == request.MerchantId), cancellationToken, "Error getting merchant state"); + if (merchantStateQueryResult.IsFailed) return ResultHelpers.CreateFailure(merchantStateQueryResult); + var merchantState = merchantStateQueryResult.Data; + // Ok now enumerate the results - Merchant result = new Merchant { - Balance = 0, + Merchant result = new() { + Balance = merchantState.Balance, CreatedDateTime = merchant.CreatedDateTime, Name = merchant.Name, Reference = merchant.Reference, diff --git a/EstateReportingAPI.IntegrationTests/DatabaseHelper.cs b/EstateReportingAPI.IntegrationTests/DatabaseHelper.cs index 2678bff..59ac51f 100644 --- a/EstateReportingAPI.IntegrationTests/DatabaseHelper.cs +++ b/EstateReportingAPI.IntegrationTests/DatabaseHelper.cs @@ -10,6 +10,7 @@ using Shared.Logger; using TransactionProcessor.Database.Contexts; using TransactionProcessor.Database.Entities; +using TransactionProcessor.ProjectionEngine.Database.Database.Entities; namespace EstateReportingAPI.IntegrationTests; @@ -361,6 +362,7 @@ public async Task AddOperator(String estateName, String operatorName) public async Task AddMerchant(String estateName, String merchantName, + Decimal balance, DateTime createDateTime, DateTime lastSaleDateTime, (String addressLine1, String town, String postCode, String region) address, @@ -447,6 +449,13 @@ await this.Context.MerchantContracts.AddAsync(new MerchantContract { } } + MerchantBalanceProjectionState state = new MerchantBalanceProjectionState { + Balance = balance, + MerchantId = savedMerchant.MerchantId, + MerchantName = savedMerchant.Name + }; + await this.Context.MerchantBalanceProjectionState.AddAsync(state); + await this.Context.SaveChangesAsync(CancellationToken.None); return merchant.MerchantId; diff --git a/EstateReportingAPI.IntegrationTests/MerchantEndpointTests.cs b/EstateReportingAPI.IntegrationTests/MerchantEndpointTests.cs index 785996a..d3cf2ac 100644 --- a/EstateReportingAPI.IntegrationTests/MerchantEndpointTests.cs +++ b/EstateReportingAPI.IntegrationTests/MerchantEndpointTests.cs @@ -17,7 +17,7 @@ public class MerchantEndpointTests : ControllerTestsBase { public async Task MerchantEndpoint_GetMerchants_MerchantsReturned() { await this.helper.AddEstate("Test Estate", "Ref1"); for (int i = 0; i < 10; i++) { - await this.helper.AddMerchant("Test Estate", $"Test Merchant {i}", DateTime.Now, DateTime.Now, + await this.helper.AddMerchant("Test Estate", $"Test Merchant {i}",100 * i, DateTime.Now, DateTime.Now, ("Address Line 1", $"Test Town {i}", $"TE57 {i}NG", $"Region {i}"), ($"Contact {i}", @"{i}@2.com", $"{i}23456")); } @@ -32,6 +32,7 @@ await this.helper.AddMerchant("Test Estate", $"Test Merchant {i}", DateTime.Now, for (int i = 0; i < 10; i++) { Merchant? expected = merchants.SingleOrDefault(m => m.Name == $"Test Merchant {i}"); expected.ShouldNotBeNull(); + expected.Balance.ShouldBe(100 * i); } } @@ -39,7 +40,7 @@ await this.helper.AddMerchant("Test Estate", $"Test Merchant {i}", DateTime.Now, public async Task MerchantEndpoint_GetMerchant_MerchantReturned() { await this.helper.AddEstate("Test Estate", "Ref1"); - var merchantId = await this.helper.AddMerchant("Test Estate", $"Test Merchant 1", DateTime.Now, DateTime.Now, + var merchantId = await this.helper.AddMerchant("Test Estate", $"Test Merchant 1",100, DateTime.Now, DateTime.Now, ("Address Line 1", $"Test Town", $"TE57 1NG", $"Region"), ("Contact 1", "1@2.com", "123456")); @@ -49,6 +50,7 @@ public async Task MerchantEndpoint_GetMerchant_MerchantReturned() merchant.ShouldNotBeNull(); merchant.Name.ShouldBe("Test Merchant 1"); + merchant.Balance.ShouldBe(100); } [Fact] @@ -57,7 +59,7 @@ public async Task MerchantEndpoint_GetRecentMerchants_MerchantsReturned() await this.helper.AddEstate("Test Estate", "Ref1"); for (int i = 0; i < 10; i++) { - await this.helper.AddMerchant("Test Estate", $"Test Merchant {i}", DateTime.Now.AddDays(i*-1), DateTime.Now, + await this.helper.AddMerchant("Test Estate", $"Test Merchant {i}",100, DateTime.Now.AddDays(i*-1), DateTime.Now, ("Address Line 1", $"Test Town {i}", $"TE57 {i}NG", $"Region {i}"), ($"Contact {i}", @"{i}@2.com", $"{i}23456")); } @@ -82,7 +84,7 @@ public async Task MerchantEndpoint_GetMerchantOperators_MerchantOperatorsReturne await this.helper.AddOperator("Test Estate", "Safaricom"); await this.helper.AddOperator("Test Estate", "Voucher"); - var merchantId = await this.helper.AddMerchant("Test Estate", $"Test Merchant 1", DateTime.Now, DateTime.Now, + var merchantId = await this.helper.AddMerchant("Test Estate", $"Test Merchant 1",100, DateTime.Now, DateTime.Now, ("Address Line 1", $"Test Town", $"TE57 1NG", $"Region"), ("Contact 1", "1@2.com", "123456"), operators: ["Safaricom", "Voucher"]); @@ -110,7 +112,7 @@ public async Task MerchantEndpoint_GetMerchantContracts_MerchantContractsReturne List<(string productName, int productType, decimal? value)> voucherProductList = new() { ("10 KES Voucher", 0, 10.00m), ("Custom", 0, null) }; await this.helper.AddContractWithProducts("Test Estate", "Healthcare Centre 1 Contract", "Voucher", voucherProductList); - var merchantId = await this.helper.AddMerchant("Test Estate", $"Test Merchant 1", DateTime.Now, DateTime.Now, + var merchantId = await this.helper.AddMerchant("Test Estate", $"Test Merchant 1", 100, DateTime.Now, DateTime.Now, ("Address Line 1", $"Test Town", $"TE57 1NG", $"Region"), ("Contact 1", "1@2.com", "123456"), operators: ["Safaricom", "Voucher"], ["Safaricom Contract", "Healthcare Centre 1 Contract"]); @@ -130,7 +132,7 @@ public async Task MerchantEndpoint_GetMerchantDevices_MerchantDevicesReturned() { await this.helper.AddEstate("Test Estate", "Ref1"); - var merchantId = await this.helper.AddMerchant("Test Estate", $"Test Merchant 1", DateTime.Now, DateTime.Now, + var merchantId = await this.helper.AddMerchant("Test Estate", $"Test Merchant 1",100, DateTime.Now, DateTime.Now, ("Address Line 1", $"Test Town", $"TE57 1NG", $"Region"), ("Contact 1", "1@2.com", "123456"), devices: ["123456"]); @@ -148,28 +150,28 @@ public async Task MerchantEndpoint_GetMerchantKpis_MerchantKpisReturned() { await this.helper.AddEstate("Test Estate", "Ref1"); - await this.helper.AddMerchant("Test Estate", $"Test Merchant 1", DateTime.Now, DateTime.Now, + await this.helper.AddMerchant("Test Estate", $"Test Merchant 1",100, DateTime.Now, DateTime.Now, ("Address Line 1", $"Test Town", $"TE57 1NG", $"Region"), ("Contact 1", "1@2.com", "123456"), devices: ["123456"]); - await this.helper.AddMerchant("Test Estate", $"Test Merchant 2", DateTime.Now, DateTime.Now.AddMinutes(-10), + await this.helper.AddMerchant("Test Estate", $"Test Merchant 2", 200, DateTime.Now, DateTime.Now.AddMinutes(-10), ("Address Line 1", $"Test Town", $"TE57 1NG", $"Region"), ("Contact 1", "1@2.com", "123456"), devices: ["123456"]); - await this.helper.AddMerchant("Test Estate", $"Test Merchant 3", DateTime.Now, DateTime.Now.AddHours(-2), + await this.helper.AddMerchant("Test Estate", $"Test Merchant 3",300, DateTime.Now, DateTime.Now.AddHours(-2), ("Address Line 1", $"Test Town", $"TE57 1NG", $"Region"), ("Contact 1", "1@2.com", "123456"), devices: ["123456"]); - await this.helper.AddMerchant("Test Estate", $"Test Merchant 4", DateTime.Now, DateTime.Now.AddHours(-3), + await this.helper.AddMerchant("Test Estate", $"Test Merchant 4",400, DateTime.Now, DateTime.Now.AddHours(-3), ("Address Line 1", $"Test Town", $"TE57 1NG", $"Region"), ("Contact 1", "1@2.com", "123456"), devices: ["123456"]); - await this.helper.AddMerchant("Test Estate", $"Test Merchant 5", DateTime.Now, DateTime.Now.AddDays(-2), + await this.helper.AddMerchant("Test Estate", $"Test Merchant 5",500, DateTime.Now, DateTime.Now.AddDays(-2), ("Address Line 1", $"Test Town", $"TE57 1NG", $"Region"), ("Contact 1", "1@2.com", "123456"), devices: ["123456"]); - await this.helper.AddMerchant("Test Estate", $"Test Merchant 6", DateTime.Now, DateTime.Now.AddDays(-1), + await this.helper.AddMerchant("Test Estate", $"Test Merchant 6",600, DateTime.Now, DateTime.Now.AddDays(-1), ("Address Line 1", $"Test Town", $"TE57 1NG", $"Region"), ("Contact 1", "1@2.com", "123456"), devices: ["123456"]); - await this.helper.AddMerchant("Test Estate", $"Test Merchant 7", DateTime.Now, DateTime.Now.AddDays(-3), + await this.helper.AddMerchant("Test Estate", $"Test Merchant 7",700, DateTime.Now, DateTime.Now.AddDays(-3), ("Address Line 1", $"Test Town", $"TE57 1NG", $"Region"), ("Contact 1", "1@2.com", "123456"), devices: ["123456"]); - await this.helper.AddMerchant("Test Estate", $"Test Merchant 8", DateTime.Now, DateTime.Now.AddDays(-10), + await this.helper.AddMerchant("Test Estate", $"Test Merchant 8",800, DateTime.Now, DateTime.Now.AddDays(-10), ("Address Line 1", $"Test Town", $"TE57 1NG", $"Region"), ("Contact 1", "1@2.com", "123456"), devices: ["123456"]); diff --git a/EstateReportingAPI.IntegrationTests/TransactionsEndpointTests.cs b/EstateReportingAPI.IntegrationTests/TransactionsEndpointTests.cs index b2848ae..329583d 100644 --- a/EstateReportingAPI.IntegrationTests/TransactionsEndpointTests.cs +++ b/EstateReportingAPI.IntegrationTests/TransactionsEndpointTests.cs @@ -46,10 +46,10 @@ protected override async Task SetupStandingData() { this.TestOutputHelper.WriteLine($"Setup Operators {sw.ElapsedMilliseconds}ms"); sw.Restart(); // Merchants - await this.helper.AddMerchant("Test Estate", "Test Merchant 1", DateTime.MinValue, DateTime.MinValue, default, default); - await this.helper.AddMerchant("Test Estate", "Test Merchant 2", DateTime.MinValue, DateTime.MinValue, default, default); - await this.helper.AddMerchant("Test Estate", "Test Merchant 3", DateTime.MinValue, DateTime.MinValue, default, default); - await this.helper.AddMerchant("Test Estate", "Test Merchant 4", DateTime.MinValue, DateTime.MinValue, default, default); + await this.helper.AddMerchant("Test Estate", "Test Merchant 1",100, DateTime.MinValue, DateTime.MinValue, default, default); + await this.helper.AddMerchant("Test Estate", "Test Merchant 2", 100, DateTime.MinValue, DateTime.MinValue, default, default); + await this.helper.AddMerchant("Test Estate", "Test Merchant 3", 100, DateTime.MinValue, DateTime.MinValue, default, default); + await this.helper.AddMerchant("Test Estate", "Test Merchant 4", 100, DateTime.MinValue, DateTime.MinValue, default, default); sw.Stop(); this.TestOutputHelper.WriteLine($"Setup Merchants {sw.ElapsedMilliseconds}ms"); sw.Restart(); @@ -1106,10 +1106,10 @@ protected override async Task SetupStandingData() { this.TestOutputHelper.WriteLine($"Setup Operators {sw.ElapsedMilliseconds}ms"); sw.Restart(); // Merchants - await this.helper.AddMerchant("Test Estate", "Test Merchant 1", DateTime.MinValue, DateTime.MinValue, default, default); - await this.helper.AddMerchant("Test Estate", "Test Merchant 2", DateTime.MinValue, DateTime.MinValue, default, default); - await this.helper.AddMerchant("Test Estate", "Test Merchant 3", DateTime.MinValue, DateTime.MinValue, default, default); - await this.helper.AddMerchant("Test Estate", "Test Merchant 4", DateTime.MinValue, DateTime.MinValue, default, default); + await this.helper.AddMerchant("Test Estate", "Test Merchant 1",100, DateTime.MinValue, DateTime.MinValue, default, default); + await this.helper.AddMerchant("Test Estate", "Test Merchant 2",100, DateTime.MinValue, DateTime.MinValue, default, default); + await this.helper.AddMerchant("Test Estate", "Test Merchant 3",100, DateTime.MinValue, DateTime.MinValue, default, default); + await this.helper.AddMerchant("Test Estate", "Test Merchant 4",100, DateTime.MinValue, DateTime.MinValue, default, default); sw.Stop(); this.TestOutputHelper.WriteLine($"Setup Merchants {sw.ElapsedMilliseconds}ms"); sw.Restart();