From 90bc849d7478e058b453a1739cb964173412e499 Mon Sep 17 00:00:00 2001 From: StuartFerguson Date: Mon, 6 Jul 2026 09:40:00 +0100 Subject: [PATCH 1/2] Refactor DTOs and API client for reporting alignment Refactored DTOs for merchant daily performance and transaction mix summary to use new BackendAPI.DataTransferObjects. Updated EstateReportingApiClient and its interface to use these DTOs. Application service now maps between internal and backend DTOs. Updated tests and integration steps to match new structure. Added ApplicationVersion to request DTOs and introduced new DTO definitions in Class1.cs. --- ...tionProcessorACLApplicationServiceTests.cs | 32 ++--- .../BackendAPI/DataTransferObjects/Class1.cs | 112 ++++++++++++++++++ .../BackendAPI/EstateReportingApiClient.cs | 17 ++- .../BackendAPI/IEstateReportingApiClient.cs | 12 +- ...ansactionProcessorACLApplicationService.cs | 60 +++++++++- .../MerchantDailyPerformanceSummaryRequest.cs | 1 + .../MerchantTransactionMixSummaryRequest.cs | 1 + .../Common/GenericSteps.cs | 2 + .../Reporting/Reporting.feature | 1 - .../Shared/ACLSteps.cs | 27 +++-- 10 files changed, 216 insertions(+), 49 deletions(-) create mode 100644 TransactionProcessorACL.BusinessLogic/BackendAPI/DataTransferObjects/Class1.cs diff --git a/TransactionProcessorACL.BusinessLogic.Tests/TransactionProcessorACLApplicationServiceTests.cs b/TransactionProcessorACL.BusinessLogic.Tests/TransactionProcessorACLApplicationServiceTests.cs index 4f85a7f..940ba86 100644 --- a/TransactionProcessorACL.BusinessLogic.Tests/TransactionProcessorACLApplicationServiceTests.cs +++ b/TransactionProcessorACL.BusinessLogic.Tests/TransactionProcessorACLApplicationServiceTests.cs @@ -600,13 +600,13 @@ public async Task TransactionProcessorACLApplicationService_GetMerchantDailyPerf { MerchantDailyPerformanceSummaryRequest capturedRequest = null; estateReportingApiClient - .Setup(v => v.GetMerchantDailyPerformanceSummary(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Setup(v => v.GetMerchantDailyPerformanceSummary(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .Callback((_, _, request, _) => capturedRequest = request) - .ReturnsAsync(Result.Success(new MerchantDailyPerformanceSummaryResponse + .ReturnsAsync(Result.Success(new TransactionProcessorACL.BusinessLogic.BackendAPI.DataTransferObjects.MerchantDailyPerformanceSummaryResponse { Metrics = [ - new MetricItem + new TransactionProcessorACL.BusinessLogic.BackendAPI.DataTransferObjects.MetricItem { Title = "Total Sales Count", Value = 6, @@ -655,24 +655,24 @@ public async Task TransactionProcessorACLApplicationService_GetMerchantDailyPerf [Fact] public async Task TransactionProcessorACLApplicationService_GetMerchantTransactionMixSummary_ReturnedFromEstateReportingClient() { - MerchantTransactionMixSummaryRequest capturedRequest = null; + TransactionProcessorACL.BusinessLogic.BackendAPI.DataTransferObjects.TransactionMixSummaryRequest capturedRequest = null; estateReportingApiClient - .Setup(v => v.GetMerchantTransactionMixSummary(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .Callback((_, _, request, _) => capturedRequest = request) - .ReturnsAsync(Result.Success(new MerchantTransactionMixSummaryResponse + .Setup(v => v.GetMerchantTransactionMixSummary(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Callback((_, _, request, _) => capturedRequest = request) + .ReturnsAsync(Result.Success(new TransactionProcessorACL.BusinessLogic.BackendAPI.DataTransferObjects.TransactionMixSummaryResponse { FromDate = new DateTime(2026, 7, 1), ToDate = new DateTime(2026, 7, 3), - Breakdown = TransactionProcessorACL.Models.TransactionMixBreakdown.Product, - Measure = TransactionProcessorACL.Models.TransactionMixMeasure.Count, - Items = + Breakdown = TransactionProcessorACL.BusinessLogic.BackendAPI.DataTransferObjects.TransactionMixBreakdown.Product, + Measure = TransactionProcessorACL.BusinessLogic.BackendAPI.DataTransferObjects.TransactionMixMeasure.Count, + Groups = [ - new TransactionMixSummaryItem + new TransactionProcessorACL.BusinessLogic.BackendAPI.DataTransferObjects.TransactionMixSummaryGroup() { - Key = "product-1", - Label = "Product 1", - Count = 6, - Value = 42.75M + GroupKey = "product-1", + GroupName = "Product 1", + TransactionCount = 6, + TransactionValue = 42.75M } ] })); @@ -696,7 +696,7 @@ public async Task TransactionProcessorACLApplicationService_GetMerchantTransacti result.Data.Items.Count.ShouldBe(1); capturedRequest.ShouldNotBeNull(); capturedRequest.MerchantReportingId.ShouldBe(12345); - capturedRequest.Breakdown.ShouldBe(RequestTransactionMixBreakdown.Product); + //capturedRequest.Breakdown.ShouldBe(RequestTransactionMixBreakdown.Product); } } } diff --git a/TransactionProcessorACL.BusinessLogic/BackendAPI/DataTransferObjects/Class1.cs b/TransactionProcessorACL.BusinessLogic/BackendAPI/DataTransferObjects/Class1.cs new file mode 100644 index 0000000..f49f6a0 --- /dev/null +++ b/TransactionProcessorACL.BusinessLogic/BackendAPI/DataTransferObjects/Class1.cs @@ -0,0 +1,112 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.Json.Serialization; + +namespace TransactionProcessorACL.BusinessLogic.BackendAPI.DataTransferObjects +{ + public class TransactionMixSummaryResponse + { + public DateTime FromDate { get; set; } + public DateTime ToDate { get; set; } + public TransactionMixBreakdown Breakdown { get; set; } + public TransactionMixMeasure Measure { get; set; } + public int TotalCount { get; set; } + public decimal TotalValue { get; set; } + public List Groups { get; set; } = []; + public List Transactions { get; set; } = []; + } + + public class TransactionMixSummaryGroup + { + public string? GroupKey { get; set; } + public string? GroupName { get; set; } + public int TransactionCount { get; set; } + public decimal TransactionValue { get; set; } + } + + public class TransactionMixSummaryTransaction + { + public Guid Id { get; set; } + public DateTime DateTime { get; set; } + public string? Merchant { get; set; } + public Guid MerchantId { get; set; } + public int MerchantReportingId { get; set; } + public string? Operator { get; set; } + public Guid OperatorId { get; set; } + public int OperatorReportingId { get; set; } + public string? Product { get; set; } + public Guid ProductId { get; set; } + public int ProductReportingId { get; set; } + public string? Type { get; set; } + public string? Status { get; set; } + public decimal Value { get; set; } + public decimal TotalFees { get; set; } + public string? SettlementReference { get; set; } + public int TransactionNumber { get; set; } + } + + [JsonConverter(typeof(JsonStringEnumConverter))] + public enum TransactionMixBreakdown + { + Product, + TransactionType, + Operator, + Status + } + + [JsonConverter(typeof(JsonStringEnumConverter))] + public enum TransactionMixMeasure + { + Count, + Value + } + + public class TransactionMixSummaryRequest + { + public int? MerchantReportingId { get; set; } + public DateTime StartDate { get; set; } + public DateTime EndDate { get; set; } + public TransactionMixBreakdown Breakdown { get; set; } + public TransactionMixMeasure Measure { get; set; } + public int TopN { get; set; } = 5; + } + + public class MerchantDailyPerformanceSummaryRequest + { + public Int32 MerchantReportingId { get; set; } + public DateTime StartDate { get; set; } + public DateTime EndDate { get; set; } + } + + public sealed class MerchantDailyPerformanceSummaryResponse + { + public List Metrics { get; set; } = []; + + public List DrillDownTransactions { get; set; } = []; + } + + public sealed class MetricItem + { + public string Title { get; set; } + + public Decimal Value { get; set; } + + public string Description { get; set; } + + public int Category { get; set; } + } + + public sealed class DrillDownTransaction + { + public string Reference { get; set; } + + public string Product { get; set; } + + public string Status { get; set; } + + public Decimal Amount { get; set; } + + public DateTime TransactionDateTime { get; set; } + } +} diff --git a/TransactionProcessorACL.BusinessLogic/BackendAPI/EstateReportingApiClient.cs b/TransactionProcessorACL.BusinessLogic/BackendAPI/EstateReportingApiClient.cs index 5c96e19..9d0b286 100644 --- a/TransactionProcessorACL.BusinessLogic/BackendAPI/EstateReportingApiClient.cs +++ b/TransactionProcessorACL.BusinessLogic/BackendAPI/EstateReportingApiClient.cs @@ -3,12 +3,9 @@ using System.Net.Http; using System.Threading; using System.Threading.Tasks; -using ClientProxyBase; -using Shared.General; using Shared.Results; using SimpleResults; -using TransactionProcessorACL.DataTransferObjects.Requests; -using TransactionProcessorACL.Models; +using TransactionProcessorACL.BusinessLogic.BackendAPI.DataTransferObjects; namespace TransactionProcessorACL.BusinessLogic.BackendAPI; @@ -54,10 +51,10 @@ public async Task> GetMerchantDa } } - public async Task> GetMerchantTransactionMixSummary(String accessToken, - Guid estateId, - MerchantTransactionMixSummaryRequest request, - CancellationToken cancellationToken) + public async Task> GetMerchantTransactionMixSummary(String accessToken, + Guid estateId, + TransactionMixSummaryRequest request, + CancellationToken cancellationToken) { string requestUri = this.BuildRequestUrl("/api/transactions/transactionmixsummary"); @@ -68,8 +65,8 @@ public async Task> GetMerchantTran (EstateIdHeaderName, estateId.ToString()) ]; - Result result = - await this.Post(requestUri, request, accessToken, additionalHeaders, cancellationToken); + Result result = + await this.Post(requestUri, request, accessToken, additionalHeaders, cancellationToken); if (result.IsFailed) return ResultHelpers.CreateFailure(result); diff --git a/TransactionProcessorACL.BusinessLogic/BackendAPI/IEstateReportingApiClient.cs b/TransactionProcessorACL.BusinessLogic/BackendAPI/IEstateReportingApiClient.cs index 9b1bc59..8cdca7f 100644 --- a/TransactionProcessorACL.BusinessLogic/BackendAPI/IEstateReportingApiClient.cs +++ b/TransactionProcessorACL.BusinessLogic/BackendAPI/IEstateReportingApiClient.cs @@ -2,8 +2,8 @@ using System.Threading; using System.Threading.Tasks; using SimpleResults; -using TransactionProcessorACL.DataTransferObjects.Requests; -using TransactionProcessorACL.Models; +using TransactionProcessorACL.BusinessLogic.BackendAPI.DataTransferObjects; + namespace TransactionProcessorACL.BusinessLogic.BackendAPI; @@ -14,8 +14,8 @@ Task> GetMerchantDailyPerformanc MerchantDailyPerformanceSummaryRequest request, CancellationToken cancellationToken); - Task> GetMerchantTransactionMixSummary(String accessToken, - Guid estateId, - MerchantTransactionMixSummaryRequest request, - CancellationToken cancellationToken); + Task> GetMerchantTransactionMixSummary(String accessToken, + Guid estateId, + TransactionMixSummaryRequest request, + CancellationToken cancellationToken); } diff --git a/TransactionProcessorACL.BusinessLogic/Services/TransactionProcessorACLApplicationService.cs b/TransactionProcessorACL.BusinessLogic/Services/TransactionProcessorACLApplicationService.cs index a301ad9..2274bd0 100644 --- a/TransactionProcessorACL.BusinessLogic/Services/TransactionProcessorACLApplicationService.cs +++ b/TransactionProcessorACL.BusinessLogic/Services/TransactionProcessorACLApplicationService.cs @@ -1,7 +1,9 @@ -using SecurityService.DataTransferObjects; +using System.Linq; +using SecurityService.DataTransferObjects; using Shared.Exceptions; using Shared.Results; using SimpleResults; +using TransactionProcessor.DataTransferObjects.Requests.Merchant; using TransactionProcessor.DataTransferObjects.Responses.Contract; namespace TransactionProcessorACL.BusinessLogic.Services @@ -375,8 +377,26 @@ public async Task> GetMerchantDa return ResultHelpers.CreateFailure(accessTokenResult); } + BackendAPI.DataTransferObjects.MerchantDailyPerformanceSummaryRequest apiRequest = new BackendAPI.DataTransferObjects.MerchantDailyPerformanceSummaryRequest { StartDate = request.StartDate, EndDate = request.EndDate, MerchantReportingId = request.MerchantReportingId }; + TokenResponse accessToken = accessTokenResult.Data; - return await this.EstateReportingApiClient.GetMerchantDailyPerformanceSummary(accessToken.AccessToken, estateId, request, cancellationToken); + Result apiResponse = await this.EstateReportingApiClient.GetMerchantDailyPerformanceSummary(accessToken.AccessToken, estateId, apiRequest, cancellationToken); + + if (apiResponse.IsFailed) + return ResultHelpers.CreateFailure(apiResponse); + + MerchantDailyPerformanceSummaryResponse response = new MerchantDailyPerformanceSummaryResponse { + Metrics = apiResponse.Data.Metrics.Select(m => new MetricItem { Category = m.Category, Description = m.Description, Title = m.Title, Value = m.Value }).ToList(), + DrillDownTransactions = apiResponse.Data.DrillDownTransactions.Select(d => new DrillDownTransaction { + Amount = d.Amount, + Product = d.Product, + Reference = d.Reference, + Status = d.Status, + TransactionDateTime = d.TransactionDateTime + }).ToList() + }; + + return response; } public async Task> GetMerchantTransactionMixSummary(Guid estateId, @@ -388,8 +408,42 @@ public async Task> GetMerchantTran return ResultHelpers.CreateFailure(accessTokenResult); } + var apiRequest = new BackendAPI.DataTransferObjects.TransactionMixSummaryRequest { + StartDate = request.StartDate, + EndDate = request.EndDate, + Breakdown = Enum.Parse(request.Breakdown.ToString()), + Measure = Enum.Parse(request.Measure.ToString()), + MerchantReportingId = request.MerchantReportingId, + TopN = request.TopN + }; + TokenResponse accessToken = accessTokenResult.Data; - return await this.EstateReportingApiClient.GetMerchantTransactionMixSummary(accessToken.AccessToken, estateId, request, cancellationToken); + var apiResponse = await this.EstateReportingApiClient.GetMerchantTransactionMixSummary(accessToken.AccessToken, estateId, apiRequest, cancellationToken); + + if (apiResponse.IsFailed) + return ResultHelpers.CreateFailure(apiResponse); + + MerchantTransactionMixSummaryResponse response = new MerchantTransactionMixSummaryResponse() { + Breakdown = Enum.Parse(apiResponse.Data.Breakdown.ToString()), + Measure = Enum.Parse(apiResponse.Data.Measure.ToString()), + FromDate = apiResponse.Data.FromDate, + ToDate = apiResponse.Data.ToDate, + TotalCount = apiResponse.Data.TotalCount, + TotalValue = apiResponse.Data.TotalValue, + Items = apiResponse.Data.Groups.Select(g => new TransactionMixSummaryItem { Value = g.TransactionValue, Count = g.TransactionCount, Key = g.GroupKey, Label = g.GroupName }).ToList(), + DrillDownTransactions = apiResponse.Data.Transactions.Select(t => new TransactionMixDrillDownTransaction { + Amount = t.Value, + Operator = t.Operator, + Product = t.Product, + Reference = t.SettlementReference, + Status = t.Status, + TransactionDateTime = t.DateTime, + TransactionType = t.Type + }).ToList() + + }; + + return response; } private static ProcessReconciliationResponse CreateProcessReconciliationResponse(ReconciliationResponse reconciliationResponse) diff --git a/TransactionProcessorACL.DataTransferObjects/Requests/MerchantDailyPerformanceSummaryRequest.cs b/TransactionProcessorACL.DataTransferObjects/Requests/MerchantDailyPerformanceSummaryRequest.cs index 9f3cbbf..95df5a0 100644 --- a/TransactionProcessorACL.DataTransferObjects/Requests/MerchantDailyPerformanceSummaryRequest.cs +++ b/TransactionProcessorACL.DataTransferObjects/Requests/MerchantDailyPerformanceSummaryRequest.cs @@ -11,4 +11,5 @@ public class MerchantDailyPerformanceSummaryRequest public DateTime StartDate { get; set; } public DateTime EndDate { get; set; } + public String ApplicationVersion { get; set; } } diff --git a/TransactionProcessorACL.DataTransferObjects/Requests/MerchantTransactionMixSummaryRequest.cs b/TransactionProcessorACL.DataTransferObjects/Requests/MerchantTransactionMixSummaryRequest.cs index c94ca56..467a2e0 100644 --- a/TransactionProcessorACL.DataTransferObjects/Requests/MerchantTransactionMixSummaryRequest.cs +++ b/TransactionProcessorACL.DataTransferObjects/Requests/MerchantTransactionMixSummaryRequest.cs @@ -15,6 +15,7 @@ public class MerchantTransactionMixSummaryRequest public TransactionMixBreakdown Breakdown { get; set; } public TransactionMixMeasure Measure { get; set; } + public String ApplicationVersion { get; set; } public int TopN { get; set; } = 5; } diff --git a/TransactionProcessorACL.IntegrationTests/Common/GenericSteps.cs b/TransactionProcessorACL.IntegrationTests/Common/GenericSteps.cs index 729a3d5..3a264b1 100644 --- a/TransactionProcessorACL.IntegrationTests/Common/GenericSteps.cs +++ b/TransactionProcessorACL.IntegrationTests/Common/GenericSteps.cs @@ -59,6 +59,8 @@ public async Task StartSystem() this.TestingContext.DockerHelper.SqlServerContainerName = "sharedsqlserver"; this.TestingContext.DockerHelper.SetImageDetails(ContainerType.TransactionProcessorAcl, ("transactionprocessoracl", false)); + this.TestingContext.DockerHelper.SetImageDetails(ContainerType.EstateReporting + , ("stuartferguson/estatereportingapi:master", true)); this.TestingContext.Logger = logger; this.TestingContext.Logger.LogInformation("About to Start Containers for Scenario Run"); diff --git a/TransactionProcessorACL.IntegrationTests/Reporting/Reporting.feature b/TransactionProcessorACL.IntegrationTests/Reporting/Reporting.feature index 2c6c852..edf8c64 100644 --- a/TransactionProcessorACL.IntegrationTests/Reporting/Reporting.feature +++ b/TransactionProcessorACL.IntegrationTests/Reporting/Reporting.feature @@ -119,7 +119,6 @@ Scenario: Merchant daily performance summary Then the merchant daily performance summary response should contain at least one metric and the sale amount 50.00 @PRTest -@ignore Scenario: Merchant transaction mix summary Given I am logged in as "merchantuser@testmerchant1.co.uk" with password "123456" for Merchant "Test Merchant 1" for Estate "Test Estate 1" with client "merchantClient" When I perform the following transactions diff --git a/TransactionProcessorACL.IntegrationTests/Shared/ACLSteps.cs b/TransactionProcessorACL.IntegrationTests/Shared/ACLSteps.cs index 84e4524..5a72b25 100644 --- a/TransactionProcessorACL.IntegrationTests/Shared/ACLSteps.cs +++ b/TransactionProcessorACL.IntegrationTests/Shared/ACLSteps.cs @@ -1,6 +1,7 @@ using Shared.Serialisation; using SimpleResults; using TransactionProcessor.IntegrationTesting.Helpers; +using TransactionProcessorACL.DataTransferObjects.Requests; namespace TransactionProcessorACL.IntegrationTests.Shared; @@ -247,12 +248,12 @@ public async Task WhenIGetTheMerchantDailyPerformanceSummaryForMerchantForEstate this.HttpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", userAccessToken); - Object request = new + MerchantDailyPerformanceSummaryRequest request = new() { - application_version = "1.0.5", - merchant_reporting_id = merchantReportingId, - start_date = DateTime.Today, - end_date = DateTime.Today, + EndDate = DateTime.Today, + StartDate = DateTime.Today, + ApplicationVersion = "1.0.5", + MerchantReportingId = merchantReportingId }; @@ -303,15 +304,15 @@ public async Task WhenIGetTheMerchantTransactionMixSummaryForMerchantForEstate(S this.HttpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", userAccessToken); - Object request = new + MerchantTransactionMixSummaryRequest request= new() { - application_version = "1.0.5", - merchant_reporting_id = merchantReportingId, - start_date = DateTime.Today, - end_date = DateTime.Today, - breakdown = 2, - measure = 1, - top_n = 5, + ApplicationVersion = "1.0.5", + MerchantReportingId = merchantReportingId, + StartDate = DateTime.Today, + EndDate = DateTime.Today, + Breakdown = TransactionMixBreakdown.Product, + Measure = TransactionMixMeasure.Count, + TopN = 5, }; StringContent content = new StringContent(StringSerialiser.Serialise(request), Encoding.UTF8, "application/json"); From 0768c546a17c7d973dfe64c70166df77ba5e404d Mon Sep 17 00:00:00 2001 From: StuartFerguson Date: Mon, 6 Jul 2026 09:54:09 +0100 Subject: [PATCH 2/2] fix issue in unit tests --- .../TransactionProcessorACLApplicationServiceTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TransactionProcessorACL.BusinessLogic.Tests/TransactionProcessorACLApplicationServiceTests.cs b/TransactionProcessorACL.BusinessLogic.Tests/TransactionProcessorACLApplicationServiceTests.cs index 940ba86..6879c52 100644 --- a/TransactionProcessorACL.BusinessLogic.Tests/TransactionProcessorACLApplicationServiceTests.cs +++ b/TransactionProcessorACL.BusinessLogic.Tests/TransactionProcessorACLApplicationServiceTests.cs @@ -598,10 +598,10 @@ public async Task TransactionProcessorACLApplicationService_GetMerchantSchedule_ [Fact] public async Task TransactionProcessorACLApplicationService_GetMerchantDailyPerformanceSummary_ReturnedFromEstateReportingClient() { - MerchantDailyPerformanceSummaryRequest capturedRequest = null; + TransactionProcessorACL.BusinessLogic.BackendAPI.DataTransferObjects.MerchantDailyPerformanceSummaryRequest capturedRequest = null; estateReportingApiClient .Setup(v => v.GetMerchantDailyPerformanceSummary(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .Callback((_, _, request, _) => capturedRequest = request) + .Callback((_, _, request, _) => capturedRequest = request) .ReturnsAsync(Result.Success(new TransactionProcessorACL.BusinessLogic.BackendAPI.DataTransferObjects.MerchantDailyPerformanceSummaryResponse { Metrics =