From 2793db724465dafb191e395ada2699b6913bf600 Mon Sep 17 00:00:00 2001 From: Stuart Ferguson Date: Mon, 23 Dec 2024 16:16:06 +0000 Subject: [PATCH 1/2] improve code coverage --- .../Queries/CalendarQueries.cs | 13 + .../Queries/MerchantQueries.cs | 16 + .../Queries/OperatorQueries.cs | 14 + .../Queries/ProductQueries.cs | 14 + .../Queries/Queries.cs | 65 - .../Queries/ResponseCodeQueries.cs | 11 + .../Queries/SettlementQueries.cs | 17 + .../Queries/TransactionQueries.cs | 17 + .../ReportingManager.cs | 185 +- .../EstateReportingApiClient.cs | 44 +- ...ateReportingAPI.DataTransferObjects.csproj | 1 + .../ControllerTestsBase.cs | 12 +- .../DatabaseHelper.cs | 243 +- .../DimensionControllerTests.cs | 7 + ...EstateReportingAPI.IntegrationTests.csproj | 2 +- .../FactSettlementsControllerTests.cs | 551 +++- .../FactTransactionsControllerTests.cs | 2938 +++++++++-------- .../xunit.runner.json | 4 +- .../EstateReportingAPI.Models.csproj | 1 + .../EstateReportingApiClientTests.cs | 587 ++++ .../General/BootstrapperTests.cs | 29 - .../General/QueryStringBuilderTests.cs | 33 + .../Bootstrapper/MediatorRegistry.cs | 6 +- .../Bootstrapper/MiddlewareRegistry.cs | 28 +- .../Bootstrapper/RepositoryRegistry.cs | 2 + .../Controllers/FactSettlementController.cs | 16 +- EstateReportingAPI/Startup.cs | 2 + 27 files changed, 3016 insertions(+), 1842 deletions(-) create mode 100644 EstateReportingAPI.BusinessLogic/Queries/CalendarQueries.cs create mode 100644 EstateReportingAPI.BusinessLogic/Queries/MerchantQueries.cs create mode 100644 EstateReportingAPI.BusinessLogic/Queries/OperatorQueries.cs create mode 100644 EstateReportingAPI.BusinessLogic/Queries/ProductQueries.cs delete mode 100644 EstateReportingAPI.BusinessLogic/Queries/Queries.cs create mode 100644 EstateReportingAPI.BusinessLogic/Queries/ResponseCodeQueries.cs create mode 100644 EstateReportingAPI.BusinessLogic/Queries/SettlementQueries.cs create mode 100644 EstateReportingAPI.BusinessLogic/Queries/TransactionQueries.cs create mode 100644 EstateReportingAPI.Tests/EstateReportingApiClientTests.cs create mode 100644 EstateReportingAPI.Tests/General/QueryStringBuilderTests.cs diff --git a/EstateReportingAPI.BusinessLogic/Queries/CalendarQueries.cs b/EstateReportingAPI.BusinessLogic/Queries/CalendarQueries.cs new file mode 100644 index 0000000..c2748aa --- /dev/null +++ b/EstateReportingAPI.BusinessLogic/Queries/CalendarQueries.cs @@ -0,0 +1,13 @@ +using System.Diagnostics.CodeAnalysis; +using EstateReportingAPI.Models; +using MediatR; +using SimpleResults; + +namespace EstateReportingAPI.BusinessLogic.Queries; + +[ExcludeFromCodeCoverage] +public record CalendarQueries { + public record GetComparisonDatesQuery(Guid EstateId) : IRequest>>; + public record GetAllDatesQuery(Guid EstateId) : IRequest>>; + public record GetYearsQuery(Guid EstateId) : IRequest>>; +} \ No newline at end of file diff --git a/EstateReportingAPI.BusinessLogic/Queries/MerchantQueries.cs b/EstateReportingAPI.BusinessLogic/Queries/MerchantQueries.cs new file mode 100644 index 0000000..a7bbe3d --- /dev/null +++ b/EstateReportingAPI.BusinessLogic/Queries/MerchantQueries.cs @@ -0,0 +1,16 @@ +using System.Diagnostics.CodeAnalysis; +using EstateReportingAPI.Models; +using MediatR; +using SimpleResults; + +namespace EstateReportingAPI.BusinessLogic.Queries; + +[ExcludeFromCodeCoverage] +public record MerchantQueries { + public record GetMerchantsQuery(Guid EstateId) : IRequest>>; + public record GetTransactionKpisQuery(Guid EstateId) : IRequest>; + public record GetByLastSaleQuery(Guid EstateId, DateTime StartDateTime, DateTime EndDateTime) : IRequest>>; + public record GetTopMerchantsBySalesValueQuery(Guid EstateId, Int32 numberOfMerchants) : IRequest>>; + public record GetBottomMerchantsBySalesValueQuery(Guid EstateId, Int32 numberOfMerchants) : IRequest>>; + public record GetMerchantPerformanceQuery(Guid EstateId, DateTime comparisonDate, List merchantReportingIds) : IRequest>; +} \ No newline at end of file diff --git a/EstateReportingAPI.BusinessLogic/Queries/OperatorQueries.cs b/EstateReportingAPI.BusinessLogic/Queries/OperatorQueries.cs new file mode 100644 index 0000000..f95f401 --- /dev/null +++ b/EstateReportingAPI.BusinessLogic/Queries/OperatorQueries.cs @@ -0,0 +1,14 @@ +using System.Diagnostics.CodeAnalysis; +using EstateReportingAPI.Models; +using MediatR; +using SimpleResults; + +namespace EstateReportingAPI.BusinessLogic.Queries; + +[ExcludeFromCodeCoverage] +public record OperatorQueries { + public record GetOperatorsQuery(Guid EstateId) : IRequest>>; + public record GetOperatorPerformanceQuery(Guid EstateId, DateTime comparisonDate, List operatorReportingIds) : IRequest>; + public record GetTopOperatorsBySalesValueQuery(Guid EstateId, Int32 numberOfOperators) : IRequest>>; + public record GetBottomOperatorsBySalesValueQuery(Guid EstateId, Int32 numberOfOperators) : IRequest>>; +} \ No newline at end of file diff --git a/EstateReportingAPI.BusinessLogic/Queries/ProductQueries.cs b/EstateReportingAPI.BusinessLogic/Queries/ProductQueries.cs new file mode 100644 index 0000000..8e44136 --- /dev/null +++ b/EstateReportingAPI.BusinessLogic/Queries/ProductQueries.cs @@ -0,0 +1,14 @@ +using System.Diagnostics.CodeAnalysis; +using EstateReportingAPI.Models; +using MediatR; +using SimpleResults; + +namespace EstateReportingAPI.BusinessLogic.Queries; + +[ExcludeFromCodeCoverage] +public record ProductQueries +{ + public record GetProductPerformanceQuery(Guid EstateId, DateTime comparisonDate, List productReportingIds) : IRequest>; + public record GetTopProductsBySalesValueQuery(Guid EstateId, Int32 numberOfProducts) : IRequest>>; + public record GetBottomProductsBySalesValueQuery(Guid EstateId, Int32 numberOfProducts) : IRequest>>; +} \ No newline at end of file diff --git a/EstateReportingAPI.BusinessLogic/Queries/Queries.cs b/EstateReportingAPI.BusinessLogic/Queries/Queries.cs deleted file mode 100644 index 59ed4a3..0000000 --- a/EstateReportingAPI.BusinessLogic/Queries/Queries.cs +++ /dev/null @@ -1,65 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using EstateReportingAPI.Models; -using MediatR; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.WebUtilities; -using SimpleResults; - -namespace EstateReportingAPI.BusinessLogic.Queries -{ - public record TransactionQueries { - public record TodaysSalesQuery(Guid estateId, Int32 merchantReportingId, Int32 operatorReportingId, DateTime comparisonDate) : IRequest>; - public record TodaysFailedSales(Guid estateId, DateTime comparisonDate, String responseCode) : IRequest>; - - public record TodaysSalesCountByHour(Guid estateId, Int32 merchantReportingId, Int32 operatorReportingId, DateTime comparisonDate) : IRequest>>; - public record TodaysSalesValueByHour(Guid estateId, Int32 merchantReportingId, Int32 operatorReportingId, DateTime comparisonDate) : IRequest>>; - - public record TransactionSearchQuery(Guid estateId, TransactionSearchRequest request, PagingRequest pagingRequest, Models.SortingRequest sortingRequest) : IRequest>>; - } - - public record CalendarQueries { - public record GetComparisonDatesQuery(Guid EstateId) : IRequest>>; - public record GetAllDatesQuery(Guid EstateId) : IRequest>>; - public record GetYearsQuery(Guid EstateId) : IRequest>>; - } - - public record MerchantQueries { - public record GetMerchantsQuery(Guid EstateId) : IRequest>>; - public record GetTransactionKpisQuery(Guid EstateId) : IRequest>; - public record GetByLastSaleQuery(Guid EstateId, DateTime StartDateTime, DateTime EndDateTime) : IRequest>>; - public record GetTopMerchantsBySalesValueQuery(Guid EstateId, Int32 numberOfMerchants) : IRequest>>; - public record GetBottomMerchantsBySalesValueQuery(Guid EstateId, Int32 numberOfMerchants) : IRequest>>; - public record GetMerchantPerformanceQuery(Guid EstateId, DateTime comparisonDate, List merchantReportingIds) : IRequest>; - } - - public record OperatorQueries { - public record GetOperatorsQuery(Guid EstateId) : IRequest>>; - public record GetOperatorPerformanceQuery(Guid EstateId, DateTime comparisonDate, List operatorReportingIds) : IRequest>; - public record GetTopOperatorsBySalesValueQuery(Guid EstateId, Int32 numberOfOperators) : IRequest>>; - public record GetBottomOperatorsBySalesValueQuery(Guid EstateId, Int32 numberOfOperators) : IRequest>>; - } - - public record ProductQueries - { - public record GetProductPerformanceQuery(Guid EstateId, DateTime comparisonDate, List productReportingIds) : IRequest>; - public record GetTopProductsBySalesValueQuery(Guid EstateId, Int32 numberOfProducts) : IRequest>>; - public record GetBottomProductsBySalesValueQuery(Guid EstateId, Int32 numberOfProducts) : IRequest>>; - } - - public record ResponseCodeQueries { - public record GetResponseCodesQuery(Guid EstateId) : IRequest>>; - } - - public record SettlementQueries { - public record GetTodaysSettlementQuery(Guid EstateId, Int32 MerchantReportingId, Int32 OperatorReportingId, DateTime ComparisonDate) : IRequest>; - - public record GetLastSettlementQuery(Guid EstateId) : IRequest>; - - public record GetUnsettledFeesQuery(Guid EstateId,DateTime StartDate, DateTime EndDate, List MerchantIdFilter, List OperatorIdFilter, List ProductIdFilter, GroupByOption GroupByOption) : IRequest>>; - - } -} diff --git a/EstateReportingAPI.BusinessLogic/Queries/ResponseCodeQueries.cs b/EstateReportingAPI.BusinessLogic/Queries/ResponseCodeQueries.cs new file mode 100644 index 0000000..a059a57 --- /dev/null +++ b/EstateReportingAPI.BusinessLogic/Queries/ResponseCodeQueries.cs @@ -0,0 +1,11 @@ +using EstateReportingAPI.Models; +using MediatR; +using SimpleResults; +using System.Diagnostics.CodeAnalysis; + +namespace EstateReportingAPI.BusinessLogic.Queries; + +[ExcludeFromCodeCoverage] +public record ResponseCodeQueries { + public record GetResponseCodesQuery(Guid EstateId) : IRequest>>; +} \ No newline at end of file diff --git a/EstateReportingAPI.BusinessLogic/Queries/SettlementQueries.cs b/EstateReportingAPI.BusinessLogic/Queries/SettlementQueries.cs new file mode 100644 index 0000000..5fa5ade --- /dev/null +++ b/EstateReportingAPI.BusinessLogic/Queries/SettlementQueries.cs @@ -0,0 +1,17 @@ +using EstateReportingAPI.Models; +using MediatR; +using SimpleResults; +using System.Diagnostics.CodeAnalysis; + +namespace EstateReportingAPI.BusinessLogic.Queries +{ + [ExcludeFromCodeCoverage] + public record SettlementQueries { + public record GetTodaysSettlementQuery(Guid EstateId, Int32 MerchantReportingId, Int32 OperatorReportingId, DateTime ComparisonDate) : IRequest>; + + public record GetLastSettlementQuery(Guid EstateId) : IRequest>; + + public record GetUnsettledFeesQuery(Guid EstateId,DateTime StartDate, DateTime EndDate, List MerchantIdFilter, List OperatorIdFilter, List ProductIdFilter, GroupByOption GroupByOption) : IRequest>>; + + } +} diff --git a/EstateReportingAPI.BusinessLogic/Queries/TransactionQueries.cs b/EstateReportingAPI.BusinessLogic/Queries/TransactionQueries.cs new file mode 100644 index 0000000..99cf91d --- /dev/null +++ b/EstateReportingAPI.BusinessLogic/Queries/TransactionQueries.cs @@ -0,0 +1,17 @@ +using System.Diagnostics.CodeAnalysis; +using EstateReportingAPI.Models; +using MediatR; +using SimpleResults; + +namespace EstateReportingAPI.BusinessLogic.Queries; + +[ExcludeFromCodeCoverage] +public record TransactionQueries { + public record TodaysSalesQuery(Guid estateId, Int32 merchantReportingId, Int32 operatorReportingId, DateTime comparisonDate) : IRequest>; + public record TodaysFailedSales(Guid estateId, DateTime comparisonDate, String responseCode) : IRequest>; + + public record TodaysSalesCountByHour(Guid estateId, Int32 merchantReportingId, Int32 operatorReportingId, DateTime comparisonDate) : IRequest>>; + public record TodaysSalesValueByHour(Guid estateId, Int32 merchantReportingId, Int32 operatorReportingId, DateTime comparisonDate) : IRequest>>; + + public record TransactionSearchQuery(Guid estateId, TransactionSearchRequest request, PagingRequest pagingRequest, Models.SortingRequest sortingRequest) : IRequest>>; +} \ No newline at end of file diff --git a/EstateReportingAPI.BusinessLogic/ReportingManager.cs b/EstateReportingAPI.BusinessLogic/ReportingManager.cs index c7fc520..2a60fc6 100644 --- a/EstateReportingAPI.BusinessLogic/ReportingManager.cs +++ b/EstateReportingAPI.BusinessLogic/ReportingManager.cs @@ -36,93 +36,104 @@ public ReportingManager(Shared.EntityFramework.IDbContextFactory> GetUnsettledFees(Guid estateId, DateTime startDate, DateTime endDate, List merchantIds, List operatorIds, List productIds, GroupByOption? groupByOption, CancellationToken cancellationToken){ - // TODO: GetUnsettledFees - //EstateManagementGenericContext? context = await this.ContextFactory.GetContext(estateId, ReportingManager.ConnectionStringIdentifier, cancellationToken); - - //var fees = (from merchantSettlementFee in context.MerchantSettlementFees - // join transaction in context.Transactions - // on merchantSettlementFee.TransactionId equals transaction.TransactionId - // where merchantSettlementFee.FeeCalculatedDateTime.Date >= startDate && - // merchantSettlementFee.FeeCalculatedDateTime.Date <= endDate - // select new{ - // fee = merchantSettlementFee, - // txn = transaction - // }).AsQueryable(); - - //if (merchantIds.Any()){ - // fees = fees.Where(f => merchantIds.Contains(f.txn.MerchantReportingId)); - //} - //if (operatorIds.Any()) - //{ - // fees = fees.Where(f => operatorIds.Contains(f.txn.EstateOperatorReportingId)); - //} - //if (productIds.Any()) - //{ - // fees = fees.Where(f => merchantIds.Contains(f.txn.ContractProductReportingId)); - //} - - //List unsettledFees = new(); - - //switch(groupByOption){ - // case GroupByOption.Merchant: - // unsettledFees = await (from f in fees - // join merchant in context.Merchants - // on f.fee.MerchantReportingId equals merchant.MerchantReportingId - // group new{ - // f.fee.MerchantReportingId, - // CalculatedValue = f.fee.CalculatedValue, - // } by merchant.Name - // into grouped - // select new UnsettledFee{ - // DimensionName = grouped.Key, - // FeesValue = grouped.Sum(item => item.CalculatedValue), - // FeesCount = grouped.Count() - // }).ToListAsync(cancellationToken); - // break; - // case GroupByOption.Operator: - // unsettledFees = await (from f in fees - // join estateOperator in context.EstateOperators - // on f.txn.EstateOperatorReportingId equals estateOperator.OperatorReportingId - // join @operator in context.Operators on estateOperator.OperatorReportingId equals @operator.OperatorReportingId - // group new - // { - // @operator.OperatorReportingId, - // CalculatedValue = f.fee.CalculatedValue, - // } by @operator.Name - // into grouped - // select new UnsettledFee - // { - // DimensionName = grouped.Key, - // FeesValue = grouped.Sum(item => item.CalculatedValue), - // FeesCount = grouped.Count() - // }).ToListAsync(cancellationToken); - // break; - // case GroupByOption.Product: - // unsettledFees = await (from f in fees - // join contractProduct in context.ContractProducts - // on f.txn.ContractProductReportingId equals contractProduct.ContractProductReportingId - // join contract in context.Contracts - // on contractProduct.ContractReportingId equals contract.ContractReportingId - // join @operator in context.Operators - // on contract.OperatorId equals @operator.OperatorId - // group new - // { - // contractProduct.ContractProductReportingId, - // CalculatedValue = f.fee.CalculatedValue, - // } by new { @operator.Name, contractProduct.ProductName} - // into grouped - // select new UnsettledFee - // { - // DimensionName = $"{grouped.Key.Name} - {grouped.Key.ProductName}", - // FeesValue = grouped.Sum(item => item.CalculatedValue), - // FeesCount = grouped.Count() - // }).ToListAsync(cancellationToken); - - // break; - //} - - //return unsettledFees; - return null; + + EstateManagementGenericContext? context = await this.ContextFactory.GetContext(estateId, ReportingManager.ConnectionStringIdentifier, cancellationToken); + + var fees = (from merchantSettlementFee in context.MerchantSettlementFees + join transaction in context.Transactions + on merchantSettlementFee.TransactionId equals transaction.TransactionId + where merchantSettlementFee.FeeCalculatedDateTime.Date >= startDate && + merchantSettlementFee.FeeCalculatedDateTime.Date <= endDate + select new + { + fee = merchantSettlementFee, + txn = transaction + }).AsQueryable(); + + if (merchantIds.Any()) + { + // Get a list of the merchant guid's + List merchantGuids = await context.Merchants.Where(m => merchantIds.Contains(m.MerchantReportingId)).Select(m => m.MerchantId).ToListAsync(cancellationToken); + // Filter the fees now + fees = fees.Where(f => merchantGuids.Contains(f.txn.MerchantId)); + } + if (operatorIds.Any()) + { + // Get a list of the operator guid's + List operatorGuids = await context.Operators.Where(m => operatorIds.Contains(m.OperatorReportingId)).Select(m => m.OperatorId).ToListAsync(cancellationToken); + // Filter the fees now + fees = fees.Where(f => operatorGuids.Contains(f.txn.OperatorId)); + } + if (productIds.Any()) + { + // Get a list of the operator guid's + List productGuids = await context.ContractProducts.Where(m => productIds.Contains(m.ContractProductReportingId)).Select(m => m.ContractProductId).ToListAsync(cancellationToken); + // Filter the fees now + fees = fees.Where(f => productGuids.Contains(f.txn.ContractProductId)); + } + + List unsettledFees = new(); + + switch (groupByOption) + { + case GroupByOption.Merchant: + unsettledFees = await (from f in fees + join merchant in context.Merchants + on f.fee.MerchantId equals merchant.MerchantId + group new + { + f.fee.MerchantId, + CalculatedValue = f.fee.CalculatedValue, + } by merchant.Name + into grouped + select new UnsettledFee + { + DimensionName = grouped.Key, + FeesValue = grouped.Sum(item => item.CalculatedValue), + FeesCount = grouped.Count() + }).ToListAsync(cancellationToken); + break; + case GroupByOption.Operator: + unsettledFees = await (from f in fees + join @operator in context.Operators on f.txn.OperatorId equals @operator.OperatorId + group new + { + @operator.OperatorId, + CalculatedValue = f.fee.CalculatedValue, + } by @operator.Name + into grouped + select new UnsettledFee + { + DimensionName = grouped.Key, + FeesValue = grouped.Sum(item => item.CalculatedValue), + FeesCount = grouped.Count() + }).ToListAsync(cancellationToken); + break; + case GroupByOption.Product: + unsettledFees = await (from f in fees + join contractProduct in context.ContractProducts + on f.txn.ContractProductId equals contractProduct.ContractProductId + join contract in context.Contracts + on contractProduct.ContractId equals contract.ContractId + join @operator in context.Operators + on contract.OperatorId equals @operator.OperatorId + group new + { + contractProduct.ContractProductId, + CalculatedValue = f.fee.CalculatedValue, + } by new { @operator.Name, contractProduct.ProductName } + into grouped + select new UnsettledFee + { + DimensionName = $"{grouped.Key.Name} - {grouped.Key.ProductName}", + FeesValue = grouped.Sum(item => item.CalculatedValue), + FeesCount = grouped.Count() + }).ToListAsync(cancellationToken); + + break; + } + + return unsettledFees; } public async Task> GetCalendarComparisonDates(Guid estateId, CancellationToken cancellationToken){ diff --git a/EstateReportingAPI.Client/EstateReportingApiClient.cs b/EstateReportingAPI.Client/EstateReportingApiClient.cs index 678a2ec..6e49e64 100644 --- a/EstateReportingAPI.Client/EstateReportingApiClient.cs +++ b/EstateReportingAPI.Client/EstateReportingApiClient.cs @@ -62,7 +62,7 @@ public async Task>> GetCalendarDates(String accessToke // An exception has occurred, add some additional information to the message Exception exception = new Exception($"Error getting calendar dates for year {year} for estate {{estateId}}.", ex); - throw exception; + return Result.Failure(exception.Message); } } @@ -95,7 +95,7 @@ public async Task>> GetCalendarYears(String accessToke // An exception has occurred, add some additional information to the message Exception exception = new Exception($"Error getting calendar years for estate {estateId}.", ex); - throw exception; + return Result.Failure(exception.Message); } } @@ -117,7 +117,7 @@ public async Task>> GetComparisonDates(String access // An exception has occurred, add some additional information to the message Exception exception = new Exception($"Error getting comparison dates for estate {estateId}.", ex); - throw exception; + return Result.Failure(exception.Message); } } @@ -141,7 +141,7 @@ public async Task> GetLastSettlement(String accessToken, // An exception has occurred, add some additional information to the message Exception exception = new Exception($"Error getting last settlement for estate {estateId}.", ex); - throw exception; + return Result.Failure(exception.Message); } return response; @@ -167,7 +167,7 @@ public async Task>> GetResponseCodes(String accessToke // An exception has occurred, add some additional information to the message Exception exception = new Exception($"Error getting response codes for estate {estateId}.", ex); - throw exception; + return Result.Failure(exception.Message); } } @@ -195,7 +195,7 @@ public async Task> GetMerchantPerformance(String accessToken // An exception has occurred, add some additional information to the message Exception exception = new Exception($"Error getting merchant performance for estate {estateId}.", ex); - throw exception; + return Result.Failure(exception.Message); } } @@ -222,7 +222,7 @@ public async Task> GetProductPerformance(String accessToken, // An exception has occurred, add some additional information to the message Exception exception = new Exception($"Error getting product performance for estate {estateId}.", ex); - throw exception; + return Result.Failure(exception.Message); } } @@ -247,7 +247,7 @@ public async Task>> GetMerchantsByLastSaleDate(String acce // An exception has occurred, add some additional information to the message Exception exception = new Exception($"Error getting merchant by last sale date estate {estateId}.", ex); - throw exception; + return Result.Failure(exception.Message); } } @@ -274,7 +274,7 @@ public async Task> GetOperatorPerformance(String accessToken // An exception has occurred, add some additional information to the message Exception exception = new Exception($"Error getting operator performance for estate {estateId}.", ex); - throw exception; + return Result.Failure(exception.Message); } } @@ -317,7 +317,7 @@ public async Task>> TransactionSearch(String acce // An exception has occurred, add some additional information to the message Exception exception = new Exception($"Error getting operator performance for estate {estateId}.", ex); - throw exception; + return Result.Failure(exception.Message); } } @@ -363,7 +363,7 @@ public async Task>> GetUnsettledFees(String accessToke // An exception has occurred, add some additional information to the message Exception exception = new Exception($"Error getting unsettled fees for estate {estateId}.", ex); - throw exception; + return Result.Failure(exception.Message); } } @@ -386,7 +386,7 @@ public async Task> GetMerchantKpi(String accessToken, Guid e // An exception has occurred, add some additional information to the message Exception exception = new Exception($"Error getting merchant kpis for estate {estateId}.", ex); - throw exception; + return Result.Failure(exception.Message); } } @@ -409,7 +409,7 @@ public async Task>> GetMerchants(String accessToken, Guid // An exception has occurred, add some additional information to the message Exception exception = new Exception($"Error getting merchants for estate {estateId}.", ex); - throw exception; + return Result.Failure(exception.Message); } } @@ -432,7 +432,7 @@ public async Task>> GetOperators(String accessToken, Guid // An exception has occurred, add some additional information to the message Exception exception = new Exception($"Error getting operators for estate {estateId}.", ex); - throw exception; + return Result.Failure(exception.Message); } } @@ -460,7 +460,7 @@ public async Task> GetTodaysFailedSales(String accessToken, // An exception has occurred, add some additional information to the message Exception exception = new Exception($"Error getting todays failed sales for estate {estateId} and response code {responseCode}.", ex); - throw exception; + return Result.Failure(exception.Message); } } @@ -487,7 +487,7 @@ public async Task> GetTodaysSales(String accessToken, Guid e // An exception has occurred, add some additional information to the message Exception exception = new Exception($"Error getting todays sales for estate {estateId}.", ex); - throw exception; + return Result.Failure(exception.Message); } } @@ -514,7 +514,7 @@ public async Task>> GetTodaysSalesCountByHou // An exception has occurred, add some additional information to the message Exception exception = new Exception($"Error getting todays sales count by hour for estate {estateId}.", ex); - throw exception; + return Result.Failure(exception.Message); } } @@ -541,7 +541,7 @@ public async Task>> GetTodaysSalesValueByHou // An exception has occurred, add some additional information to the message Exception exception = new Exception($"Error getting todays sales value by hour for estate {estateId}.", ex); - throw exception; + return Result.Failure(exception.Message); } } @@ -568,7 +568,7 @@ public async Task> GetTodaysSettlement(String accessTok // An exception has occurred, add some additional information to the message Exception exception = new Exception($"Error getting todays settlement for estate {estateId}.", ex); - throw exception; + return Result.Failure(exception.Message); } } @@ -590,7 +590,7 @@ public async Task>> GetTopBottomMerchantData( // An exception has occurred, add some additional information to the message Exception exception = new Exception($"Error getting top/bottom sales by merchant for estate {estateId} TopOrBottom {topBottom} ans count {resultCount}.", ex); - throw exception; + return Result.Failure(exception.Message); } } @@ -612,7 +612,7 @@ public async Task>> GetTopBottomOperatorData( // An exception has occurred, add some additional information to the message Exception exception = new Exception($"Error getting top/bottom sales by operator for estate {estateId} TopOrBottom {topBottom} ans count {resultCount}.", ex); - throw exception; + return Result.Failure(exception.Message); } } @@ -634,7 +634,7 @@ public async Task>> GetTopBottomProductData(St // An exception has occurred, add some additional information to the message Exception exception = new Exception($"Error getting top/bottom sales by product for estate {estateId} TopOrBottom {topBottom} ans count {resultCount}.", ex); - throw exception; + return Result.Failure(exception.Message); } } diff --git a/EstateReportingAPI.DataTrasferObjects/EstateReportingAPI.DataTransferObjects.csproj b/EstateReportingAPI.DataTrasferObjects/EstateReportingAPI.DataTransferObjects.csproj index e01523b..fa0317b 100644 --- a/EstateReportingAPI.DataTrasferObjects/EstateReportingAPI.DataTransferObjects.csproj +++ b/EstateReportingAPI.DataTrasferObjects/EstateReportingAPI.DataTransferObjects.csproj @@ -3,6 +3,7 @@ netstandard2.1 enable + None diff --git a/EstateReportingAPI.IntegrationTests/ControllerTestsBase.cs b/EstateReportingAPI.IntegrationTests/ControllerTestsBase.cs index 5287034..4b86344 100644 --- a/EstateReportingAPI.IntegrationTests/ControllerTestsBase.cs +++ b/EstateReportingAPI.IntegrationTests/ControllerTestsBase.cs @@ -1,4 +1,5 @@ -using SimpleResults; +using EstateManagement.Database.Entities; +using SimpleResults; namespace EstateReportingAPI.IntegrationTests; @@ -16,12 +17,15 @@ namespace EstateReportingAPI.IntegrationTests; using Shared.Logger; using Shouldly; using Xunit; +using Xunit.Abstractions; public abstract class ControllerTestsBase : IAsyncLifetime { - protected List merchantsList; - protected List<(String contract, String operatorname)> contractList; + protected List merchantsList; + protected List<(Guid contractId, String contractName, Guid operatorId, String operatorName)> contractList; + protected Dictionary> contractProducts; protected DatabaseHelper helper; + protected ITestOutputHelper TestOutputHelper; public virtual async Task InitializeAsync() { this.TestId = Guid.NewGuid(); @@ -44,7 +48,7 @@ public virtual async Task InitializeAsync() public virtual async Task DisposeAsync() { } - + protected EstateManagementGenericContext context; protected abstract Task ClearStandingData(); diff --git a/EstateReportingAPI.IntegrationTests/DatabaseHelper.cs b/EstateReportingAPI.IntegrationTests/DatabaseHelper.cs index befbc9e..8cccb81 100644 --- a/EstateReportingAPI.IntegrationTests/DatabaseHelper.cs +++ b/EstateReportingAPI.IntegrationTests/DatabaseHelper.cs @@ -83,7 +83,7 @@ public List GetDatesForYear(Int32 year){ return datesInYear; } - public async Task AddTransaction(DateTime dateTime, + public async Task AddTransactionOld(DateTime dateTime, String merchantName, String contractName, String contractProductName, @@ -169,6 +169,104 @@ public async Task AddTransaction(DateTime dateTime, return transaction; } + public async Task BuildTransactionX(DateTime dateTime, + Guid merchantId, + Guid operatorId, + Guid contractId, + Guid contractProductId, + String responseCode, + Decimal? transactionAmount = null, + Int32? transactionReportingId = null, + String authCode = null) + { + + //var contract = await this.Context.Contracts.SingleOrDefaultAsync(c => c.Description == contractName); + + //if (contract == null) + //{ + // throw new Exception($"No Contact record found with description {contractName}"); + //} + + //var @operator = await this.Context.Operators.SingleOrDefaultAsync(o => o.OperatorId == contract.OperatorId); + + //if (@operator == null) + //{ + // throw new Exception($"No Operator record found with for contract {contractName}"); + //} + + //var contractProduct = await this.Context.ContractProducts.SingleOrDefaultAsync(cp => cp.ContractId == contract.ContractId && + // cp.ProductName == contractProductName); + + //if (contractProduct == null) + //{ + // throw new Exception($"No Contact Product record found with description {contractProductName} for contract {contractName}"); + //} + + //if (contractProduct.Value.HasValue) + //{ + // transactionAmount = contractProduct.Value.GetValueOrDefault(); + //} + //else + //{ + if (transactionAmount.HasValue == false) + { + transactionAmount = 75.00m; + } + //} + + Transaction transaction = new Transaction + { + MerchantId = merchantId, + TransactionDate = dateTime.Date, + TransactionDateTime = dateTime, + IsCompleted = true, + AuthorisationCode = "ABCD1234", + DeviceIdentifier = "testdevice1", + OperatorId = operatorId, + ContractId = contractId, + ContractProductId = contractProductId, + IsAuthorised = responseCode == "0000", + ResponseCode = responseCode, + TransactionAmount = transactionAmount.GetValueOrDefault(), + TransactionId = Guid.NewGuid(), + TransactionTime = dateTime.TimeOfDay, + TransactionType = "Sale", + ResponseMessage = responseCode == "0000" ? "SUCCESS" : "FAILED", + TransactionNumber = "0001", + TransactionReference = "Ref1", + TransactionSource = 1 + }; + + if (transactionReportingId.HasValue) + { + transaction.TransactionReportingId = transactionReportingId.Value; + transaction.TransactionNumber = transactionReportingId.Value.ToString("0000"); + } + + if (String.IsNullOrEmpty(authCode) == false) + { + transaction.AuthorisationCode = authCode; + } + + return transaction; + } + + public async Task AddTransactionsX(List transactions) { + + foreach (Transaction transaction in transactions) { + await this.Context.Transactions.AddAsync(transaction, CancellationToken.None); + } + + if (transactions.Any(t => t.TransactionReportingId> 0)) + { + await this.Context.SaveChangesWithIdentityInsert(); + } + else + { + await this.Context.SaveChangesAsync(CancellationToken.None); + } + } + public async Task AddEstate(String estateName, String reference){ Estate estate = new(){ CreatedDateTime = DateTime.Now, @@ -182,6 +280,30 @@ public async Task AddEstate(String estateName, String reference){ return estate.EstateReportingId; } + public async Task AddOperators(String estateName, List operators) + { + Estate? estate = await this.Context.Estates.SingleOrDefaultAsync(e => e.Name == estateName); + + if (estate == null) + { + throw new Exception($"No estate found with name {estateName}"); + } + + foreach (String operatorName in operators) { + Operator @operator = new Operator { + EstateId = estate.EstateId, + Name = operatorName, + OperatorId = Guid.NewGuid(), + RequireCustomMerchantNumber = false, + RequireCustomTerminalNumber = false + }; + + await this.Context.Operators.AddAsync(@operator); + } + + await this.Context.SaveChangesAsync(CancellationToken.None); + } + public async Task AddOperator(String estateName, String operatorName) { Estate? estate = await this.Context.Estates.SingleOrDefaultAsync(e => e.Name == estateName); @@ -206,7 +328,8 @@ public async Task AddOperator(String estateName, String operatorName) return @operator.OperatorReportingId; } - public async Task AddMerchant(String estateName, String merchantName, DateTime lastSaleDateTime, List<(String addressLine1, String town, String postCode, String region)> addressList = null){ + public async Task AddMerchant(String estateName, String merchantName, DateTime lastSaleDateTime, + List<(String addressLine1, String town, String postCode, String region)> addressList=null){ Estate? estate = await this.Context.Estates.SingleOrDefaultAsync(e => e.Name == estateName); if (estate == null){ @@ -246,6 +369,42 @@ await this.Context.MerchantAddresses.AddAsync(new MerchantAddress{ return merchant.MerchantReportingId; } + public async Task AddMerchants(String estateName, + List<(String merchantName, DateTime lastSaleDateTime, List<(String addressLine1, String town, String postCode, String region)> addressList)> merchants) { + Estate? estate = await this.Context.Estates.SingleOrDefaultAsync(e => e.Name == estateName); + + if (estate == null) { + throw new Exception($"No estate found with name {estateName}"); + } + + foreach (var merchant1 in merchants) { + Merchant merchant = new Merchant { + EstateId = estate.EstateId, + MerchantId = Guid.NewGuid(), + Name = merchant1.merchantName, + LastSaleDate = merchant1.lastSaleDateTime.Date, + LastSaleDateTime = merchant1.lastSaleDateTime + }; + await this.Context.Merchants.AddAsync(merchant); + + if (merchant1.addressList != null) { + foreach ((String addressLine1, String town, String postCode, String region) address in merchant1.addressList) { + await this.Context.MerchantAddresses.AddAsync(new MerchantAddress { + AddressId = Guid.NewGuid(), + AddressLine1 = address.addressLine1, + Region = address.region, + Town = address.town, + PostalCode = address.postCode, + MerchantId = merchant.MerchantId + }); + } + } + + await this.Context.SaveChangesAsync(CancellationToken.None); + + } + } + public async Task AddContract(String estateName, String contractName, String operatorName){ Estate? estate = await this.Context.Estates.SingleOrDefaultAsync(e => e.Name == estateName); @@ -314,38 +473,15 @@ public async Task AddContractProduct(String contractName, String productName, In await this.Context.SaveChangesAsync(CancellationToken.None); } - public async Task AddMerchantSettlementFee(Guid settlementId, DateTime feeCalculatedDateTime, String merchantName, String contractName, String contractProductName, CancellationToken cancellationToken, Boolean isSettled = false){ - var merchant = await this.Context.Merchants.SingleOrDefaultAsync(m => m.Name == merchantName, cancellationToken); + public async Task AddMerchantSettlementFee(Guid settlementId, DateTime feeCalculatedDateTime, Guid merchantId, Guid operatorId, Guid contractId, Guid contractProductId, CancellationToken cancellationToken, Boolean isSettled = false){ + + var productTransactionFee = await this.Context.ContractProductTransactionFees.SingleOrDefaultAsync(f => f.ContractProductId == contractProductId, cancellationToken); - if (merchant == null) - throw new Exception($"Merchant not found with name {merchantName}"); - - var contract = await this.Context.Contracts.SingleOrDefaultAsync(c => c.Description == contractName, cancellationToken); - if (contract== null) - throw new Exception($"Contract not found with name {contractName}"); - - - var contractProduct = await this.Context.ContractProducts.SingleOrDefaultAsync(cp => cp.ProductName == contractProductName && cp.ContractId == contract.ContractId, cancellationToken); - if (contractProduct == null) - throw new Exception($"Contract Product not found with name {contractProductName} on contract {contractName}"); - - var productTransactionFee = await this.Context.ContractProductTransactionFees.SingleOrDefaultAsync(f => f.ContractProductId == contractProduct.ContractProductId, cancellationToken); - - if (productTransactionFee == null){ - throw new Exception($"Fees not found for Product name {contractProductName}"); - } - - var @operator = await this.Context.Operators.SingleOrDefaultAsync(o => o.OperatorId == contract.OperatorId, cancellationToken); - if (@operator == null) - { - throw new Exception($"Operator not found with Id {contract.OperatorId}"); - } - Transaction transaction = new(){ - ContractProductId = contractProduct.ContractProductId, - MerchantId = merchant.MerchantId, - OperatorId = @operator.OperatorId, - ContractId = contract.ContractId, + ContractProductId = contractProductId, + MerchantId = merchantId, + OperatorId = operatorId, + ContractId = contractId, IsAuthorised = true, IsCompleted = true, TransactionAmount = 1.00m, @@ -360,7 +496,7 @@ public async Task AddMerchantSettlementFee(Guid settlementId, DateTime feeCalcul MerchantSettlementFee fee = new(){ FeeCalculatedDateTime = feeCalculatedDateTime, - MerchantId = merchant.MerchantId, + MerchantId = merchantId, CalculatedValue = productTransactionFee.Value, FeeValue = productTransactionFee.Value, IsSettled = isSettled, @@ -372,14 +508,14 @@ public async Task AddMerchantSettlementFee(Guid settlementId, DateTime feeCalcul await this.Context.SaveChangesAsync(cancellationToken); } - public async Task<(Decimal settledTransactions, Decimal pendingSettlementTransactions, Decimal settlementFees, Decimal pendingSettlementFees)> AddSettlementRecord(String merchantName, String operatorName, DateTime settlementDate, Int32 settledTransactionCount, Int32 pendingSettlementTransactionCount){ + public async Task<(Decimal settledTransactions, Decimal pendingSettlementTransactions, Decimal settlementFees, Decimal pendingSettlementFees)> AddSettlementRecord(Guid estateId, Guid merchantId, Guid operatorId, DateTime settlementDate, Int32 settledTransactionCount, Int32 pendingSettlementTransactionCount){ List settledTransactions = new(); List pendingSettlementTransactions = new(); List<(Decimal feeValue, Decimal calulatedValue, Guid contractProductTransactionFeeId, Boolean isSettled, Guid transactionId)> settlementFees = new(); List<(Decimal feeValue, Decimal calulatedValue, Guid contractProductTransactionFeeId, Boolean isSettled, Guid transactionId)> pendingSettlementFees = new(); var feeId = Guid.NewGuid(); - var @operator = await this.Context.Operators.SingleOrDefaultAsync(o => o.Name == operatorName); + //var @operator = await this.Context.Operators.SingleOrDefaultAsync(o => o.Name == operatorName); var contractProducts = await this.Context.ContractProducts.Join( this.Context.Contracts, c => c.ContractId, @@ -397,41 +533,41 @@ public async Task AddMerchantSettlementFee(Guid settlementId, DateTime feeCalcul c.cp, c.c, f - }).Where(x => x.c.OperatorId == @operator.OperatorId).FirstAsync(); + }).Where(x => x.c.OperatorId == operatorId).FirstAsync(); for (int i = 1; i <= settledTransactionCount; i++){ - Transaction transaction = await this.AddTransaction(settlementDate.AddDays(-1), merchantName, contractProducts.c.Description, contractProducts.cp.ProductName, "0000", i); + Transaction transaction = await this.BuildTransactionX(settlementDate.AddDays(-1), merchantId, operatorId, contractProducts.c.ContractId, + contractProducts.cp.ContractProductId,"0000", null); settledTransactions.Add(transaction); settlementFees.Add((0.5m, 0.5m * i, contractProducts.f.ContractProductTransactionFeeId, true, transaction.TransactionId)); } for (int i = 1; i <= pendingSettlementTransactionCount; i++){ - Transaction transaction = await this.AddTransaction(settlementDate.AddDays(-1), merchantName, contractProducts.c.Description, contractProducts.cp.ProductName, "0000", i); + Transaction transaction = await this.BuildTransactionX(settlementDate.AddDays(-1), merchantId, operatorId, contractProducts.c.ContractId, + contractProducts.cp.ContractProductId, "0000", null); pendingSettlementTransactions.Add(transaction); pendingSettlementFees.Add((0.5m, 0.5m * i, contractProducts.f.ContractProductTransactionFeeId, false, transaction.TransactionId)); } - - await this.AddSettlementRecordWithFees(settlementDate, merchantName, pendingSettlementFees, settlementFees); + await this.AddTransactionsX(settledTransactions); + await this.AddTransactionsX(pendingSettlementTransactions); + await this.AddSettlementRecordWithFees(settlementDate, estateId, merchantId, pendingSettlementFees, settlementFees); return (settledTransactions.Sum(s => s.TransactionAmount), pendingSettlementTransactions.Sum(p => p.TransactionAmount), settlementFees.Sum(s => s.calulatedValue), pendingSettlementFees.Sum(p => p.calulatedValue)); } public async Task AddSettlementRecordWithFees(DateTime dateTime, - String merchantName, + Guid estateId, + Guid merchantId, List<(Decimal feeValue, Decimal calulatedValue, Guid contractProductTransactionFeeId, Boolean isSettled, Guid transactionId)> pendingFeesList, List<(Decimal feeValue, Decimal calulatedValue, Guid contractProductTransactionFeeId, Boolean isSettled, Guid transactionId)> settledFeesList){ - var merchant = await this.Context.Merchants.SingleOrDefaultAsync(m => m.Name == merchantName); - if (merchant == null){ - throw new Exception($"No Merchant found with name {merchantName}"); - } Settlement settlementRecord = new Settlement{ ProcessingStarted = true, IsCompleted = pendingFeesList.Count == 0, - EstateId = merchant.EstateId, - MerchantId = merchant.MerchantId, + EstateId = estateId, + MerchantId = merchantId, ProcessingStartedDateTIme = dateTime, SettlementDate = dateTime, SettlementId = Guid.NewGuid(), @@ -442,7 +578,7 @@ public async Task AddSettlementRecordWithFees(DateTime dateTime, foreach ((Decimal feeValue, Decimal calulatedValue, Guid contractProductTransactionFeeId, Boolean isSettled, Guid transactionId) fee in pendingFeesList){ MerchantSettlementFee merchantSettlementFee = new MerchantSettlementFee{ - MerchantId = merchant.MerchantId, + MerchantId = merchantId, SettlementId = settlementRecord.SettlementId, IsSettled = fee.isSettled, FeeCalculatedDateTime = dateTime, @@ -458,7 +594,7 @@ public async Task AddSettlementRecordWithFees(DateTime dateTime, { MerchantSettlementFee merchantSettlementFee = new MerchantSettlementFee { - MerchantId = merchant.MerchantId, + MerchantId = merchantId, SettlementId = settlementRecord.SettlementId, IsSettled = fee.isSettled, FeeCalculatedDateTime = dateTime, @@ -527,6 +663,15 @@ public async Task CreateStoredProcedures(CancellationToken cancellationToken) } } } + + public async Task GetOperatorId(Int32 operatorReportingId, CancellationToken cancellationToken) { + return await this.Context.Operators.Where(o => o.OperatorReportingId == operatorReportingId).Select(o => o.OperatorId).SingleOrDefaultAsync(cancellationToken); + } + + public async Task GetMerchantId(Int32 merchantReportingId, CancellationToken cancellationToken) + { + return await this.Context.Merchants.Where(o => o.MerchantReportingId == merchantReportingId).Select(o => o.MerchantId).SingleOrDefaultAsync(cancellationToken); + } } public static class IdentityHelpers{ diff --git a/EstateReportingAPI.IntegrationTests/DimensionControllerTests.cs b/EstateReportingAPI.IntegrationTests/DimensionControllerTests.cs index 6f196ee..fe6f3b3 100644 --- a/EstateReportingAPI.IntegrationTests/DimensionControllerTests.cs +++ b/EstateReportingAPI.IntegrationTests/DimensionControllerTests.cs @@ -63,6 +63,13 @@ public async Task DimensionsController_GetCalendarComparisonDates_DatesReturned( dates.Select(d => d.Description).Contains("Last Month"); } + [Fact] + public async Task DimensionsController_GetCalendarComparisonDates_NoDataInDatabase() + { + var result= await ApiClient.GetComparisonDates(string.Empty, Guid.NewGuid(), CancellationToken.None); + result.IsFailed.ShouldBeTrue();; + } + [Fact] public async Task DimensionsController_GetCalendarDates_DatesReturned() { diff --git a/EstateReportingAPI.IntegrationTests/EstateReportingAPI.IntegrationTests.csproj b/EstateReportingAPI.IntegrationTests/EstateReportingAPI.IntegrationTests.csproj index 1bec7f3..74a04df 100644 --- a/EstateReportingAPI.IntegrationTests/EstateReportingAPI.IntegrationTests.csproj +++ b/EstateReportingAPI.IntegrationTests/EstateReportingAPI.IntegrationTests.csproj @@ -4,7 +4,7 @@ net8.0 enable enable - Full + None false true diff --git a/EstateReportingAPI.IntegrationTests/FactSettlementsControllerTests.cs b/EstateReportingAPI.IntegrationTests/FactSettlementsControllerTests.cs index 7d775e2..62bf58d 100644 --- a/EstateReportingAPI.IntegrationTests/FactSettlementsControllerTests.cs +++ b/EstateReportingAPI.IntegrationTests/FactSettlementsControllerTests.cs @@ -1,5 +1,4 @@ -namespace EstateReportingAPI.IntegrationTests -{ +namespace EstateReportingAPI.IntegrationTests { using EstateManagement.Database.Contexts; using EstateManagement.Database.Entities; using EstateReportingAPI.DataTransferObjects; @@ -8,20 +7,16 @@ using System.Diagnostics.Contracts; using Xunit; - public class FactSettlementsControllerTests : ControllerTestsBase - { - protected Dictionary<(String Name,Guid contractId, String Description), List> contractProducts; + public class FactSettlementsControllerTests : ControllerTestsBase { - protected override async Task ClearStandingData() - { + protected override async Task ClearStandingData() { await helper.DeleteAllContracts(); await helper.DeleteAllMerchants(); } - protected override async Task SetupStandingData() - { + protected override async Task SetupStandingData() { await helper.AddCalendarYear(2024); - + // Estates await helper.AddEstate("Test Estate", "Ref1"); @@ -30,7 +25,7 @@ protected override async Task SetupStandingData() Int32 voucherReportingId = await this.helper.AddOperator("Test Estate", "Voucher"); Int32 pataPawaPostPayReportingId = await this.helper.AddOperator("Test Estate", "PataPawa PostPay"); Int32 pataPawaPrePay = await this.helper.AddOperator("Test Estate", "PataPawa PrePay"); - + // Merchants await helper.AddMerchant("Test Estate", "Test Merchant 1", DateTime.MinValue); await helper.AddMerchant("Test Estate", "Test Merchant 2", DateTime.MinValue); @@ -38,28 +33,16 @@ protected override async Task SetupStandingData() await helper.AddMerchant("Test Estate", "Test Merchant 4", DateTime.MinValue); // Contracts & Products - List<(string productName, int productType, decimal? value)> safaricomProductList = new(){ - ("200 KES Topup", 0, 200.00m), - ("100 KES Topup", 0, 100.00m), - ("50 KES Topup", 0, 50.00m), - ("Custom", 0, null) - }; + List<(string productName, int productType, decimal? value)> safaricomProductList = new() { ("200 KES Topup", 0, 200.00m), ("100 KES Topup", 0, 100.00m), ("50 KES Topup", 0, 50.00m), ("Custom", 0, null) }; await helper.AddContractWithProducts("Test Estate", "Safaricom Contract", "Safaricom", safaricomProductList); - List<(string productName, int productType, decimal? value)> voucherProductList = new(){ - ("10 KES Voucher", 0, 10.00m), - ("Custom", 0, null) - }; + List<(string productName, int productType, decimal? value)> voucherProductList = new() { ("10 KES Voucher", 0, 10.00m), ("Custom", 0, null) }; await helper.AddContractWithProducts("Test Estate", "Healthcare Centre 1 Contract", "Voucher", voucherProductList); - List<(string productName, int productType, decimal? value)> postPayProductList = new(){ - ("Post Pay Bill Pay", 0, null) - }; + List<(string productName, int productType, decimal? value)> postPayProductList = new() { ("Post Pay Bill Pay", 0, null) }; await helper.AddContractWithProducts("Test Estate", "PataPawa PostPay Contract", "PataPawa PostPay", postPayProductList); - List<(string productName, int productType, decimal? value)> prePayProductList = new(){ - ("Pre Pay Bill Pay", 0, null) - }; + List<(string productName, int productType, decimal? value)> prePayProductList = new() { ("Pre Pay Bill Pay", 0, null) }; await helper.AddContractWithProducts("Test Estate", "PataPawa PrePay Contract", "PataPawa PrePay", prePayProductList); // Response Codes @@ -69,54 +52,19 @@ protected override async Task SetupStandingData() await helper.AddResponseCode(1002, "Unknown Merchant"); await helper.AddResponseCode(1003, "No Devices Configured"); - merchantsList = context.Merchants.Select(m => m.Name).ToList(); - contractList = context.Contracts - .Join( - context.Operators, - c => c.OperatorId, - o => o.OperatorId, - (c, o) => new { c.Description, OperatorName = o.Name } - ) - .ToList().Select(x => (x.Description, x.OperatorName)) - .ToList(); - - - var query1 = context.Contracts - .GroupJoin( - context.ContractProducts, - c => c.ContractId, - cp => cp.ContractId, - (c, productGroup) => new - { - c.OperatorId, - c.ContractId, - c.Description, - Products = productGroup.Select(p => new { p.ContractProductReportingId, p.ProductName }) - .OrderBy(p => p.ContractProductReportingId) - .Select(p => p.ProductName) - .ToList() - }) - .ToList(); - - var query2 = query1.Join(this.context.Operators, - c => c.OperatorId, - o => o.OperatorId, - (c, o) => new{ - o.Name, - c.ContractId, - c.Description, - c.Products - }).ToList(); - - contractProducts = query2.ToDictionary( - item => (item.Name, item.ContractId, item.Description), - item => item.Products - ); + merchantsList = context.Merchants.Select(m => m).ToList(); + + contractList = context.Contracts.Join(context.Operators, c => c.OperatorId, o => o.OperatorId, (c, + o) => new { c.ContractId, c.Description, o.OperatorId, o.Name }).ToList().Select(x => (x.ContractId, x.Description, x.OperatorId, x.Name)).ToList(); + + var query1 = context.Contracts.GroupJoin(context.ContractProducts, c => c.ContractId, cp => cp.ContractId, (c, + productGroup) => new { c.ContractId, Products = productGroup.Select(p => new { p.ContractProductId, p.ProductName, p.Value }).OrderBy(p => p.ContractProductId).Select(p => new { p.ContractProductId, p.ProductName, p.Value }).ToList() }).ToList(); + + contractProducts = query1.ToDictionary(item => item.ContractId, item => item.Products.Select(i => (i.ContractProductId, i.ProductName, i.Value)).ToList()); } - + [Fact] - public async Task FactSettlementsController_TodaysSettlement_SettlementReturned() - { + public async Task FactSettlementsController_TodaysSettlement_SettlementReturned() { int overallTodaysSettlementTransactionCount = 0; int overallTodaysPendingSettlementTransactionCount = 0; @@ -127,19 +75,20 @@ public async Task FactSettlementsController_TodaysSettlement_SettlementReturned( DateTime todaysDate = DateTime.Now; DateTime comparisonDate = DateTime.Now.AddDays(-1); - foreach (string merchant in merchantsList) - { + foreach (var merchant in merchantsList) { int todaysSettlementTransactionCount = 5; int todaysPendingSettlementTransactionCount = 9; - (decimal settledTransactions, decimal pendingSettlementTransactions, decimal settlementFees, decimal pendingSettlementFees) todayTotals = await helper.AddSettlementRecord(merchant, "Safaricom", todaysDate, todaysSettlementTransactionCount, todaysPendingSettlementTransactionCount); + var contract = this.contractList.Single(c => c.operatorName == "Safaricom"); + (decimal settledTransactions, decimal pendingSettlementTransactions, decimal settlementFees, decimal pendingSettlementFees) todayTotals = await helper.AddSettlementRecord(merchant.EstateId, merchant.MerchantId, contract.operatorId, todaysDate, todaysSettlementTransactionCount, todaysPendingSettlementTransactionCount); todayOverallTotals.Add(todayTotals); - overallTodaysSettlementTransactionCount += todaysSettlementTransactionCount; ; + overallTodaysSettlementTransactionCount += todaysSettlementTransactionCount; + ; overallTodaysPendingSettlementTransactionCount += todaysPendingSettlementTransactionCount; int comparisonSettlementTransactionCount = 12; int comparisonPendingSettlementTransactionCount = 15; - var comparisonTotals = await helper.AddSettlementRecord(merchant, "Safaricom",comparisonDate, comparisonSettlementTransactionCount, comparisonPendingSettlementTransactionCount); + var comparisonTotals = await helper.AddSettlementRecord(merchant.EstateId, merchant.MerchantId, contract.operatorId, comparisonDate, comparisonSettlementTransactionCount, comparisonPendingSettlementTransactionCount); comparisonOverallTotals.Add(comparisonTotals); overallComparisonSettlementTransactionCount += comparisonSettlementTransactionCount; @@ -167,41 +116,210 @@ public async Task FactSettlementsController_TodaysSettlement_SettlementReturned( } [Fact] - public async Task FactSettlementsController_LastSettlement_SettlementReturned() + public async Task FactSettlementsController_TodaysSettlement_MerchantFilter_SettlementReturned() { + int overallTodaysSettlementTransactionCount = 0; + int overallTodaysPendingSettlementTransactionCount = 0; + + int overallComparisonSettlementTransactionCount = 0; + int overallComparisonPendingSettlementTransactionCount = 0; + List<(decimal settledTransactions, decimal pendingSettlementTransactions, decimal settlementFees, decimal pendingSettlementFees)> todayOverallTotals = new(); + List<(decimal settledTransactions, decimal pendingSettlementTransactions, decimal settlementFees, decimal pendingSettlementFees)> comparisonOverallTotals = new(); + + DateTime todaysDate = DateTime.Now; + DateTime comparisonDate = DateTime.Now.AddDays(-1); + Dictionary merchantTodaysSettlementFees = new(); + Dictionary merchantTodaysPendingSettlementFees = new(); + Dictionary merchantComparisonSettlementFees = new(); + Dictionary merchantComparisonPendingSettlementFees = new(); + + Dictionary merchantTodaysSettlementTransactionCount = new(); + Dictionary merchantTodaysPendingSettlementTransactionCount = new(); + Dictionary merchantComparisonSettlementTransactionCount = new(); + Dictionary merchantComparisonPendingSettlementTransactionCount = new(); + + foreach (var merchant in merchantsList) + { + merchantTodaysSettlementTransactionCount.Add(merchant.MerchantReportingId, 0); + merchantTodaysPendingSettlementTransactionCount.Add(merchant.MerchantReportingId, 0); + merchantComparisonSettlementTransactionCount.Add(merchant.MerchantReportingId, 0); + merchantComparisonPendingSettlementTransactionCount.Add(merchant.MerchantReportingId, 0); + + merchantTodaysSettlementFees.Add(merchant.MerchantReportingId, 0); + merchantTodaysPendingSettlementFees.Add(merchant.MerchantReportingId, 0); + merchantComparisonSettlementFees.Add(merchant.MerchantReportingId, 0); + merchantComparisonPendingSettlementFees.Add(merchant.MerchantReportingId, 0); + + int todaysSettlementTransactionCount = 5; + int todaysPendingSettlementTransactionCount = 9; + var contract = this.contractList.Single(c => c.operatorName == "Safaricom"); + (decimal settledTransactions, decimal pendingSettlementTransactions, decimal settlementFees, decimal pendingSettlementFees) todayTotals = await helper.AddSettlementRecord(merchant.EstateId, merchant.MerchantId, contract.operatorId, todaysDate, todaysSettlementTransactionCount, todaysPendingSettlementTransactionCount); + todayOverallTotals.Add(todayTotals); + + overallTodaysSettlementTransactionCount += todaysSettlementTransactionCount; + overallTodaysPendingSettlementTransactionCount += todaysPendingSettlementTransactionCount; + + merchantTodaysSettlementTransactionCount[merchant.MerchantReportingId] = todaysSettlementTransactionCount; + merchantTodaysPendingSettlementTransactionCount[merchant.MerchantReportingId] = todaysPendingSettlementTransactionCount; + + merchantTodaysSettlementFees[merchant.MerchantReportingId] = todayTotals.settlementFees; + merchantTodaysPendingSettlementFees[merchant.MerchantReportingId] = todayTotals.pendingSettlementFees; + + int comparisonSettlementTransactionCount = 12; + int comparisonPendingSettlementTransactionCount = 15; + var comparisonTotals = await helper.AddSettlementRecord(merchant.EstateId, merchant.MerchantId, contract.operatorId, comparisonDate, comparisonSettlementTransactionCount, comparisonPendingSettlementTransactionCount); + comparisonOverallTotals.Add(comparisonTotals); + + overallComparisonSettlementTransactionCount += comparisonSettlementTransactionCount; + overallComparisonPendingSettlementTransactionCount += comparisonPendingSettlementTransactionCount; + + merchantComparisonSettlementTransactionCount[merchant.MerchantReportingId] = comparisonSettlementTransactionCount; + merchantComparisonPendingSettlementTransactionCount[merchant.MerchantReportingId] = comparisonPendingSettlementTransactionCount; + + merchantComparisonSettlementFees[merchant.MerchantReportingId] = comparisonTotals.settlementFees; + merchantComparisonPendingSettlementFees[merchant.MerchantReportingId] = comparisonTotals.pendingSettlementFees; + } + + await helper.RunTodaysTransactionsSummaryProcessing(comparisonDate.Date.AddDays(-1)); + await helper.RunHistoricTransactionsSummaryProcessing(comparisonDate.Date.AddDays(-1)); + await helper.RunTodaysTransactionsSummaryProcessing(todaysDate.Date.AddDays(-1)); + await helper.RunSettlementSummaryProcessing(comparisonDate.Date); + + var result = await ApiClient.GetTodaysSettlement(string.Empty, Guid.NewGuid(), 1, 0, DateTime.Now.AddDays(-1), CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + var todaysSettlement = result.Data; + todaysSettlement.ShouldNotBeNull(); + todaysSettlement.ComparisonSettlementCount.ShouldBe(merchantComparisonSettlementTransactionCount[1]); + todaysSettlement.ComparisonSettlementValue.ShouldBe(merchantComparisonSettlementFees[1]); + todaysSettlement.ComparisonPendingSettlementCount.ShouldBe(merchantComparisonPendingSettlementTransactionCount[1]); + todaysSettlement.ComparisonPendingSettlementValue.ShouldBe(merchantComparisonPendingSettlementFees[1]); + + todaysSettlement.TodaysSettlementCount.ShouldBe(merchantTodaysSettlementTransactionCount[1]); + todaysSettlement.TodaysSettlementValue.ShouldBe(merchantTodaysSettlementFees[1]); + todaysSettlement.TodaysPendingSettlementCount.ShouldBe(merchantTodaysPendingSettlementTransactionCount[1]); + todaysSettlement.TodaysPendingSettlementValue.ShouldBe(merchantTodaysPendingSettlementFees[1]); + } + + [Fact] + public async Task FactSettlementsController_TodaysSettlement_OperatorFilter_SettlementReturned() + { + int overallTodaysSettlementTransactionCount = 0; + int overallTodaysPendingSettlementTransactionCount = 0; + + int overallComparisonSettlementTransactionCount = 0; + int overallComparisonPendingSettlementTransactionCount = 0; + List<(decimal settledTransactions, decimal pendingSettlementTransactions, decimal settlementFees, decimal pendingSettlementFees)> todayOverallTotals = new(); + List<(decimal settledTransactions, decimal pendingSettlementTransactions, decimal settlementFees, decimal pendingSettlementFees)> comparisonOverallTotals = new(); + + DateTime todaysDate = DateTime.Now; + DateTime comparisonDate = DateTime.Now.AddDays(-1); + Dictionary operatorTodaysSettlementFees = new(); + Dictionary operatorTodaysPendingSettlementFees = new(); + Dictionary operatorComparisonSettlementFees = new(); + Dictionary operatorComparisonPendingSettlementFees = new(); + + Dictionary operatorTodaysSettlementTransactionCount = new(); + Dictionary operatorTodaysPendingSettlementTransactionCount = new(); + Dictionary operatorComparisonSettlementTransactionCount = new(); + Dictionary operatorComparisonPendingSettlementTransactionCount = new(); + + foreach (var @operator in this.contractList.Select(c => c.operatorName)) { + var operatorRecord = this.context.Operators.Single(o => o.Name == @operator); + operatorTodaysSettlementTransactionCount.Add(operatorRecord.OperatorReportingId, 0); + operatorTodaysPendingSettlementTransactionCount.Add(operatorRecord.OperatorReportingId, 0); + operatorComparisonSettlementTransactionCount.Add(operatorRecord.OperatorReportingId, 0); + operatorComparisonPendingSettlementTransactionCount.Add(operatorRecord.OperatorReportingId, 0); + + operatorTodaysSettlementFees.Add(operatorRecord.OperatorReportingId, 0); + operatorTodaysPendingSettlementFees.Add(operatorRecord.OperatorReportingId, 0); + operatorComparisonSettlementFees.Add(operatorRecord.OperatorReportingId, 0); + operatorComparisonPendingSettlementFees.Add(operatorRecord.OperatorReportingId, 0); + foreach (var merchant in merchantsList) { + + int todaysSettlementTransactionCount = 5; + int todaysPendingSettlementTransactionCount = 9; + var contract = this.contractList.Single(c => c.operatorName == @operator); + (decimal settledTransactions, decimal pendingSettlementTransactions, decimal settlementFees, decimal pendingSettlementFees) todayTotals = await helper.AddSettlementRecord(merchant.EstateId, merchant.MerchantId, contract.operatorId, todaysDate, todaysSettlementTransactionCount, todaysPendingSettlementTransactionCount); + todayOverallTotals.Add(todayTotals); + + overallTodaysSettlementTransactionCount += todaysSettlementTransactionCount; + overallTodaysPendingSettlementTransactionCount += todaysPendingSettlementTransactionCount; + + operatorTodaysSettlementTransactionCount[operatorRecord.OperatorReportingId] += todaysSettlementTransactionCount; + operatorTodaysPendingSettlementTransactionCount[operatorRecord.OperatorReportingId] += todaysPendingSettlementTransactionCount; + + operatorTodaysSettlementFees[operatorRecord.OperatorReportingId] += todayTotals.settlementFees; + operatorTodaysPendingSettlementFees[operatorRecord.OperatorReportingId] += todayTotals.pendingSettlementFees; + + int comparisonSettlementTransactionCount = 12; + int comparisonPendingSettlementTransactionCount = 15; + var comparisonTotals = await helper.AddSettlementRecord(merchant.EstateId, merchant.MerchantId, contract.operatorId, comparisonDate, comparisonSettlementTransactionCount, comparisonPendingSettlementTransactionCount); + comparisonOverallTotals.Add(comparisonTotals); + + overallComparisonSettlementTransactionCount += comparisonSettlementTransactionCount; + overallComparisonPendingSettlementTransactionCount += comparisonPendingSettlementTransactionCount; + + operatorComparisonSettlementTransactionCount[operatorRecord.OperatorReportingId] += comparisonSettlementTransactionCount; + operatorComparisonPendingSettlementTransactionCount[operatorRecord.OperatorReportingId] += comparisonPendingSettlementTransactionCount; + + operatorComparisonSettlementFees[operatorRecord.OperatorReportingId] += comparisonTotals.settlementFees; + operatorComparisonPendingSettlementFees[operatorRecord.OperatorReportingId] += comparisonTotals.pendingSettlementFees; + } + + } + + await helper.RunTodaysTransactionsSummaryProcessing(comparisonDate.Date.AddDays(-1)); + await helper.RunHistoricTransactionsSummaryProcessing(comparisonDate.Date.AddDays(-1)); + await helper.RunTodaysTransactionsSummaryProcessing(todaysDate.Date.AddDays(-1)); + await helper.RunSettlementSummaryProcessing(comparisonDate.Date); + + var result = await ApiClient.GetTodaysSettlement(string.Empty, Guid.NewGuid(), 0, 1, DateTime.Now.AddDays(-1), CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + var todaysSettlement = result.Data; + todaysSettlement.ShouldNotBeNull(); + todaysSettlement.ComparisonSettlementCount.ShouldBe(operatorComparisonSettlementTransactionCount[1]); + todaysSettlement.ComparisonSettlementValue.ShouldBe(operatorComparisonSettlementFees[1]); + todaysSettlement.ComparisonPendingSettlementCount.ShouldBe(operatorComparisonPendingSettlementTransactionCount[1]); + todaysSettlement.ComparisonPendingSettlementValue.ShouldBe(operatorComparisonPendingSettlementFees[1]); + + todaysSettlement.TodaysSettlementCount.ShouldBe(operatorTodaysSettlementTransactionCount[1]); + todaysSettlement.TodaysSettlementValue.ShouldBe(operatorTodaysSettlementFees[1]); + todaysSettlement.TodaysPendingSettlementCount.ShouldBe(operatorTodaysPendingSettlementTransactionCount[1]); + todaysSettlement.TodaysPendingSettlementValue.ShouldBe(operatorTodaysPendingSettlementFees[1]); + } + + [Fact] + public async Task FactSettlementsController_LastSettlement_SettlementReturned() { EstateManagementGenericContext context = new EstateManagementSqlServerContext(GetLocalConnectionString($"EstateReportingReadModel{TestId.ToString()}")); DatabaseHelper helper = new DatabaseHelper(context); - List<(decimal settledTransactions, decimal pendingSettlementTransactions, decimal settlementFees, decimal - pendingSettlementFees)> incompleteTotalsList = new(); - List<(decimal settledTransactions, decimal pendingSettlementTransactions, decimal settlementFees, decimal - pendingSettlementFees)> completeTotalsList = new(); - + List<(decimal settledTransactions, decimal pendingSettlementTransactions, decimal settlementFees, decimal pendingSettlementFees)> incompleteTotalsList = new(); + List<(decimal settledTransactions, decimal pendingSettlementTransactions, decimal settlementFees, decimal pendingSettlementFees)> completeTotalsList = new(); + // Add todays settlement (incomplete) Int32 totalSettledTransactionCount = 0; Int32 totalPendingSettlementTransactionCount = 21; - foreach (string merchant in merchantsList) - { + var contract = this.contractList.Single(c => c.operatorName == "Safaricom"); + foreach (Merchant merchant in merchantsList) { Int32 settledTransactionCount = 0; totalSettledTransactionCount += settledTransactionCount; int pendingSettlementTransactionCount = 21; totalPendingSettlementTransactionCount += pendingSettlementTransactionCount; - var incompleteTotals = await helper.AddSettlementRecord(merchant, "Safaricom", DateTime.Now, settledTransactionCount, pendingSettlementTransactionCount); + var incompleteTotals = await helper.AddSettlementRecord(merchant.EstateId, merchant.MerchantId, contract.operatorId, DateTime.Now, settledTransactionCount, pendingSettlementTransactionCount); incompleteTotalsList.Add(incompleteTotals); } // Add yesterdays settlement (complete) - foreach (string merchant in merchantsList) - { + foreach (Merchant merchant in merchantsList) { Int32 settledTransactionCount = 18; totalSettledTransactionCount += settledTransactionCount; int pendingSettlementTransactionCount = 0; totalPendingSettlementTransactionCount += pendingSettlementTransactionCount; - var completeTotals = await helper.AddSettlementRecord(merchant, "Safaricom", DateTime.Now.AddDays(-1), settledTransactionCount, pendingSettlementTransactionCount); + var completeTotals = await helper.AddSettlementRecord(merchant.EstateId, merchant.MerchantId, contract.operatorId, DateTime.Now.AddDays(-1), settledTransactionCount, pendingSettlementTransactionCount); completeTotalsList.Add(completeTotals); } - + await helper.RunSettlementSummaryProcessing(DateTime.Now.AddDays(-1)); var result = await ApiClient.GetLastSettlement(string.Empty, Guid.NewGuid(), CancellationToken.None); @@ -215,12 +333,11 @@ public async Task FactSettlementsController_LastSettlement_SettlementReturned() } [Fact] - public async Task FactSettlementsController_LastSettlement_NoSettlementRecords_SettlementReturned() - { + public async Task FactSettlementsController_LastSettlement_NoSettlementRecords_SettlementReturned() { EstateManagementGenericContext context = new EstateManagementSqlServerContext(GetLocalConnectionString($"EstateReportingReadModel{TestId.ToString()}")); DatabaseHelper helper = new DatabaseHelper(context); - + await helper.RunSettlementSummaryProcessing(DateTime.Now.AddDays(-1)); var result = await ApiClient.GetLastSettlement(string.Empty, Guid.NewGuid(), CancellationToken.None); @@ -228,8 +345,8 @@ public async Task FactSettlementsController_LastSettlement_NoSettlementRecords_S } - [Fact(Skip = "To be fixed")] - public async Task FactSettlementsController_UnsettledFees_ByOperator_SettlementReturned(){ + [Fact] + public async Task FactSettlementsController_UnsettledFees_ByOperator_SettlementReturned() { // Add some fees over a date range for multiple operators EstateManagementGenericContext context = new EstateManagementSqlServerContext(GetLocalConnectionString($"EstateReportingReadModel{TestId.ToString()}")); @@ -241,16 +358,15 @@ public async Task FactSettlementsController_UnsettledFees_ByOperator_SettlementR dates.Add(new DateTime(2024, 5, 26)); dates.Add(new DateTime(2024, 5, 27)); - - foreach (DateTime dateTime in dates) - { + + foreach (DateTime dateTime in dates) { Guid settlementId = Guid.NewGuid(); - foreach (String merchant in this.merchantsList){ - foreach ((String contract, String operatorname) contract in this.contractList){ - var products = this.contractProducts.Single(cp => cp.Key.Description == contract.contract); + foreach (Merchant merchant in this.merchantsList) { + foreach ((Guid contractId, String contractName, Guid operatorId, String operatorName) contract in this.contractList) { + var products = this.contractProducts.Single(cp => cp.Key == contract.contractId); - foreach (var product in products.Value){ - await helper.AddMerchantSettlementFee(settlementId, dateTime, merchant, contract.contract, product, CancellationToken.None); + foreach (var product in products.Value) { + await helper.AddMerchantSettlementFee(settlementId, dateTime, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, CancellationToken.None); } } } @@ -259,31 +375,30 @@ public async Task FactSettlementsController_UnsettledFees_ByOperator_SettlementR DateTime startDate = dates.Min(); DateTime endDate = dates.Max(); - var result = await ApiClient.GetUnsettledFees(string.Empty, Guid.NewGuid(), startDate, endDate, null, null, null, GroupByOption.Merchant, CancellationToken.None); + var result = await ApiClient.GetUnsettledFees(string.Empty, Guid.NewGuid(), startDate, endDate, null, null, null, GroupByOption.Operator, CancellationToken.None); result.IsSuccess.ShouldBeTrue(); var unsettledFees = result.Data; unsettledFees.ShouldNotBeNull(); unsettledFees.ShouldNotBeEmpty(); unsettledFees.Count.ShouldBe(this.contractList.Count); - foreach ((String contract, String operatorname) contract in this.contractList){ - var c = await this.context.Contracts.SingleOrDefaultAsync(c => c.Description == contract.contract, CancellationToken.None); - var cps = await this.context.ContractProducts.Where(cp => cp.ContractId== c.ContractId).Select(cp => cp.ContractProductId).ToListAsync(CancellationToken.None); + foreach ((Guid contractId, String contractName, Guid operatorId, String operatorName) contract in this.contractList) { + var c = await this.context.Contracts.SingleOrDefaultAsync(c => c.ContractId == contract.contractId, CancellationToken.None); + var cps = await this.context.ContractProducts.Where(cp => cp.ContractId == c.ContractId).Select(cp => cp.ContractProductId).ToListAsync(CancellationToken.None); var tf = await context.ContractProductTransactionFees.Where(cptf => cps.Contains(cptf.ContractProductId)).Select(t => t.ContractProductTransactionFeeId).ToListAsync(CancellationToken.None); var expectedFees = this.context.MerchantSettlementFees.Where(f => tf.Contains(f.ContractProductTransactionFeeId)); - var u = unsettledFees.SingleOrDefault(u => u.DimensionName == contract.operatorname); + var u = unsettledFees.SingleOrDefault(u => u.DimensionName == contract.operatorName); u.ShouldNotBeNull(); u.FeesCount.ShouldBe(await expectedFees.CountAsync(CancellationToken.None)); - u.FeesValue.ShouldBe(await expectedFees.SumAsync(f=> f.CalculatedValue,CancellationToken.None)); + u.FeesValue.ShouldBe(await expectedFees.SumAsync(f => f.CalculatedValue, CancellationToken.None)); } } - [Fact(Skip = "To be fixed")] - public async Task FactSettlementsController_UnsettledFees_ByMerchant_SettlementReturned() - { + [Fact] + public async Task FactSettlementsController_UnsettledFees_ByOperator_OperatorFilter_SettlementReturned() { // Add some fees over a date range for multiple operators EstateManagementGenericContext context = new EstateManagementSqlServerContext(GetLocalConnectionString($"EstateReportingReadModel{TestId.ToString()}")); @@ -295,18 +410,65 @@ public async Task FactSettlementsController_UnsettledFees_ByMerchant_SettlementR dates.Add(new DateTime(2024, 5, 26)); dates.Add(new DateTime(2024, 5, 27)); - foreach (DateTime dateTime in dates) - { + + foreach (DateTime dateTime in dates) { Guid settlementId = Guid.NewGuid(); - foreach (String merchant in this.merchantsList) - { - foreach ((String contract, String operatorname) contract in this.contractList) - { - var products = this.contractProducts.Single(cp => cp.Key.Description == contract.contract); + foreach (Merchant merchant in this.merchantsList) { + foreach ((Guid contractId, String contractName, Guid operatorId, String operatorName) contract in this.contractList) { + var products = this.contractProducts.Single(cp => cp.Key == contract.contractId); - foreach (var product in products.Value) - { - await helper.AddMerchantSettlementFee(settlementId, dateTime, merchant, contract.contract, product, CancellationToken.None); + foreach (var product in products.Value) { + await helper.AddMerchantSettlementFee(settlementId, dateTime, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, CancellationToken.None); + } + } + } + } + + DateTime startDate = dates.Min(); + DateTime endDate = dates.Max(); + + var result = await ApiClient.GetUnsettledFees(string.Empty, Guid.NewGuid(), startDate, endDate, null, [1], null, GroupByOption.Operator, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + var unsettledFees = result.Data; + + unsettledFees.ShouldNotBeNull(); + unsettledFees.ShouldNotBeEmpty(); + + var @operator = await this.context.Operators.SingleOrDefaultAsync(o => o.OperatorReportingId == 1, CancellationToken.None); + var c = await this.context.Contracts.SingleOrDefaultAsync(c => c.OperatorId == @operator.OperatorId, CancellationToken.None); + var cps = await this.context.ContractProducts.Where(cp => cp.ContractId == c.ContractId).Select(cp => cp.ContractProductId).ToListAsync(CancellationToken.None); + var tf = await context.ContractProductTransactionFees.Where(cptf => cps.Contains(cptf.ContractProductId)).Select(t => t.ContractProductTransactionFeeId).ToListAsync(CancellationToken.None); + + var expectedFees = this.context.MerchantSettlementFees.Where(f => tf.Contains(f.ContractProductTransactionFeeId)); + + var u = unsettledFees.SingleOrDefault(u => u.DimensionName == @operator.Name); + + unsettledFees.Sum(d => d.FeesCount).ShouldBe(await expectedFees.CountAsync(CancellationToken.None)); + unsettledFees.Sum(d => d.FeesValue).ShouldBe(await expectedFees.SumAsync(f => f.CalculatedValue, CancellationToken.None)); + + } + + [Fact] + public async Task FactSettlementsController_UnsettledFees_ByMerchant_SettlementReturned() { + // Add some fees over a date range for multiple operators + EstateManagementGenericContext context = new EstateManagementSqlServerContext(GetLocalConnectionString($"EstateReportingReadModel{TestId.ToString()}")); + + DatabaseHelper helper = new DatabaseHelper(context); + + List dates = new(); + dates.Add(new DateTime(2024, 5, 24)); + dates.Add(new DateTime(2024, 5, 25)); + dates.Add(new DateTime(2024, 5, 26)); + dates.Add(new DateTime(2024, 5, 27)); + + foreach (DateTime dateTime in dates) { + Guid settlementId = Guid.NewGuid(); + foreach (Merchant merchant in this.merchantsList) { + foreach ((Guid contractId, String contractName, Guid operatorId, String operatorName) contract in this.contractList) { + var products = this.contractProducts.Single(cp => cp.Key == contract.contractId); + + foreach (var product in products.Value) { + await helper.AddMerchantSettlementFee(settlementId, dateTime, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, CancellationToken.None); } } } @@ -321,12 +483,10 @@ public async Task FactSettlementsController_UnsettledFees_ByMerchant_SettlementR unsettledFees.ShouldNotBeNull(); unsettledFees.ShouldNotBeEmpty(); unsettledFees.Count.ShouldBe(this.merchantsList.Count); - foreach (var merchantName in this.merchantsList) - { - var merchant = await this.context.Merchants.SingleOrDefaultAsync(me => me.Name == merchantName); + foreach (var merchant in this.merchantsList) { var expectedFees = this.context.MerchantSettlementFees.Where(f => f.MerchantId == merchant.MerchantId); - - var u = unsettledFees.SingleOrDefault(u => u.DimensionName == merchantName); + + var u = unsettledFees.SingleOrDefault(u => u.DimensionName == merchant.Name); u.ShouldNotBeNull(); u.FeesCount.ShouldBe(await expectedFees.CountAsync(CancellationToken.None)); @@ -334,9 +494,8 @@ public async Task FactSettlementsController_UnsettledFees_ByMerchant_SettlementR } } - [Fact(Skip = "To be fixed")] - public async Task FactSettlementsController_UnsettledFees_ByProduct_SettlementReturned() - { + [Fact] + public async Task FactSettlementsController_UnsettledFees_ByMerchant_MerchantFilter_SettlementReturned() { // Add some fees over a date range for multiple operators EstateManagementGenericContext context = new EstateManagementSqlServerContext(GetLocalConnectionString($"EstateReportingReadModel{TestId.ToString()}")); @@ -348,18 +507,14 @@ public async Task FactSettlementsController_UnsettledFees_ByProduct_SettlementRe dates.Add(new DateTime(2024, 5, 26)); dates.Add(new DateTime(2024, 5, 27)); - foreach (DateTime dateTime in dates) - { + foreach (DateTime dateTime in dates) { Guid settlementId = Guid.NewGuid(); - foreach (String merchant in this.merchantsList) - { - foreach ((String contract, String operatorname) contract in this.contractList) - { - var products = this.contractProducts.Single(cp => cp.Key.Description == contract.contract); + foreach (Merchant merchant in this.merchantsList) { + foreach ((Guid contractId, String contractName, Guid operatorId, String operatorName) contract in this.contractList) { + var products = this.contractProducts.Single(cp => cp.Key == contract.contractId); - foreach (var product in products.Value) - { - await helper.AddMerchantSettlementFee(settlementId, dateTime, merchant, contract.contract, product, CancellationToken.None); + foreach (var product in products.Value) { + await helper.AddMerchantSettlementFee(settlementId, dateTime, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, CancellationToken.None); } } } @@ -368,22 +523,70 @@ public async Task FactSettlementsController_UnsettledFees_ByProduct_SettlementRe DateTime startDate = dates.Min(); DateTime endDate = dates.Max(); - var result = await ApiClient.GetUnsettledFees(string.Empty, Guid.NewGuid(), startDate, endDate, null, null, null, GroupByOption.Merchant, CancellationToken.None); + var result = await ApiClient.GetUnsettledFees(string.Empty, Guid.NewGuid(), startDate, endDate, [1], null, null, GroupByOption.Merchant, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + var unsettledFees = result.Data; + unsettledFees.ShouldNotBeNull(); + unsettledFees.ShouldNotBeEmpty(); + + var merchantRecord = await this.context.Merchants.SingleOrDefaultAsync(me => me.MerchantReportingId == 1); + var expectedFees = this.context.MerchantSettlementFees.Where(f => f.MerchantId == merchantRecord.MerchantId); + + var u = unsettledFees.SingleOrDefault(u => u.DimensionName == merchantRecord.Name); + + u.ShouldNotBeNull(); + u.FeesCount.ShouldBe(await expectedFees.CountAsync(CancellationToken.None)); + u.FeesValue.ShouldBe(await expectedFees.SumAsync(f => f.CalculatedValue, CancellationToken.None)); + } + + [Fact] + public async Task FactSettlementsController_UnsettledFees_ByProduct_SettlementReturned() { + // Add some fees over a date range for multiple operators + EstateManagementGenericContext context = new EstateManagementSqlServerContext(GetLocalConnectionString($"EstateReportingReadModel{TestId.ToString()}")); + + DatabaseHelper helper = new DatabaseHelper(context); + + List dates = new(); + dates.Add(new DateTime(2024, 5, 24)); + dates.Add(new DateTime(2024, 5, 25)); + dates.Add(new DateTime(2024, 5, 26)); + dates.Add(new DateTime(2024, 5, 27)); + + foreach (DateTime dateTime in dates) { + Guid settlementId = Guid.NewGuid(); + foreach (Merchant merchant in this.merchantsList) { + foreach ((Guid contractId, String contractName, Guid operatorId, String operatorName) contract in this.contractList) { + var products = this.contractProducts.Single(cp => cp.Key == contract.contractId); + + foreach (var product in products.Value) { + await helper.AddMerchantSettlementFee(settlementId, dateTime, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, CancellationToken.None); + } + } + } + } + + DateTime startDate = dates.Min(); + DateTime endDate = dates.Max(); + + var result = await ApiClient.GetUnsettledFees(string.Empty, Guid.NewGuid(), startDate, endDate, null, null, null, GroupByOption.Product, CancellationToken.None); result.IsSuccess.ShouldBeTrue(); var unsettledFees = result.Data; unsettledFees.ShouldNotBeNull(); unsettledFees.ShouldNotBeEmpty(); - - List<(String,Guid,String)> allProducts = new(); - foreach (var contractProduct in this.contractProducts){ - foreach (String s in contractProduct.Value){ - allProducts.Add((contractProduct.Key.Name, contractProduct.Key.contractId,s)); + + List<(String, Guid, String)> allProducts = new(); + foreach (KeyValuePair> contractProduct in this.contractProducts) { + var contract = this.contractList.Single(c => c.contractId == contractProduct.Key); + var @operator = await this.context.Operators.SingleOrDefaultAsync(o => o.OperatorId == contract.operatorId, CancellationToken.None); + + foreach ((Guid productId, String productName, Decimal? productValue) s in contractProduct.Value) { + allProducts.Add((@operator.Name, contractProduct.Key, s.productName)); } } + unsettledFees.Count.ShouldBe(allProducts.Distinct().Count()); - foreach (var contractProduct in allProducts.Distinct()) - { + foreach (var contractProduct in allProducts.Distinct()) { var product = await this.context.ContractProducts.Where(cp => cp.ProductName == contractProduct.Item3 && cp.ContractId == contractProduct.Item2).SingleOrDefaultAsync(CancellationToken.None); var tf = await context.ContractProductTransactionFees.Where(cptf => cptf.ContractProductId == product.ContractProductId).ToListAsync(CancellationToken.None); var expectedFees = this.context.MerchantSettlementFees.Where(f => tf.Select(t => t.ContractProductTransactionFeeId).Contains(f.ContractProductTransactionFeeId)); @@ -395,6 +598,56 @@ public async Task FactSettlementsController_UnsettledFees_ByProduct_SettlementRe u.FeesValue.ShouldBe(await expectedFees.SumAsync(f => f.CalculatedValue, CancellationToken.None)); } } + + [Fact] + public async Task FactSettlementsController_UnsettledFees_ByProduct_ProductFilter_SettlementReturned() { + // Add some fees over a date range for multiple operators + EstateManagementGenericContext context = new EstateManagementSqlServerContext(GetLocalConnectionString($"EstateReportingReadModel{TestId.ToString()}")); + + DatabaseHelper helper = new DatabaseHelper(context); + + List dates = new(); + dates.Add(new DateTime(2024, 5, 24)); + dates.Add(new DateTime(2024, 5, 25)); + dates.Add(new DateTime(2024, 5, 26)); + dates.Add(new DateTime(2024, 5, 27)); + + foreach (DateTime dateTime in dates) { + Guid settlementId = Guid.NewGuid(); + foreach (Merchant merchant in this.merchantsList) { + foreach ((Guid contractId, String contractName, Guid operatorId, String operatorName) contract in this.contractList) { + var products = this.contractProducts.Single(cp => cp.Key == contract.contractId); + + foreach (var product in products.Value) { + await helper.AddMerchantSettlementFee(settlementId, dateTime, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, CancellationToken.None); + } + } + } + } + + DateTime startDate = dates.Min(); + DateTime endDate = dates.Max(); + + var result = await ApiClient.GetUnsettledFees(string.Empty, Guid.NewGuid(), startDate, endDate, null, null, [1], GroupByOption.Product, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + var unsettledFees = result.Data; + + unsettledFees.ShouldNotBeNull(); + unsettledFees.ShouldNotBeEmpty(); + + var contractProduct = await this.context.ContractProducts.Where(cp => cp.ContractProductReportingId == 1).SingleOrDefaultAsync(CancellationToken.None); + var contractRecord = this.contractList.Single(c => c.contractId == contractProduct.ContractId); + var @operator = await this.context.Operators.SingleOrDefaultAsync(o => o.OperatorId == contractRecord.operatorId, CancellationToken.None); + var tf = await context.ContractProductTransactionFees.Where(cptf => cptf.ContractProductId == contractProduct.ContractProductId).ToListAsync(CancellationToken.None); + var expectedFees = this.context.MerchantSettlementFees.Where(f => tf.Select(t => t.ContractProductTransactionFeeId).Contains(f.ContractProductTransactionFeeId)); + + var u = unsettledFees.SingleOrDefault(u => u.DimensionName == $"{@operator.Name} - {contractProduct.ProductName}"); + + u.ShouldNotBeNull($"{@operator.Name} - {contractProduct.ProductName}"); + u.FeesCount.ShouldBe(await expectedFees.CountAsync(CancellationToken.None)); + u.FeesValue.ShouldBe(await expectedFees.SumAsync(f => f.CalculatedValue, CancellationToken.None)); + + } } } diff --git a/EstateReportingAPI.IntegrationTests/FactTransactionsControllerTests.cs b/EstateReportingAPI.IntegrationTests/FactTransactionsControllerTests.cs index c12be64..cbf0d07 100644 --- a/EstateReportingAPI.IntegrationTests/FactTransactionsControllerTests.cs +++ b/EstateReportingAPI.IntegrationTests/FactTransactionsControllerTests.cs @@ -1,13 +1,19 @@ -using Ductus.FluentDocker.Common; +using System.Diagnostics; +using Ductus.FluentDocker.Common; +using EstateReportingAPI.IntegrationTests; using Microsoft.EntityFrameworkCore; +using Xunit.Abstractions; namespace EstateReportingAPI.IntegrationTests; using System.Collections.Generic; +using System.Diagnostics.Contracts; +using System.Linq; using DataTrasferObjects; using EstateManagement.Database.Contexts; using EstateManagement.Database.Entities; using EstateReportingAPI.DataTransferObjects; +using EstateReportingAPI.Models; using Microsoft.OpenApi.Services; using Newtonsoft.Json; using Shouldly; @@ -15,9 +21,8 @@ namespace EstateReportingAPI.IntegrationTests; using Merchant = DataTrasferObjects.Merchant; using SortDirection = DataTransferObjects.SortDirection; -public class FactTransactionsControllerTests : ControllerTestsBase -{ - protected Dictionary> contractProducts; +public class FactTransactionsControllerTestsBase : ControllerTestsBase { + protected override async Task ClearStandingData() { await helper.DeleteAllContracts(); @@ -26,20 +31,31 @@ protected override async Task ClearStandingData() protected override async Task SetupStandingData() { + Stopwatch sw = Stopwatch.StartNew(); + this.TestOutputHelper.WriteLine("Setting up standing data"); + // Estates await helper.AddEstate("Test Estate", "Ref1"); - + sw.Stop(); + this.TestOutputHelper.WriteLine($"Setup Estate {sw.ElapsedMilliseconds}ms"); + sw.Restart(); // Operators Int32 safaricomReportingId = await this.helper.AddOperator("Test Estate", "Safaricom"); Int32 voucherReportingId = await this.helper.AddOperator("Test Estate", "Voucher"); Int32 pataPawaPostPayReportingId = await this.helper.AddOperator("Test Estate", "PataPawa PostPay"); Int32 pataPawaPrePay = await this.helper.AddOperator("Test Estate", "PataPawa PrePay"); - + + sw.Stop(); + this.TestOutputHelper.WriteLine($"Setup Operators {sw.ElapsedMilliseconds}ms"); + sw.Restart(); // Merchants await helper.AddMerchant("Test Estate", "Test Merchant 1", DateTime.MinValue); await helper.AddMerchant("Test Estate", "Test Merchant 2", DateTime.MinValue); await helper.AddMerchant("Test Estate", "Test Merchant 3", DateTime.MinValue); await helper.AddMerchant("Test Estate", "Test Merchant 4", DateTime.MinValue); + sw.Stop(); + this.TestOutputHelper.WriteLine($"Setup Merchants {sw.ElapsedMilliseconds}ms"); + sw.Restart(); // Contracts & Products List<(string productName, int productType, decimal? value)> safaricomProductList = new(){ @@ -66,6 +82,10 @@ protected override async Task SetupStandingData() }; await helper.AddContractWithProducts("Test Estate", "PataPawa PrePay Contract", "PataPawa PrePay", prePayProductList); + sw.Stop(); + this.TestOutputHelper.WriteLine($"Setup Contracts {sw.ElapsedMilliseconds}ms"); + sw.Restart(); + // Response Codes await helper.AddResponseCode(0, "Success"); await helper.AddResponseCode(1000, "Unknown Device"); @@ -73,337 +93,327 @@ protected override async Task SetupStandingData() await helper.AddResponseCode(1002, "Unknown Merchant"); await helper.AddResponseCode(1003, "No Devices Configured"); - merchantsList = context.Merchants.Select(m => m.Name).ToList(); - contractList = context.Contracts - .Join( - context.Operators, - c => c.OperatorId, - o => o.OperatorId, - (c, o) => new { c.Description, OperatorName = o.Name } - ) - .ToList().Select(x => (x.Description, x.OperatorName)) - .ToList(); + sw.Stop(); + this.TestOutputHelper.WriteLine($"Setup Response Codes {sw.ElapsedMilliseconds}ms"); + sw.Restart(); + merchantsList = context.Merchants.Select(m => m).ToList(); + contractList = context.Contracts + .Join( + context.Operators, + c => c.OperatorId, + o => o.OperatorId, + (c, o) => new { c.ContractId, c.Description, o.OperatorId, o.Name} + ) + .ToList().Select(x => (x.ContractId, x.Description, x.OperatorId,x.Name)) + .ToList(); + var query1 = context.Contracts - .GroupJoin( - context.ContractProducts, - c => c.ContractId, - cp => cp.ContractId, - (c, productGroup) => new - { - c.Description, - Products = productGroup.Select(p => new { p.ContractProductReportingId, p.ProductName }) - .OrderBy(p => p.ContractProductReportingId) - .Select(p => p.ProductName) - .ToList() - }) - .ToList(); + .GroupJoin( + context.ContractProducts, + c => c.ContractId, + cp => cp.ContractId, + (c, productGroup) => new + { + c.ContractId, + Products = productGroup.Select(p => new { p.ContractProductId, p.ProductName, p.Value}) + .OrderBy(p => p.ContractProductId) + .Select(p => new {p.ContractProductId,p.ProductName, p.Value}) + .ToList() + }) + .ToList(); contractProducts = query1.ToDictionary( - item => item.Description, - item => item.Products - ); + item => item.ContractId, + item => item.Products.Select(i => (i.ContractProductId, i.ProductName, i.Value)).ToList() + ); + + + sw.Stop(); + this.TestOutputHelper.WriteLine($"Data Caching {sw.ElapsedMilliseconds}ms"); + sw.Restart(); } - #region Todays Sales Tests + public FactTransactionsControllerTestsBase(ITestOutputHelper testOutputHelper) { + this.TestOutputHelper = testOutputHelper; + } +} - [Fact] - public async Task FactTransactionsControllerController_TodaysSales_SalesReturned() +public class FactTransactionsControllerTests_OperatorsTests : FactTransactionsControllerTestsBase { + public FactTransactionsControllerTests_OperatorsTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) { - List? todaysTransactions = new List(); - List comparisonDateTransactions = new List(); + + } + [Fact] + public async Task FactTransactionsController_GetTopBottomOperatorsByValue_BottomOperators_OperatorsReturned() { DateTime todaysDateTime = DateTime.Now; - DateTime comparisonDate = DateTime.Now.AddDays(-1).AddHours(-1); - Dictionary transactionCounts = new(){ - { "Test Merchant 1", 15 }, - { "Test Merchant 2", 18 }, - { "Test Merchant 3", 9 }, - { "Test Merchant 4", 0 } - }; + Dictionary transactionCounts = new() { + { "Safaricom", 25 }, // 5000 + { "Voucher", 15 }, // 150 + { "PataPawa PostPay", 45 }, // 3375 + { "PataPawa PrePay", 8 } // 600 + }; - // Todays sales - foreach (string merchantName in merchantsList) - { - foreach ((string contract, string operatorname) contract in contractList) - { - var productList = contractProducts.Single(cp => cp.Key == contract.contract).Value; - foreach (string product in productList) - { - var transactionCount = transactionCounts.Single(m => m.Key == merchantName).Value; - for (int i = 0; i < transactionCount; i++) - { - Transaction transaction = await helper.AddTransaction(todaysDateTime.AddHours(-1), merchantName, contract.contract, product, "0000"); - todaysTransactions.Add(transaction); - } + Dictionary> transactionsDictionary = new(); + var merchant = merchantsList.First(); + foreach (KeyValuePair transactionCount in transactionCounts) { + var contract = contractList.Single(s => s.operatorName == transactionCount.Key); + var products = contractProducts.Single(p => p.Key == contract.contractId); + var product = products.Value.OrderByDescending(p => p.productValue.GetValueOrDefault()).First(); + for (int i = 0; i < transactionCount.Value; i++) { + Transaction transaction = await helper.BuildTransactionX(todaysDateTime.AddHours(-1), merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", product.productValue); + if (transactionsDictionary.ContainsKey(transactionCount.Key) == false) { + transactionsDictionary.Add(transactionCount.Key, new List()); } + + transactionsDictionary[transactionCount.Key].Add(transaction); } } - // Comparison Date sales - foreach (string merchantName in merchantsList) + await this.helper.AddTransactionsX(transactionsDictionary.Values.SelectMany(t=> t).ToList()); + + await helper.RunTodaysTransactionsSummaryProcessing(todaysDateTime.Date); + + var result = await ApiClient.GetTopBottomOperatorData(string.Empty, Guid.NewGuid(), DataTransferObjects.TopBottom.Bottom, 3, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + List? topBottomOperatorData = result.Data; + topBottomOperatorData.ShouldNotBeNull(); + topBottomOperatorData[0].OperatorName.ShouldBe("Voucher"); + topBottomOperatorData[0].SalesValue.ShouldBe(transactionsDictionary["Voucher"].Sum(p => p.TransactionAmount)); + topBottomOperatorData[1].OperatorName.ShouldBe("PataPawa PrePay"); + topBottomOperatorData[1].SalesValue.ShouldBe(transactionsDictionary["PataPawa PrePay"].Sum(p => p.TransactionAmount)); + topBottomOperatorData[2].OperatorName.ShouldBe("PataPawa PostPay"); + topBottomOperatorData[2].SalesValue.ShouldBe(transactionsDictionary["PataPawa PostPay"].Sum(p => p.TransactionAmount)); + } + + + [Fact] + public async Task FactTransactionsController_GetTopBottomOperatorsByValue_TopOperators_OperatorsReturned() { + DateTime todaysDateTime = DateTime.Now; + + Dictionary transactionCounts = new() { + { "Safaricom", 25 }, // 5000 + { "Voucher", 15 }, // 150 + { "PataPawa PostPay", 45 }, // 3375 + { "PataPawa PrePay", 8 } // 600 + }; + + Dictionary> transactionsDictionary = new(); + var merchant = merchantsList.First(); + foreach (KeyValuePair transactionCount in transactionCounts) { - foreach ((string contract, string operatorname) contract in contractList) + var contract = contractList.Single(s => s.operatorName == transactionCount.Key); + var products = contractProducts.Single(p => p.Key == contract.contractId); + var product = products.Value.OrderByDescending(p => p.productValue.GetValueOrDefault()).First(); + for (int i = 0; i < transactionCount.Value; i++) { - var productList = contractProducts.Single(cp => cp.Key == contract.contract).Value; - foreach (string product in productList) + Transaction transaction = await helper.BuildTransactionX(todaysDateTime.AddHours(-1), merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", product.productValue); + if (transactionsDictionary.ContainsKey(transactionCount.Key) == false) { - var transactionCount = transactionCounts.Single(m => m.Key == merchantName).Value; - for (int i = 0; i < transactionCount; i++) - { - Transaction transaction = await helper.AddTransaction(comparisonDate, merchantName, contract.contract, product, "0000"); - comparisonDateTransactions.Add(transaction); - } + transactionsDictionary.Add(transactionCount.Key, new List()); } + + transactionsDictionary[transactionCount.Key].Add(transaction); } } - await helper.RunTodaysTransactionsSummaryProcessing(comparisonDate.Date); - await helper.RunHistoricTransactionsSummaryProcessing(comparisonDate.Date); + await this.helper.AddTransactionsX(transactionsDictionary.Values.SelectMany(t => t).ToList()); + await helper.RunTodaysTransactionsSummaryProcessing(todaysDateTime.Date); - var result = await ApiClient.GetTodaysSales(string.Empty, Guid.NewGuid(), 0, 0, comparisonDate, CancellationToken.None); + var result = await ApiClient.GetTopBottomOperatorData(string.Empty, Guid.NewGuid(), DataTransferObjects.TopBottom.Top, 3, CancellationToken.None); result.IsSuccess.ShouldBeTrue(); - var todaysSales = result.Data; - - todaysSales.ShouldNotBeNull(); - todaysSales.ComparisonSalesCount.ShouldBe(comparisonDateTransactions.Count); - todaysSales.ComparisonSalesValue.ShouldBe(comparisonDateTransactions.Sum(c => c.TransactionAmount)); - - todaysSales.TodaysSalesCount.ShouldBe(todaysTransactions.Count); - todaysSales.ComparisonSalesValue.ShouldBe(comparisonDateTransactions.Sum(c => c.TransactionAmount)); + List? topBottomOperatorData = result.Data; + topBottomOperatorData[0].OperatorName.ShouldBe("Safaricom"); + topBottomOperatorData[0].SalesValue.ShouldBe(transactionsDictionary["Safaricom"].Sum(p => p.TransactionAmount)); + topBottomOperatorData[1].OperatorName.ShouldBe("PataPawa PostPay"); + topBottomOperatorData[1].SalesValue.ShouldBe(transactionsDictionary["PataPawa PostPay"].Sum(p => p.TransactionAmount)); + topBottomOperatorData[2].OperatorName.ShouldBe("PataPawa PrePay"); + topBottomOperatorData[2].SalesValue.ShouldBe(transactionsDictionary["PataPawa PrePay"].Sum(p => p.TransactionAmount)); } - + [Fact] - public async Task FactTransactionsControllerController_TodaysSalesCountByHour_SalesReturned() - { - List todaysTransactions = new List(); - List comparisonDateTransactions = new List(); - - Dictionary transactionCounts = new(){ - { "Test Merchant 1", 3 }, - { "Test Merchant 2", 6 }, - { "Test Merchant 3", 2 }, - { "Test Merchant 4", 0 } - }; + public async Task FactTransactionsControllerController_OperatorPerformance_SingleOperator_SalesReturned() { + var todaysTransactions = new List(); + var comparisonDateTransactions = new List(); - // TODO: make counts dynamic DateTime todaysDateTime = DateTime.Now; + DateTime comparisonDate = DateTime.Now.AddDays(-1).AddHours(-1); + + Dictionary transactionCounts = new() { + { "Safaricom", 25 }, // 5000 + { "Voucher", 15 }, // 150 + { "PataPawa PostPay", 45 }, // 3375 + { "PataPawa PrePay", 8 } // 600 + }; - for (int hour = 0; hour < 24; hour++) + var merchant = merchantsList.First(); + foreach (KeyValuePair transactionCount in transactionCounts) { - List localList = new List(); - DateTime date = new DateTime(todaysDateTime.Year, todaysDateTime.Month, todaysDateTime.Day, hour, 0, 0); - foreach (string merchantName in merchantsList) + var contract = contractList.Single(s => s.operatorName == transactionCount.Key); + var products = contractProducts.Single(p => p.Key == contract.contractId); + var product = products.Value.OrderByDescending(p => p.productValue.GetValueOrDefault()).First(); + for (int i = 0; i < transactionCount.Value; i++) { - foreach ((string contract, string operatorname) contract in contractList) - { - var productList = contractProducts.Single(cp => cp.Key == contract.contract).Value; - foreach (string product in productList) - { - var transactionCount = transactionCounts.Single(m => m.Key == merchantName).Value; - for (int i = 0; i < transactionCount; i++) - { - Transaction transaction = await helper.AddTransaction(todaysDateTime.AddHours(-1), merchantName, contract.contract, product, "0000"); - todaysTransactions.Add(transaction); - } - } - } + Transaction transaction = await helper.BuildTransactionX(todaysDateTime.AddHours(-1), merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", product.productValue); + todaysTransactions.Add(transaction); } - - todaysTransactions.AddRange(localList); } - DateTime comparisonDate = todaysDateTime.AddDays(-1); - for (int hour = 0; hour < 24; hour++) + await this.helper.AddTransactionsX(todaysTransactions); + + foreach (KeyValuePair transactionCount in transactionCounts) { - List localList = new List(); - DateTime date = new DateTime(comparisonDate.Year, comparisonDate.Month, comparisonDate.Day, hour, 0, 0); - foreach (string merchantName in merchantsList) + var contract = contractList.Single(s => s.operatorName == transactionCount.Key); + var products = contractProducts.Single(p => p.Key == contract.contractId); + var product = products.Value.OrderByDescending(p => p.productValue.GetValueOrDefault()).First(); + for (int i = 0; i < transactionCount.Value; i++) { - foreach (var contract in contractList) - { - var productList = contractProducts.Single(cp => cp.Key == contract.contract).Value; - foreach (string product in productList) - { - var transactionCount = transactionCounts.Single(m => m.Key == merchantName).Value; - for (int i = 0; i < transactionCount; i++) - { - Transaction transaction = await helper.AddTransaction(comparisonDate, merchantName, contract.contract, product, "0000"); - comparisonDateTransactions.Add(transaction); - } - } - } + Transaction transaction = await helper.BuildTransactionX(comparisonDate.AddHours(-1), merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", product.productValue); + comparisonDateTransactions.Add(transaction); } - - comparisonDateTransactions.AddRange(localList); } + await this.helper.AddTransactionsX(comparisonDateTransactions); + + List operatorFilterList = new List { 2 }; + var operatorIdsForVerify = await context.Operators.Where(cp => cp.OperatorReportingId == 2).Select(cp => cp.OperatorId).ToListAsync(CancellationToken.None); + + + string serializedArray = string.Join(",", operatorFilterList); await helper.RunTodaysTransactionsSummaryProcessing(comparisonDate.Date); await helper.RunHistoricTransactionsSummaryProcessing(comparisonDate.Date); await helper.RunTodaysTransactionsSummaryProcessing(todaysDateTime.Date); - var result = await ApiClient.GetTodaysSalesCountByHour(string.Empty, Guid.NewGuid(), 0, 0, comparisonDate, CancellationToken.None); + var result = await ApiClient.GetOperatorPerformance(string.Empty, Guid.NewGuid(), comparisonDate, operatorFilterList, CancellationToken.None); result.IsSuccess.ShouldBeTrue(); - var todaysSalesCountByHour= result.Data; + DataTransferObjects.TodaysSales? todaysSales = result.Data; + todaysSales.ShouldNotBeNull(); + todaysSales.ComparisonSalesCount.ShouldBe(comparisonDateTransactions.Count(c => operatorIdsForVerify.Contains(c.OperatorId))); + todaysSales.ComparisonSalesValue.ShouldBe(comparisonDateTransactions.Where(c => operatorIdsForVerify.Contains(c.OperatorId)).Sum(c => c.TransactionAmount)); - todaysSalesCountByHour.ShouldNotBeNull(); - foreach (TodaysSalesCountByHour salesCountByHour in todaysSalesCountByHour) - { - IEnumerable todayHour = todaysTransactions.Where(t => t.TransactionDateTime.Hour == salesCountByHour.Hour); - IEnumerable comparisonHour = comparisonDateTransactions.Where(t => t.TransactionDateTime.Hour == salesCountByHour.Hour); - salesCountByHour.ComparisonSalesCount.ShouldBe(comparisonHour.Count()); - salesCountByHour.TodaysSalesCount.ShouldBe(todayHour.Count()); - } + todaysSales.TodaysSalesCount.ShouldBe(todaysTransactions.Count(c => operatorIdsForVerify.Contains(c.OperatorId))); + todaysSales.ComparisonSalesValue.ShouldBe(comparisonDateTransactions.Where(c => operatorIdsForVerify.Contains(c.OperatorId)).Sum(c => c.TransactionAmount)); } - + [Fact] - public async Task FactTransactionsControllerController_TodaysSalesValueByHour_SalesReturned() - { + public async Task FactTransactionsControllerController_OperatorPerformance_MultipleOperators_SalesReturned() { var todaysTransactions = new List(); var comparisonDateTransactions = new List(); - Dictionary transactionCounts = new(){ - { "Test Merchant 1", 3 }, - { "Test Merchant 2", 6 }, - { "Test Merchant 3", 2 }, - { "Test Merchant 4", 0 } - }; - DateTime todaysDateTime = DateTime.Now; + DateTime comparisonDate = DateTime.Now.AddDays(-1).AddHours(-1); - for (int hour = 0; hour < 24; hour++) - { - List localList = new List(); - DateTime date = new DateTime(todaysDateTime.Year, todaysDateTime.Month, todaysDateTime.Day, hour, 0, 0); - foreach (string merchantName in merchantsList) - { - foreach (var contract in contractList) - { - var productList = contractProducts.Single(cp => cp.Key == contract.contract).Value; - foreach (string product in productList) - { - var transactionCount = transactionCounts.Single(m => m.Key == merchantName).Value; - for (int i = 0; i < transactionCount; i++) - { - Transaction transaction = await helper.AddTransaction(todaysDateTime.AddHours(-1), merchantName, contract.contract, product, "0000"); - todaysTransactions.Add(transaction); - } - } - } - } - - todaysTransactions.AddRange(localList); - } + Dictionary transactionCounts = new() { + { "Safaricom", 25 }, // 5000 + { "Voucher", 15 }, // 150 + { "PataPawa PostPay", 45 }, // 3375 + { "PataPawa PrePay", 8 } // 600 + }; - DateTime comparisonDate = todaysDateTime.AddDays(-1); - for (int hour = 0; hour < 24; hour++) - { - List localList = new List(); - DateTime date = new DateTime(comparisonDate.Year, comparisonDate.Month, comparisonDate.Day, hour, 0, 0); - foreach (string merchantName in merchantsList) - { - foreach (var contract in contractList) - { - var productList = contractProducts.Single(cp => cp.Key == contract.contract).Value; - foreach (string product in productList) - { - var transactionCount = transactionCounts.Single(m => m.Key == merchantName).Value; - for (int i = 0; i < transactionCount; i++) - { - Transaction transaction = await helper.AddTransaction(comparisonDate, merchantName, contract.contract, product, "0000"); - comparisonDateTransactions.Add(transaction); - } - } - } - } + var merchant = merchantsList.First(); + foreach (KeyValuePair transactionCount in transactionCounts) + { + var contract = contractList.Single(s => s.operatorName == transactionCount.Key); + var products = contractProducts.Single(p => p.Key == contract.contractId); + var product = products.Value.OrderByDescending(p => p.productValue.GetValueOrDefault()).First(); + for (int i = 0; i < transactionCount.Value; i++) + { + Transaction transaction = await helper.BuildTransactionX(todaysDateTime.AddHours(-1), merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", product.productValue); + todaysTransactions.Add(transaction); + } + } + + await this.helper.AddTransactionsX(todaysTransactions); + + foreach (KeyValuePair transactionCount in transactionCounts) + { + var contract = contractList.Single(s => s.operatorName == transactionCount.Key); + var products = contractProducts.Single(p => p.Key == contract.contractId); + var product = products.Value.OrderByDescending(p => p.productValue.GetValueOrDefault()).First(); + for (int i = 0; i < transactionCount.Value; i++) + { + Transaction transaction = await helper.BuildTransactionX(comparisonDate.AddHours(-1), merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", product.productValue); + comparisonDateTransactions.Add(transaction); + } + } + await this.helper.AddTransactionsX(comparisonDateTransactions); + + List operatorFilterList = new List { 2, 3 }; + + var operatorIdsForVerify = await context.Operators.Where(cp => cp.OperatorReportingId >= 2 && cp.OperatorReportingId <= 3).Select(cp => cp.OperatorId).ToListAsync(CancellationToken.None); - comparisonDateTransactions.AddRange(localList); - } + string serializedArray = string.Join(",", operatorFilterList); await helper.RunTodaysTransactionsSummaryProcessing(comparisonDate.Date); await helper.RunHistoricTransactionsSummaryProcessing(comparisonDate.Date); await helper.RunTodaysTransactionsSummaryProcessing(todaysDateTime.Date); - var result = await ApiClient.GetTodaysSalesValueByHour(string.Empty, Guid.NewGuid(), 0, 0, comparisonDate, CancellationToken.None); + var result = await ApiClient.GetOperatorPerformance(string.Empty, Guid.NewGuid(), comparisonDate, operatorFilterList, CancellationToken.None); result.IsSuccess.ShouldBeTrue(); - List? todaysSalesValueByHour = result.Data; - foreach (TodaysSalesValueByHour salesValueByHour in todaysSalesValueByHour) - { - IEnumerable todayHour = todaysTransactions.Where(t => t.TransactionDateTime.Hour == salesValueByHour.Hour); - IEnumerable comparisonHour = comparisonDateTransactions.Where(t => t.TransactionDateTime.Hour == salesValueByHour.Hour); - salesValueByHour.ComparisonSalesValue.ShouldBe(comparisonHour.Sum(c => c.TransactionAmount)); - salesValueByHour.TodaysSalesValue.ShouldBe(todayHour.Sum(c => c.TransactionAmount)); - } + DataTransferObjects.TodaysSales? todaysSales = result.Data; + todaysSales.ShouldNotBeNull(); + todaysSales.ComparisonSalesCount.ShouldBe(comparisonDateTransactions.Count(c => operatorIdsForVerify.Contains(c.OperatorId))); + todaysSales.ComparisonSalesValue.ShouldBe(comparisonDateTransactions.Where(c => operatorIdsForVerify.Contains(c.OperatorId)).Sum(c => c.TransactionAmount)); + + todaysSales.TodaysSalesCount.ShouldBe(todaysTransactions.Count(c => operatorIdsForVerify.Contains(c.OperatorId))); + todaysSales.ComparisonSalesValue.ShouldBe(comparisonDateTransactions.Where(c => operatorIdsForVerify.Contains(c.OperatorId)).Sum(c => c.TransactionAmount)); } +} + +public class FactTransactionsControllerTests_ProductsTests : FactTransactionsControllerTestsBase { + public FactTransactionsControllerTests_ProductsTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) { - #endregion + } - #region Todays Failed Sales Tests [Fact] - public async Task FactTransactionsControllerController_TodaysFailedSales_SalesReturned() - { - EstateManagementGenericContext context = new EstateManagementSqlServerContext(GetLocalConnectionString($"EstateReportingReadModel{TestId.ToString()}")); + public async Task FactTransactionsControllerController_ProductPerformance_AllProducts_SalesReturned() { var todaysTransactions = new List(); var comparisonDateTransactions = new List(); - DatabaseHelper helper = new DatabaseHelper(context); - // TODO: make counts dynamic + DateTime todaysDateTime = DateTime.Now; + DateTime comparisonDate = DateTime.Now.AddDays(-1).AddHours(-1); - Dictionary transactionCounts = new(){ - { "Test Merchant 1", 3 }, - { "Test Merchant 2", 6 }, - { "Test Merchant 3", 2 }, - { "Test Merchant 4", 0 } - }; + var merchant = merchantsList.First(); + var contract = contractList.Single(c => c.operatorName == "Safaricom"); + List<(Guid, String, Decimal?)> productList = contractProducts.Single(cp => cp.Key == contract.contractId).Value; - foreach (string merchantName in merchantsList) - { - foreach (var contract in contractList) - { - var productList = contractProducts.Single(cp => cp.Key == contract.contract).Value; - foreach (string product in productList) - { - var transactionCount = transactionCounts.Single(m => m.Key == merchantName).Value; - for (int i = 0; i < transactionCount; i++) - { - Transaction transaction = await helper.AddTransaction(todaysDateTime.AddHours(-1), merchantName, contract.contract, product, "1009"); - todaysTransactions.Add(transaction); - } - } + Dictionary transactionCounts = new() { + { "200 KES Topup", 25 }, //5000 + { "100 KES Topup", 15 }, // 1500 + { "50 KES Topup", 45 }, // 2250 + { "Custom", 8 } // 600 + }; + foreach ((Guid productId, String productName, Decimal? productValue) product in productList) { + int transactionCount = transactionCounts.Single(m => m.Key == product.productName).Value; + for (int i = 0; i < transactionCount; i++) { + Transaction transaction = await helper.BuildTransactionX(todaysDateTime.AddHours(-1), merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", product.productValue); + todaysTransactions.Add(transaction); } } - DateTime comparisonDate = todaysDateTime.AddDays(-1); - // Comparison Date sales - foreach (string merchantName in merchantsList) - { - foreach (var contract in contractList) - { - var productList = contractProducts.Single(cp => cp.Key == contract.contract).Value; - foreach (string product in productList) - { - var transactionCount = transactionCounts.Single(m => m.Key == merchantName).Value; - for (int i = 0; i < transactionCount; i++) - { - Transaction transaction = await helper.AddTransaction(comparisonDate.AddHours(-1), merchantName, contract.contract, product, "1009"); - comparisonDateTransactions.Add(transaction); - } - } + await this.helper.AddTransactionsX(todaysTransactions); + foreach ((Guid productId, String productName, Decimal? productValue) product in productList) { + int transactionCount = transactionCounts.Single(m => m.Key == product.productName).Value; + for (int i = 0; i < transactionCount; i++) { + Transaction transaction = await helper.BuildTransactionX(comparisonDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", product.productValue); + comparisonDateTransactions.Add(transaction); } } + await this.helper.AddTransactionsX(comparisonDateTransactions); + await helper.RunTodaysTransactionsSummaryProcessing(comparisonDate.Date); await helper.RunHistoricTransactionsSummaryProcessing(comparisonDate.Date); await helper.RunTodaysTransactionsSummaryProcessing(todaysDateTime.Date); - var result = await ApiClient.GetTodaysFailedSales(string.Empty, Guid.NewGuid(), 1, 1, "1009", comparisonDate, CancellationToken.None); + var result = await ApiClient.GetProductPerformance(string.Empty, Guid.NewGuid(), comparisonDate, new List(), CancellationToken.None); result.IsSuccess.ShouldBeTrue(); - TodaysSales? todaysSales = result.Data; - + DataTransferObjects.TodaysSales? todaysSales = result.Data; todaysSales.ShouldNotBeNull(); todaysSales.ComparisonSalesCount.ShouldBe(comparisonDateTransactions.Count); todaysSales.ComparisonSalesValue.ShouldBe(comparisonDateTransactions.Sum(c => c.TransactionAmount)); @@ -412,1319 +422,1447 @@ public async Task FactTransactionsControllerController_TodaysFailedSales_SalesRe todaysSales.ComparisonSalesValue.ShouldBe(comparisonDateTransactions.Sum(c => c.TransactionAmount)); } - #endregion [Fact] - public async Task FactTransactionsController_GetTopBottomProductsByValue_BottomProducts_ProductsReturned() - { + public async Task FactTransactionsControllerController_ProductPerformance_SingleProduct_SalesReturned() { + var todaysTransactions = new List(); + var comparisonDateTransactions = new List(); + DateTime todaysDateTime = DateTime.Now; + DateTime comparisonDate = DateTime.Now.AddDays(-1).AddHours(-1); - string merchantName = merchantsList.First(); - (string contract, string operatorname) contract = contractList.Single(c => c.operatorname == "Safaricom"); - List productList = contractProducts.Single(cp => cp.Key == contract.contract).Value; + var merchant = merchantsList.First(); + var contract = this.contractList.Single(c => c.operatorName == "Safaricom"); + var productList = contractProducts.Single(cp => cp.Key == contract.contractId).Value; - Dictionary transactionCounts = new(){ - { "200 KES Topup", 25 }, //5000 - { "100 KES Topup", 15 }, // 1500 - { "50 KES Topup", 45 }, // 2250 - { "Custom", 8 } // 600 - }; - Dictionary> transactionsDictionary = new(); - foreach (string product in productList) - { - int transactionCount = transactionCounts.Single(m => m.Key == product).Value; - for (int i = 0; i < transactionCount; i++) - { - Transaction transaction = await helper.AddTransaction(todaysDateTime.AddHours(-1), merchantName, contract.contract, product, "0000"); - if (transactionsDictionary.ContainsKey(product) == false) - { - transactionsDictionary.Add(product, new List()); - } + Dictionary transactionCounts = new() { + { "200 KES Topup", 25 }, //5000 + { "100 KES Topup", 15 }, // 1500 + { "50 KES Topup", 45 }, // 2250 + { "Custom", 8 } // 600 + }; - transactionsDictionary[product].Add(transaction); + foreach ((Guid productId, String productName, Decimal? productValue) product in productList) { + int transactionCount = transactionCounts.Single(m => m.Key == product.productName).Value; + for (int i = 0; i < transactionCount; i++) { + Transaction transaction = await helper.BuildTransactionX(todaysDateTime.AddHours(-1), merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", product.productValue); + todaysTransactions.Add(transaction); + } } - } - await helper.RunTodaysTransactionsSummaryProcessing(todaysDateTime.Date); + await this.helper.AddTransactionsX(todaysTransactions); + foreach ((Guid productId, String productName, Decimal? productValue) product in productList) { + + int transactionCount = transactionCounts.Single(m => m.Key == product.productName).Value; + for (int i = 0; i < transactionCount; i++) { + Transaction transaction = await helper.BuildTransactionX(comparisonDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", product.productValue); + comparisonDateTransactions.Add(transaction); + } + } - var result = await ApiClient.GetTopBottomProductData(string.Empty, Guid.NewGuid(), TopBottom.Bottom, 3, CancellationToken.None); - result.IsSuccess.ShouldBeTrue(); - List? topBottomProductData = result.Data; - - topBottomProductData[0].ProductName.ShouldBe("Custom"); - topBottomProductData[0].SalesValue.ShouldBe(transactionsDictionary["Custom"].Sum(p => p.TransactionAmount)); - topBottomProductData[1].ProductName.ShouldBe("100 KES Topup"); - topBottomProductData[1].SalesValue.ShouldBe(transactionsDictionary["100 KES Topup"].Sum(p => p.TransactionAmount)); - topBottomProductData[2].ProductName.ShouldBe("50 KES Topup"); - topBottomProductData[2].SalesValue.ShouldBe(transactionsDictionary["50 KES Topup"].Sum(p => p.TransactionAmount)); - } + await this.helper.AddTransactionsX(comparisonDateTransactions); - [Fact] - public async Task FactTransactionsController_GetTopBottomProductsByValue_TopProducts_ProductsReturned() - { - DateTime todaysDateTime = DateTime.Now; + List productFilterList = new List { 2 }; - string merchantName = merchantsList.First(); - (string contract, string operatorname) contract = contractList.Single(c => c.operatorname == "Safaricom"); - List productList = contractProducts.Single(cp => cp.Key == contract.contract).Value; + var productIdsForVerify = await context.ContractProducts.Where(cp => cp.ContractProductReportingId == 2).Select(cp => cp.ContractProductId).ToListAsync(CancellationToken.None); - Dictionary transactionCounts = new(){ - { "200 KES Topup", 25 }, //5000 - { "100 KES Topup", 15 }, // 1500 - { "50 KES Topup", 45 }, // 2250 - { "Custom", 8 } // 600 - }; - Dictionary> transactionsDictionary = new(); - foreach (string product in productList) - { - int transactionCount = transactionCounts.Single(m => m.Key == product).Value; - for (int i = 0; i < transactionCount; i++) - { - Transaction transaction = await helper.AddTransaction(todaysDateTime.AddHours(-1), merchantName, contract.contract, product, "0000"); - if (transactionsDictionary.ContainsKey(product) == false) - { - transactionsDictionary.Add(product, new List()); - } + await helper.RunTodaysTransactionsSummaryProcessing(comparisonDate.Date); + await helper.RunHistoricTransactionsSummaryProcessing(comparisonDate.Date); + await helper.RunTodaysTransactionsSummaryProcessing(todaysDateTime.Date); + + string serializedArray = string.Join(",", productFilterList); + + var result = await ApiClient.GetProductPerformance(string.Empty, Guid.NewGuid(), comparisonDate, productFilterList, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + DataTransferObjects.TodaysSales? todaysSales = result.Data; + todaysSales.ShouldNotBeNull(); + todaysSales.ComparisonSalesCount.ShouldBe(comparisonDateTransactions.Count(c => productIdsForVerify.Contains(c.ContractProductId))); + todaysSales.ComparisonSalesValue.ShouldBe(comparisonDateTransactions.Where(c => productIdsForVerify.Contains(c.ContractProductId)).Sum(c => c.TransactionAmount)); - transactionsDictionary[product].Add(transaction); + todaysSales.TodaysSalesCount.ShouldBe(todaysTransactions.Count(c => productIdsForVerify.Contains(c.ContractProductId))); + todaysSales.ComparisonSalesValue.ShouldBe(comparisonDateTransactions.Where(c => productIdsForVerify.Contains(c.ContractProductId)).Sum(c => c.TransactionAmount)); } - } + - await helper.RunTodaysTransactionsSummaryProcessing(todaysDateTime.Date); + [Fact] + public async Task FactTransactionsControllerController_ProductPerformance_MultipleProducts_SalesReturned() { + var todaysTransactions = new List(); + var comparisonDateTransactions = new List(); + + DateTime todaysDateTime = DateTime.Now; + DateTime comparisonDate = DateTime.Now.AddDays(-1).AddHours(-1); + + var merchant = merchantsList.First(); + var contract = this.contractList.Single(c => c.operatorName == "Safaricom"); + var productList = contractProducts.Single(cp => cp.Key == contract.contractId).Value; + + Dictionary transactionCounts = new() { + { "200 KES Topup", 25 }, //5000 + { "100 KES Topup", 15 }, // 1500 + { "50 KES Topup", 45 }, // 2250 + { "Custom", 8 } // 600 + }; + foreach ((Guid productId, String productName, Decimal? productValue) product in productList) { + int transactionCount = transactionCounts.Single(m => m.Key == product.productName).Value; + for (int i = 0; i < transactionCount; i++) { + Transaction transaction = await helper.BuildTransactionX(todaysDateTime.AddHours(-1), merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", product.productValue); + todaysTransactions.Add(transaction); + } + } - var result= await ApiClient.GetTopBottomProductData(string.Empty, Guid.NewGuid(), TopBottom.Top, 3, CancellationToken.None); - result.IsSuccess.ShouldBeTrue(); - List? topBottomProductData = result.Data; - topBottomProductData[0].ProductName.ShouldBe("200 KES Topup"); - topBottomProductData[0].SalesValue.ShouldBe(transactionsDictionary["200 KES Topup"].Sum(p => p.TransactionAmount)); - topBottomProductData[1].ProductName.ShouldBe("50 KES Topup"); - topBottomProductData[1].SalesValue.ShouldBe(transactionsDictionary["50 KES Topup"].Sum(p => p.TransactionAmount)); - topBottomProductData[2].ProductName.ShouldBe("100 KES Topup"); - topBottomProductData[2].SalesValue.ShouldBe(transactionsDictionary["100 KES Topup"].Sum(p => p.TransactionAmount)); - } + await this.helper.AddTransactionsX(todaysTransactions); + foreach ((Guid productId, String productName, Decimal? productValue) product in productList) { + int transactionCount = transactionCounts.Single(m => m.Key == product.productName).Value; + for (int i = 0; i < transactionCount; i++) { + Transaction transaction = await helper.BuildTransactionX(comparisonDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", product.productValue); + comparisonDateTransactions.Add(transaction); + } + } - [Fact] - public async Task FactTransactionsController_GetTopBottomOperatorsByValue_BottomOperators_OperatorsReturned() - { - DateTime todaysDateTime = DateTime.Now; + await this.helper.AddTransactionsX(comparisonDateTransactions); - Dictionary transactionCounts = new(){ - { "Safaricom", 25 }, // 5000 - { "Voucher", 15 }, // 150 - { "PataPawa PostPay", 45 }, // 3375 - { "PataPawa PrePay", 8 } // 600 - }; + List productFilterList = new List { 2, 3 }; + var productIdsForVerify = await context.ContractProducts.Where(cp => cp.ContractProductReportingId >= 2 && cp.ContractProductReportingId <= 3).Select(cp => cp.ContractProductId).ToListAsync(CancellationToken.None); - Dictionary> transactionsDictionary = new(); - string merchantName = merchantsList.First(); - //List productList = contractProducts.Single(cp => cp.Key == contractName).Value; - foreach (KeyValuePair transactionCount in transactionCounts) - { - var contract = contractList.Single(s => s.operatorname == transactionCount.Key); - var products = contractProducts.Single(p => p.Key == contract.contract); - var productname = products.Value.First(); - for (int i = 0; i < transactionCount.Value; i++) - { - Transaction transaction = await helper.AddTransaction(todaysDateTime.AddHours(-1), merchantName, contract.contract, productname, "0000"); - if (transactionsDictionary.ContainsKey(transactionCount.Key) == false) - { - transactionsDictionary.Add(transactionCount.Key, new List()); - } + string serializedArray = string.Join(",", productFilterList); - transactionsDictionary[transactionCount.Key].Add(transaction); - } - } + await helper.RunTodaysTransactionsSummaryProcessing(comparisonDate.Date); + await helper.RunHistoricTransactionsSummaryProcessing(comparisonDate.Date); + await helper.RunTodaysTransactionsSummaryProcessing(todaysDateTime.Date); - await helper.RunTodaysTransactionsSummaryProcessing(todaysDateTime.Date); + var result = await ApiClient.GetProductPerformance(string.Empty, Guid.NewGuid(), comparisonDate, productFilterList, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + DataTransferObjects.TodaysSales? todaysSales = result.Data; + todaysSales.ShouldNotBeNull(); + todaysSales.ComparisonSalesCount.ShouldBe(comparisonDateTransactions.Count(c => productIdsForVerify.Contains(c.ContractProductId))); + todaysSales.ComparisonSalesValue.ShouldBe(comparisonDateTransactions.Where(c => productIdsForVerify.Contains(c.ContractProductId)).Sum(c => c.TransactionAmount)); - var result= await ApiClient.GetTopBottomOperatorData(string.Empty, Guid.NewGuid(), TopBottom.Bottom, 3, CancellationToken.None); - result.IsSuccess.ShouldBeTrue(); - List? topBottomOperatorData = result.Data; - topBottomOperatorData.ShouldNotBeNull(); - topBottomOperatorData[0].OperatorName.ShouldBe("Voucher"); - topBottomOperatorData[0].SalesValue.ShouldBe(transactionsDictionary["Voucher"].Sum(p => p.TransactionAmount)); - topBottomOperatorData[1].OperatorName.ShouldBe("PataPawa PrePay"); - topBottomOperatorData[1].SalesValue.ShouldBe(transactionsDictionary["PataPawa PrePay"].Sum(p => p.TransactionAmount)); - topBottomOperatorData[2].OperatorName.ShouldBe("PataPawa PostPay"); - topBottomOperatorData[2].SalesValue.ShouldBe(transactionsDictionary["PataPawa PostPay"].Sum(p => p.TransactionAmount)); - } + todaysSales.TodaysSalesCount.ShouldBe(todaysTransactions.Count(c => productIdsForVerify.Contains(c.ContractProductId))); + todaysSales.ComparisonSalesValue.ShouldBe(comparisonDateTransactions.Where(c => productIdsForVerify.Contains(c.ContractProductId)).Sum(c => c.TransactionAmount)); + } - [Fact] - public async Task FactTransactionsController_GetTopBottomOperatorsByValue_TopOperators_OperatorsReturned() - { - DateTime todaysDateTime = DateTime.Now; + [Fact] + public async Task FactTransactionsController_GetTopBottomProductsByValue_BottomProducts_ProductsReturned() { + DateTime todaysDateTime = DateTime.Now; + + var merchant = merchantsList.First(); + var contract = this.contractList.Single(c => c.operatorName == "Safaricom"); + var productList = contractProducts.Single(cp => cp.Key == contract.contractId).Value; + + Dictionary transactionCounts = new() { + { "200 KES Topup", 25 }, //5000 + { "100 KES Topup", 15 }, // 1500 + { "50 KES Topup", 45 }, // 2250 + { "Custom", 8 } // 600 + }; + Dictionary> transactionsDictionary = new(); + foreach ((Guid productId, String productName, Decimal? productValue) product in productList) { + int transactionCount = transactionCounts.Single(m => m.Key == product.productName).Value; + for (int i = 0; i < transactionCount; i++) { + Transaction transaction = await helper.BuildTransactionX(todaysDateTime.AddHours(-1), merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", product.productValue); + if (transactionsDictionary.ContainsKey(product.productName) == false) { + transactionsDictionary.Add(product.productName, new List()); + } + + transactionsDictionary[product.productName].Add(transaction); + } + } - Dictionary transactionCounts = new(){ - { "Safaricom", 25 }, // 5000 - { "Voucher", 15 }, // 150 - { "PataPawa PostPay", 45 }, // 3375 - { "PataPawa PrePay", 8 } // 600 - }; + await this.helper.AddTransactionsX(transactionsDictionary.Values.SelectMany(t => t).ToList()); - Dictionary> transactionsDictionary = new(); - string merchantName = merchantsList.First(); - foreach (KeyValuePair transactionCount in transactionCounts) - { - var contract = contractList.Single(s => s.operatorname == transactionCount.Key); - var products = contractProducts.Single(p => p.Key == contract.contract); - var productname = products.Value.First(); - for (int i = 0; i < transactionCount.Value; i++) - { - Transaction transaction = await helper.AddTransaction(todaysDateTime.AddHours(-1), merchantName, contract.contract, productname, "0000"); - if (transactionsDictionary.ContainsKey(transactionCount.Key) == false) - { - transactionsDictionary.Add(transactionCount.Key, new List()); + await helper.RunTodaysTransactionsSummaryProcessing(todaysDateTime.Date); + + var result = await ApiClient.GetTopBottomProductData(string.Empty, Guid.NewGuid(), DataTransferObjects.TopBottom.Bottom, 3, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + List? topBottomProductData = result.Data; + + topBottomProductData[0].ProductName.ShouldBe("Custom"); + topBottomProductData[0].SalesValue.ShouldBe(transactionsDictionary["Custom"].Sum(p => p.TransactionAmount)); + topBottomProductData[1].ProductName.ShouldBe("100 KES Topup"); + topBottomProductData[1].SalesValue.ShouldBe(transactionsDictionary["100 KES Topup"].Sum(p => p.TransactionAmount)); + topBottomProductData[2].ProductName.ShouldBe("50 KES Topup"); + topBottomProductData[2].SalesValue.ShouldBe(transactionsDictionary["50 KES Topup"].Sum(p => p.TransactionAmount)); } - transactionsDictionary[transactionCount.Key].Add(transaction); - } - } - await helper.RunTodaysTransactionsSummaryProcessing(todaysDateTime.Date); - - var result = await ApiClient.GetTopBottomOperatorData(string.Empty, Guid.NewGuid(), TopBottom.Top, 3, CancellationToken.None); - result.IsSuccess.ShouldBeTrue(); - List? topBottomOperatorData = result.Data; - topBottomOperatorData[0].OperatorName.ShouldBe("Safaricom"); - topBottomOperatorData[0].SalesValue.ShouldBe(transactionsDictionary["Safaricom"].Sum(p => p.TransactionAmount)); - topBottomOperatorData[1].OperatorName.ShouldBe("PataPawa PostPay"); - topBottomOperatorData[1].SalesValue.ShouldBe(transactionsDictionary["PataPawa PostPay"].Sum(p => p.TransactionAmount)); - topBottomOperatorData[2].OperatorName.ShouldBe("PataPawa PrePay"); - topBottomOperatorData[2].SalesValue.ShouldBe(transactionsDictionary["PataPawa PrePay"].Sum(p => p.TransactionAmount)); - } + [Fact] + public async Task FactTransactionsController_GetTopBottomProductsByValue_TopProducts_ProductsReturned() { + DateTime todaysDateTime = DateTime.Now; + + var merchant = merchantsList.First(); + var contract = this.contractList.Single(c => c.operatorName == "Safaricom"); + var productList = contractProducts.Single(cp => cp.Key == contract.contractId).Value; + + Dictionary transactionCounts = new() { + { "200 KES Topup", 25 }, //5000 + { "100 KES Topup", 15 }, // 1500 + { "50 KES Topup", 45 }, // 2250 + { "Custom", 8 } // 600 + }; + + Dictionary> transactionsDictionary = new(); + foreach ((Guid productId, String productName, Decimal? productValue) product in productList) { + + int transactionCount = transactionCounts.Single(m => m.Key == product.productName).Value; + for (int i = 0; i < transactionCount; i++) { + Transaction transaction = await helper.BuildTransactionX(todaysDateTime.AddHours(-1), merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", product.productValue); + if (transactionsDictionary.ContainsKey(product.productName) == false) { + transactionsDictionary.Add(product.productName, new List()); + } + + transactionsDictionary[product.productName].Add(transaction); + } + } - [Fact] - public async Task FactTransactionsController_GetTopBottomMerchantsByValue_BottomMerchants_MerchantsReturned() - { - DateTime todaysDateTime = DateTime.Now; + await this.helper.AddTransactionsX(transactionsDictionary.Values.SelectMany(t => t).ToList()); - Dictionary transactionCounts = new(){ - { "Test Merchant 1", 25 }, - { "Test Merchant 2", 15 }, - { "Test Merchant 3", 45 }, - { "Test Merchant 4", 8 } - }; + await helper.RunTodaysTransactionsSummaryProcessing(todaysDateTime.Date); - Dictionary> transactionsDictionary = new(); - foreach (KeyValuePair transactionCount in transactionCounts) - { - var contract = contractList.First(); - var products = contractProducts.Single(p => p.Key == contract.contract); - var productname = products.Value.First(); - for (int i = 0; i < transactionCount.Value; i++) - { - Transaction transaction = await helper.AddTransaction(todaysDateTime.AddHours(-1), transactionCount.Key, contract.contract, productname, "0000"); - if (transactionsDictionary.ContainsKey(transactionCount.Key) == false) - { - transactionsDictionary.Add(transactionCount.Key, new List()); + var result = await ApiClient.GetTopBottomProductData(string.Empty, Guid.NewGuid(), DataTransferObjects.TopBottom.Top, 3, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + List? topBottomProductData = result.Data; + topBottomProductData[0].ProductName.ShouldBe("200 KES Topup"); + topBottomProductData[0].SalesValue.ShouldBe(transactionsDictionary["200 KES Topup"].Sum(p => p.TransactionAmount)); + topBottomProductData[1].ProductName.ShouldBe("50 KES Topup"); + topBottomProductData[1].SalesValue.ShouldBe(transactionsDictionary["50 KES Topup"].Sum(p => p.TransactionAmount)); + topBottomProductData[2].ProductName.ShouldBe("100 KES Topup"); + topBottomProductData[2].SalesValue.ShouldBe(transactionsDictionary["100 KES Topup"].Sum(p => p.TransactionAmount)); + } } - transactionsDictionary[transactionCount.Key].Add(transaction); - } + public class FactTransactionsControllerTests_MerchantsTests : FactTransactionsControllerTestsBase { + + public FactTransactionsControllerTests_MerchantsTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) { + } - - await helper.RunTodaysTransactionsSummaryProcessing(todaysDateTime.Date); - var result = await ApiClient.GetTopBottomMerchantData(string.Empty, Guid.NewGuid(), TopBottom.Bottom, 3, CancellationToken.None); - result.IsSuccess.ShouldBeTrue(); - List? topBottomMerchantData = result.Data; - topBottomMerchantData.ShouldNotBeNull(); - topBottomMerchantData[0].MerchantName.ShouldBe("Test Merchant 4"); - topBottomMerchantData[0].SalesValue.ShouldBe(transactionsDictionary["Test Merchant 4"].Sum(p => p.TransactionAmount)); - topBottomMerchantData[1].MerchantName.ShouldBe("Test Merchant 2"); - topBottomMerchantData[1].SalesValue.ShouldBe(transactionsDictionary["Test Merchant 2"].Sum(p => p.TransactionAmount)); - topBottomMerchantData[2].MerchantName.ShouldBe("Test Merchant 1"); - topBottomMerchantData[2].SalesValue.ShouldBe(transactionsDictionary["Test Merchant 1"].Sum(p => p.TransactionAmount)); - } + [Fact] + public async Task FactTransactionsControllerController_GetMerchantsByLastDaleDate_MerchantsReturned() { + DateTime todaysDateTime = DateTime.Now; + + await ClearStandingData(); + + // Last Hour + await helper.AddMerchant("Test Estate", "Merchant 1", todaysDateTime.AddMinutes(-10)); + await helper.AddMerchant("Test Estate", "Merchant 2", todaysDateTime.AddMinutes(-10)); + await helper.AddMerchant("Test Estate", "Merchant 3", todaysDateTime.AddMinutes(-10)); + await helper.AddMerchant("Test Estate", "Merchant 4", todaysDateTime.AddMinutes(-10)); + + // Yesterday + await helper.AddMerchant("Test Estate", "Merchant 5", todaysDateTime.AddDays(-1)); + await helper.AddMerchant("Test Estate", "Merchant 6", todaysDateTime.AddDays(-1)); + await helper.AddMerchant("Test Estate", "Merchant 7", todaysDateTime.AddDays(-1)); + await helper.AddMerchant("Test Estate", "Merchant 8", todaysDateTime.AddDays(-1)); + await helper.AddMerchant("Test Estate", "Merchant 9", todaysDateTime.AddDays(-1)); + await helper.AddMerchant("Test Estate", "Merchant 10", todaysDateTime.AddDays(-1)); + + // 5 days ago + await helper.AddMerchant("Test Estate", "Merchant 11", todaysDateTime.AddDays(-5)); + await helper.AddMerchant("Test Estate", "Merchant 12", todaysDateTime.AddDays(-5)); + await helper.AddMerchant("Test Estate", "Merchant 13", todaysDateTime.AddDays(-5)); + + // 10 Days Ago + await helper.AddMerchant("Test Estate", "Merchant 14", todaysDateTime.AddDays(-10)); + await helper.AddMerchant("Test Estate", "Merchant 15", todaysDateTime.AddDays(-10)); + await helper.AddMerchant("Test Estate", "Merchant 16", todaysDateTime.AddDays(-10)); + await helper.AddMerchant("Test Estate", "Merchant 17", todaysDateTime.AddDays(-10)); + await helper.AddMerchant("Test Estate", "Merchant 18", todaysDateTime.AddDays(-10)); + + DateTime startDate = DateTime.Now; + DateTime endDate = DateTime.Now; + + // Test 1 - sale in last hour + startDate = DateTime.Now.AddHours(-1); + endDate = DateTime.Now; + var result = await this.ApiClient.GetMerchantsByLastSaleDate(String.Empty, Guid.NewGuid(), startDate, endDate, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + var searchResult = result.Data; + searchResult.ShouldNotBeNull(); + searchResult.Count.ShouldBe(4); + searchResult.SingleOrDefault(s => s.Name == "Merchant 1").ShouldNotBeNull(); + searchResult.SingleOrDefault(s => s.Name == "Merchant 2").ShouldNotBeNull(); + searchResult.SingleOrDefault(s => s.Name == "Merchant 3").ShouldNotBeNull(); + searchResult.SingleOrDefault(s => s.Name == "Merchant 4").ShouldNotBeNull(); + + // Test 2 - sale in last day but over an hour ago + startDate = DateTime.Now.Date.AddDays(-1); + endDate = DateTime.Now.AddHours(-1); + result = await this.ApiClient.GetMerchantsByLastSaleDate(String.Empty, Guid.NewGuid(), startDate, endDate, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + searchResult = result.Data; + searchResult.ShouldNotBeNull(); + searchResult.Count.ShouldBe(6); + searchResult.SingleOrDefault(s => s.Name == "Merchant 5").ShouldNotBeNull(); + searchResult.SingleOrDefault(s => s.Name == "Merchant 6").ShouldNotBeNull(); + searchResult.SingleOrDefault(s => s.Name == "Merchant 7").ShouldNotBeNull(); + searchResult.SingleOrDefault(s => s.Name == "Merchant 8").ShouldNotBeNull(); + searchResult.SingleOrDefault(s => s.Name == "Merchant 9").ShouldNotBeNull(); + searchResult.SingleOrDefault(s => s.Name == "Merchant 10").ShouldNotBeNull(); + + // Test 3 - sale in last 7 days but non yesterday + startDate = DateTime.Now.Date.AddDays(-7); + endDate = DateTime.Now.Date.AddDays(-1); + result = await this.ApiClient.GetMerchantsByLastSaleDate(String.Empty, Guid.NewGuid(), startDate, endDate, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + searchResult = result.Data; + searchResult.ShouldNotBeNull(); + searchResult.Count.ShouldBe(3); + searchResult.SingleOrDefault(s => s.Name == "Merchant 11").ShouldNotBeNull(); + searchResult.SingleOrDefault(s => s.Name == "Merchant 12").ShouldNotBeNull(); + searchResult.SingleOrDefault(s => s.Name == "Merchant 13").ShouldNotBeNull(); + + // Test 4 - sale more than 7 days ago + startDate = DateTime.Now.Date.AddYears(-1); + endDate = DateTime.Now.Date.AddDays(-7); + result = await this.ApiClient.GetMerchantsByLastSaleDate(String.Empty, Guid.NewGuid(), startDate, endDate, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + searchResult = result.Data; + searchResult.ShouldNotBeNull(); + searchResult.Count.ShouldBe(5); + searchResult.SingleOrDefault(s => s.Name == "Merchant 14").ShouldNotBeNull(); + searchResult.SingleOrDefault(s => s.Name == "Merchant 15").ShouldNotBeNull(); + searchResult.SingleOrDefault(s => s.Name == "Merchant 16").ShouldNotBeNull(); + searchResult.SingleOrDefault(s => s.Name == "Merchant 17").ShouldNotBeNull(); + searchResult.SingleOrDefault(s => s.Name == "Merchant 18").ShouldNotBeNull(); + } - [Fact] - public async Task FactTransactionsController_GetTopBottomMerchantsByValue_TopMerchants_MerchantsReturned() - { - DateTime todaysDateTime = DateTime.Now; + [Fact] + public async Task FactTransactionsControllerController_GetMerchantsTransactionKpis_SalesReturned() { + DateTime todaysDateTime = DateTime.Now; + + await ClearStandingData(); + + // Last Hour + await helper.AddMerchant("Test Estate", "Merchant 1", todaysDateTime.AddMinutes(-10)); + await helper.AddMerchant("Test Estate", "Merchant 2", todaysDateTime.AddMinutes(-10)); + await helper.AddMerchant("Test Estate", "Merchant 3", todaysDateTime.AddMinutes(-10)); + await helper.AddMerchant("Test Estate", "Merchant 4", todaysDateTime.AddMinutes(-10)); + + // Yesterday + await helper.AddMerchant("Test Estate", "Merchant 5", todaysDateTime.AddDays(-1)); + await helper.AddMerchant("Test Estate", "Merchant 6", todaysDateTime.AddDays(-1)); + await helper.AddMerchant("Test Estate", "Merchant 7", todaysDateTime.AddDays(-1)); + await helper.AddMerchant("Test Estate", "Merchant 8", todaysDateTime.AddDays(-1)); + await helper.AddMerchant("Test Estate", "Merchant 9", todaysDateTime.AddDays(-1)); + await helper.AddMerchant("Test Estate", "Merchant 10", todaysDateTime.AddDays(-1)); + + // 10 Days Ago + await helper.AddMerchant("Test Estate", "Merchant 11", todaysDateTime.AddDays(-10)); + await helper.AddMerchant("Test Estate", "Merchant 12", todaysDateTime.AddDays(-10)); + await helper.AddMerchant("Test Estate", "Merchant 13", todaysDateTime.AddDays(-10)); + await helper.AddMerchant("Test Estate", "Merchant 14", todaysDateTime.AddDays(-10)); + await helper.AddMerchant("Test Estate", "Merchant 15", todaysDateTime.AddDays(-10)); + await helper.AddMerchant("Test Estate", "Merchant 16", todaysDateTime.AddDays(-10)); + await helper.AddMerchant("Test Estate", "Merchant 17", todaysDateTime.AddDays(-10)); + await helper.AddMerchant("Test Estate", "Merchant 18", todaysDateTime.AddDays(-10)); + + var result = await ApiClient.GetMerchantKpi(string.Empty, Guid.NewGuid(), CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + DataTransferObjects.MerchantKpi? merchantKpi = result.Data; + merchantKpi.ShouldNotBeNull(); + merchantKpi.MerchantsWithSaleInLastHour.ShouldBe(4); + merchantKpi.MerchantsWithNoSaleToday.ShouldBe(6); + merchantKpi.MerchantsWithNoSaleInLast7Days.ShouldBe(8); + } - Dictionary transactionCounts = new(){ - { "Test Merchant 1", 25 }, - { "Test Merchant 2", 15 }, - { "Test Merchant 3", 45 }, - { "Test Merchant 4", 8 } - }; + [Fact] + public async Task FactTransactionsController_GetTopBottomMerchantsByValue_BottomMerchants_MerchantsReturned() { + DateTime todaysDateTime = DateTime.Now; + + Dictionary transactionCounts = new() { { "Test Merchant 1", 25 }, { "Test Merchant 2", 15 }, { "Test Merchant 3", 45 }, { "Test Merchant 4", 8 } }; + + Dictionary> transactionsDictionary = new(); + foreach (KeyValuePair transactionCount in transactionCounts) { + var merchant = merchantsList.Where(m => m.Name == transactionCount.Key).Single(); + var contract = this.contractList.Single(c => c.operatorName == "Safaricom"); + var product = contractProducts.Single(cp => cp.Key == contract.contractId).Value.First(); + for (int i = 0; i < transactionCount.Value; i++) { + Transaction transaction = await helper.BuildTransactionX(todaysDateTime.AddHours(-1), merchant.MerchantId, contract.operatorId, contract.contractId, product.Item1, "0000", product.productValue); + if (transactionsDictionary.ContainsKey(transactionCount.Key) == false) { + transactionsDictionary.Add(transactionCount.Key, new List()); + } - Dictionary> transactionsDictionary = new(); - foreach (KeyValuePair transactionCount in transactionCounts) - { - var contract = contractList.First(); - var products = contractProducts.Single(p => p.Key == contract.contract); - var productname = products.Value.First(); - for (int i = 0; i < transactionCount.Value; i++) - { - Transaction transaction = await helper.AddTransaction(todaysDateTime.AddHours(-1), transactionCount.Key, contract.contract, productname, "0000"); - if (transactionsDictionary.ContainsKey(transactionCount.Key) == false) - { - transactionsDictionary.Add(transactionCount.Key, new List()); + transactionsDictionary[transactionCount.Key].Add(transaction); } - - transactionsDictionary[transactionCount.Key].Add(transaction); } + + await this.helper.AddTransactionsX(transactionsDictionary.Values.SelectMany(t => t).ToList()); + + await helper.RunTodaysTransactionsSummaryProcessing(todaysDateTime.Date); + + var result = await ApiClient.GetTopBottomMerchantData(string.Empty, Guid.NewGuid(), DataTransferObjects.TopBottom.Bottom, 3, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + List? topBottomMerchantData = result.Data; + topBottomMerchantData.ShouldNotBeNull(); + topBottomMerchantData[0].MerchantName.ShouldBe("Test Merchant 4"); + topBottomMerchantData[0].SalesValue.ShouldBe(transactionsDictionary["Test Merchant 4"].Sum(p => p.TransactionAmount)); + topBottomMerchantData[1].MerchantName.ShouldBe("Test Merchant 2"); + topBottomMerchantData[1].SalesValue.ShouldBe(transactionsDictionary["Test Merchant 2"].Sum(p => p.TransactionAmount)); + topBottomMerchantData[2].MerchantName.ShouldBe("Test Merchant 1"); + topBottomMerchantData[2].SalesValue.ShouldBe(transactionsDictionary["Test Merchant 1"].Sum(p => p.TransactionAmount)); } - await helper.RunTodaysTransactionsSummaryProcessing(todaysDateTime.Date); - var result = await ApiClient.GetTopBottomMerchantData(string.Empty, Guid.NewGuid(), TopBottom.Top, 3, CancellationToken.None); - result.IsSuccess.ShouldBeTrue(); - List? topBottomMerchantData = result.Data; - topBottomMerchantData.ShouldNotBeNull(); - topBottomMerchantData[0].MerchantName.ShouldBe("Test Merchant 3"); - topBottomMerchantData[0].SalesValue.ShouldBe(transactionsDictionary["Test Merchant 3"].Sum(p => p.TransactionAmount)); - topBottomMerchantData[1].MerchantName.ShouldBe("Test Merchant 1"); - topBottomMerchantData[1].SalesValue.ShouldBe(transactionsDictionary["Test Merchant 1"].Sum(p => p.TransactionAmount)); - topBottomMerchantData[2].MerchantName.ShouldBe("Test Merchant 2"); - topBottomMerchantData[2].SalesValue.ShouldBe(transactionsDictionary["Test Merchant 2"].Sum(p => p.TransactionAmount)); - } + [Fact] + public async Task FactTransactionsController_GetTopBottomMerchantsByValue_TopMerchants_MerchantsReturned() { + DateTime todaysDateTime = DateTime.Now; + + Dictionary transactionCounts = new() { { "Test Merchant 1", 25 }, { "Test Merchant 2", 15 }, { "Test Merchant 3", 45 }, { "Test Merchant 4", 8 } }; + + Dictionary> transactionsDictionary = new(); + foreach (KeyValuePair transactionCount in transactionCounts) { + var merchant = merchantsList.Where(m => m.Name == transactionCount.Key).Single(); + var contract = this.contractList.Single(c => c.operatorName == "Safaricom"); + (Guid productId, String productName, Decimal? productValue) product = contractProducts.Single(cp => cp.Key == contract.contractId).Value.First(); + for (int i = 0; i < transactionCount.Value; i++) { + Transaction transaction = await helper.BuildTransactionX(todaysDateTime.AddHours(-1), merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", product.productValue); + if (transactionsDictionary.ContainsKey(transactionCount.Key) == false) { + transactionsDictionary.Add(transactionCount.Key, new List()); + } - [Fact] - public async Task FactTransactionsControllerController_MerchantPerformance_AllMerchants_SalesReturned() - { + transactionsDictionary[transactionCount.Key].Add(transaction); + } + } - var todaysTransactions = new List(); - var comparisonDateTransactions = new List(); + await this.helper.AddTransactionsX(transactionsDictionary.Values.SelectMany(t => t).ToList()); + + await helper.RunTodaysTransactionsSummaryProcessing(todaysDateTime.Date); + var result = await ApiClient.GetTopBottomMerchantData(string.Empty, Guid.NewGuid(), DataTransferObjects.TopBottom.Top, 3, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + List? topBottomMerchantData = result.Data; + topBottomMerchantData.ShouldNotBeNull(); + topBottomMerchantData[0].MerchantName.ShouldBe("Test Merchant 3"); + topBottomMerchantData[0].SalesValue.ShouldBe(transactionsDictionary["Test Merchant 3"].Sum(p => p.TransactionAmount)); + topBottomMerchantData[1].MerchantName.ShouldBe("Test Merchant 1"); + topBottomMerchantData[1].SalesValue.ShouldBe(transactionsDictionary["Test Merchant 1"].Sum(p => p.TransactionAmount)); + topBottomMerchantData[2].MerchantName.ShouldBe("Test Merchant 2"); + topBottomMerchantData[2].SalesValue.ShouldBe(transactionsDictionary["Test Merchant 2"].Sum(p => p.TransactionAmount)); + } - DateTime todaysDateTime = DateTime.Now; - DateTime comparisonDate = DateTime.Now.AddDays(-1).AddHours(-1); + [Fact] + public async Task FactTransactionsControllerController_MerchantPerformance_AllMerchants_SalesReturned() { - Dictionary transactionCounts = new(){ - { "Test Merchant 1", 15 }, - { "Test Merchant 2", 18 }, - { "Test Merchant 3", 9 }, - { "Test Merchant 4", 3 }, - }; + var todaysTransactions = new List(); + var comparisonDateTransactions = new List(); - // Todays sales - foreach (string merchantName in merchantsList) - { - foreach ((string contract, string operatorname) contract in contractList) - { - var productList = contractProducts.Single(cp => cp.Key == contract.contract).Value; - foreach (string product in productList) - { - var transactionCount = transactionCounts.Single(m => m.Key == merchantName).Value; - for (int i = 0; i < transactionCount; i++) - { - Transaction transaction = await helper.AddTransaction(todaysDateTime.AddHours(-1), merchantName, contract.contract, product, "0000"); - todaysTransactions.Add(transaction); + DateTime todaysDateTime = DateTime.Now; + DateTime comparisonDate = DateTime.Now.AddDays(-1).AddHours(-1); + + Dictionary transactionCounts = new() { + { "Test Merchant 1", 15 }, { "Test Merchant 2", 18 }, { "Test Merchant 3", 9 }, { "Test Merchant 4", 3 }, + }; + + // Todays sales + foreach (var merchant in merchantsList) { + foreach (var contract in contractList) { + var productList = contractProducts.Single(cp => cp.Key == contract.contractId).Value; + foreach ((Guid productId, String productName, Decimal? productValue) product in productList) { + var transactionCount = transactionCounts.Single(m => m.Key == merchant.Name).Value; + for (int i = 0; i < transactionCount; i++) { + Transaction transaction = await helper.BuildTransactionX(todaysDateTime.AddHours(-1), merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", product.productValue); + todaysTransactions.Add(transaction); + } } } } - } - // Comparison Date sales - foreach (string merchantName in merchantsList) - { - foreach ((string contract, string operatorname) contract in contractList) - { - var productList = contractProducts.Single(cp => cp.Key == contract.contract).Value; - foreach (string product in productList) - { - var transactionCount = transactionCounts.Single(m => m.Key == merchantName).Value; - for (int i = 0; i < transactionCount; i++) - { - Transaction transaction = await helper.AddTransaction(comparisonDate, merchantName, contract.contract, product, "0000"); - comparisonDateTransactions.Add(transaction); + await this.helper.AddTransactionsX(todaysTransactions); + + // Comparison Date sales + foreach (var merchant in merchantsList) { + foreach (var contract in contractList) { + var productList = contractProducts.Single(cp => cp.Key == contract.contractId).Value; + foreach ((Guid productId, String productName, Decimal? productValue) product in productList) { + var transactionCount = transactionCounts.Single(m => m.Key == merchant.Name).Value; + for (int i = 0; i < transactionCount; i++) { + Transaction transaction = await helper.BuildTransactionX(comparisonDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", product.productValue); + comparisonDateTransactions.Add(transaction); + } } } } - } - await helper.RunTodaysTransactionsSummaryProcessing(comparisonDate.Date); - await helper.RunHistoricTransactionsSummaryProcessing(comparisonDate.Date); - await helper.RunTodaysTransactionsSummaryProcessing(todaysDateTime.Date); + await this.helper.AddTransactionsX(comparisonDateTransactions); - var result= await ApiClient.GetMerchantPerformance(string.Empty, Guid.NewGuid(), comparisonDate, new List(), CancellationToken.None); - result.IsSuccess.ShouldBeTrue(); - TodaysSales? todaysSales = result.Data; - todaysSales.ShouldNotBeNull(); - todaysSales.ComparisonSalesCount.ShouldBe(comparisonDateTransactions.Count); - todaysSales.ComparisonSalesValue.ShouldBe(comparisonDateTransactions.Sum(c => c.TransactionAmount)); + await helper.RunTodaysTransactionsSummaryProcessing(comparisonDate.Date); + await helper.RunHistoricTransactionsSummaryProcessing(comparisonDate.Date); + await helper.RunTodaysTransactionsSummaryProcessing(todaysDateTime.Date); - todaysSales.TodaysSalesCount.ShouldBe(todaysTransactions.Count); - todaysSales.ComparisonSalesValue.ShouldBe(comparisonDateTransactions.Sum(c => c.TransactionAmount)); - } + var result = await ApiClient.GetMerchantPerformance(string.Empty, Guid.NewGuid(), comparisonDate, new List(), CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + DataTransferObjects.TodaysSales? todaysSales = result.Data; + todaysSales.ShouldNotBeNull(); + todaysSales.ComparisonSalesCount.ShouldBe(comparisonDateTransactions.Count); + todaysSales.ComparisonSalesValue.ShouldBe(comparisonDateTransactions.Sum(c => c.TransactionAmount)); - [Fact] - public async Task FactTransactionsControllerController_MerchantPerformance_SingleMerchant_SalesReturned() - { + todaysSales.TodaysSalesCount.ShouldBe(todaysTransactions.Count); + todaysSales.ComparisonSalesValue.ShouldBe(comparisonDateTransactions.Sum(c => c.TransactionAmount)); + } - var todaysTransactions = new List(); - var comparisonDateTransactions = new List(); + [Fact] + public async Task FactTransactionsControllerController_MerchantPerformance_SingleMerchant_SalesReturned() { - DateTime todaysDateTime = DateTime.Now; - DateTime comparisonDate = DateTime.Now.AddDays(-1).AddHours(-1); + var todaysTransactions = new List(); + var comparisonDateTransactions = new List(); - Dictionary transactionCounts = new(){ - { "Test Merchant 1", 15 }, - { "Test Merchant 2", 18 }, - { "Test Merchant 3", 9 }, - { "Test Merchant 4", 3 }, - }; + DateTime todaysDateTime = DateTime.Now; + DateTime comparisonDate = DateTime.Now.AddDays(-1).AddHours(-1); - // Todays sales - foreach (string merchantName in merchantsList) - { - foreach ((string contract, string operatorname) contract in contractList) - { - var productList = contractProducts.Single(cp => cp.Key == contract.contract).Value; - foreach (string product in productList) - { - var transactionCount = transactionCounts.Single(m => m.Key == merchantName).Value; - for (int i = 0; i < transactionCount; i++) - { - Transaction transaction = await helper.AddTransaction(todaysDateTime.AddHours(-1), merchantName, contract.contract, product, "0000"); - todaysTransactions.Add(transaction); + Dictionary transactionCounts = new() { + { "Test Merchant 1", 15 }, { "Test Merchant 2", 18 }, { "Test Merchant 3", 9 }, { "Test Merchant 4", 3 }, + }; + + // Todays sales + foreach (var merchant in merchantsList) { + foreach (var contract in contractList) { + var productList = contractProducts.Single(cp => cp.Key == contract.contractId).Value; + foreach ((Guid productId, String productName, Decimal? productValue) product in productList) { + var transactionCount = transactionCounts.Single(m => m.Key == merchant.Name).Value; + for (int i = 0; i < transactionCount; i++) { + Transaction transaction = await helper.BuildTransactionX(todaysDateTime.AddHours(-1), merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", product.productValue); + todaysTransactions.Add(transaction); + } } } } - } - // Comparison Date sales - foreach (string merchantName in merchantsList) - { - foreach ((string contract, string operatorname) contract in contractList) - { - var productList = contractProducts.Single(cp => cp.Key == contract.contract).Value; - foreach (string product in productList) - { - var transactionCount = transactionCounts.Single(m => m.Key == merchantName).Value; - for (int i = 0; i < transactionCount; i++) - { - Transaction transaction = await helper.AddTransaction(comparisonDate, merchantName, contract.contract, product, "0000"); - comparisonDateTransactions.Add(transaction); + await this.helper.AddTransactionsX(todaysTransactions); + + // Comparison Date sales + foreach (var merchant in merchantsList) { + foreach (var contract in contractList) { + var productList = contractProducts.Single(cp => cp.Key == contract.contractId).Value; + foreach ((Guid productId, String productName, Decimal? productValue) product in productList) { + var transactionCount = transactionCounts.Single(m => m.Key == merchant.Name).Value; + for (int i = 0; i < transactionCount; i++) { + Transaction transaction = await helper.BuildTransactionX(comparisonDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", product.productValue); + comparisonDateTransactions.Add(transaction); + } } } } - } - List merchantFilterList = new List{ - 2 - }; + await this.helper.AddTransactionsX(comparisonDateTransactions); - var merchantIdsForVerify = await this.context.Merchants.Where(m => m.MerchantReportingId == 2).Select(m => m.MerchantId) - .ToListAsync(CancellationToken.None); + List merchantFilterList = new List { 2 }; - string serializedArray = string.Join(",", merchantFilterList); + var merchantIdsForVerify = await this.context.Merchants.Where(m => m.MerchantReportingId == 2).Select(m => m.MerchantId).ToListAsync(CancellationToken.None); - await helper.RunTodaysTransactionsSummaryProcessing(comparisonDate.Date); - await helper.RunHistoricTransactionsSummaryProcessing(comparisonDate.Date); - await helper.RunTodaysTransactionsSummaryProcessing(todaysDateTime.Date); + string serializedArray = string.Join(",", merchantFilterList); - var result = await ApiClient.GetMerchantPerformance(string.Empty, Guid.NewGuid(), comparisonDate, merchantFilterList, CancellationToken.None); - result.IsSuccess.ShouldBeTrue(); - TodaysSales? todaysSales = result.Data; - todaysSales.ShouldNotBeNull(); - todaysSales.ComparisonSalesCount.ShouldBe(comparisonDateTransactions.Count(c => merchantIdsForVerify.Contains(c.MerchantId))); - todaysSales.ComparisonSalesValue.ShouldBe(comparisonDateTransactions.Where(c => merchantIdsForVerify.Contains(c.MerchantId)).Sum(c => c.TransactionAmount)); + await helper.RunTodaysTransactionsSummaryProcessing(comparisonDate.Date); + await helper.RunHistoricTransactionsSummaryProcessing(comparisonDate.Date); + await helper.RunTodaysTransactionsSummaryProcessing(todaysDateTime.Date); - todaysSales.TodaysSalesCount.ShouldBe(todaysTransactions.Count(c => merchantIdsForVerify.Contains(c.MerchantId))); - todaysSales.ComparisonSalesValue.ShouldBe(comparisonDateTransactions.Where(c => merchantIdsForVerify.Contains(c.MerchantId)).Sum(c => c.TransactionAmount)); - } + var result = await ApiClient.GetMerchantPerformance(string.Empty, Guid.NewGuid(), comparisonDate, merchantFilterList, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + DataTransferObjects.TodaysSales? todaysSales = result.Data; + todaysSales.ShouldNotBeNull(); + todaysSales.ComparisonSalesCount.ShouldBe(comparisonDateTransactions.Count(c => merchantIdsForVerify.Contains(c.MerchantId))); + todaysSales.ComparisonSalesValue.ShouldBe(comparisonDateTransactions.Where(c => merchantIdsForVerify.Contains(c.MerchantId)).Sum(c => c.TransactionAmount)); - [Fact] - public async Task FactTransactionsControllerController_ProductPerformance_AllProducts_SalesReturned() - { - var todaysTransactions = new List(); - var comparisonDateTransactions = new List(); + todaysSales.TodaysSalesCount.ShouldBe(todaysTransactions.Count(c => merchantIdsForVerify.Contains(c.MerchantId))); + todaysSales.ComparisonSalesValue.ShouldBe(comparisonDateTransactions.Where(c => merchantIdsForVerify.Contains(c.MerchantId)).Sum(c => c.TransactionAmount)); + } - DateTime todaysDateTime = DateTime.Now; - DateTime comparisonDate = DateTime.Now.AddDays(-1).AddHours(-1); + } - string merchantName = merchantsList.First(); - (string contract, string operatorname) contract = contractList.Single(c => c.operatorname == "Safaricom"); - List productList = contractProducts.Single(cp => cp.Key == contract.contract).Value; - - Dictionary transactionCounts = new(){ - { "200 KES Topup", 25 }, //5000 - { "100 KES Topup", 15 }, // 1500 - { "50 KES Topup", 45 }, // 2250 - { "Custom", 8 } // 600 - }; - foreach (string product in productList) - { - int transactionCount = transactionCounts.Single(m => m.Key == product).Value; - for (int i = 0; i < transactionCount; i++) - { - Transaction transaction = await helper.AddTransaction(todaysDateTime.AddHours(-1), merchantName, contract.contract, product, "0000"); - todaysTransactions.Add(transaction); - } - } + public class FactTransactionsControllerTests_SalesTests : FactTransactionsControllerTestsBase { + + public FactTransactionsControllerTests_SalesTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) { - foreach (string product in productList) - { - int transactionCount = transactionCounts.Single(m => m.Key == product).Value; - for (int i = 0; i < transactionCount; i++) - { - Transaction transaction = await helper.AddTransaction(comparisonDate, merchantName, contract.contract, product, "0000"); - comparisonDateTransactions.Add(transaction); - } } - await helper.RunTodaysTransactionsSummaryProcessing(comparisonDate.Date); - await helper.RunHistoricTransactionsSummaryProcessing(comparisonDate.Date); - await helper.RunTodaysTransactionsSummaryProcessing(todaysDateTime.Date); + #region Todays Sales Tests - var result = await ApiClient.GetProductPerformance(string.Empty, Guid.NewGuid(), comparisonDate, new List(), CancellationToken.None); - result.IsSuccess.ShouldBeTrue(); - TodaysSales? todaysSales = result.Data; - todaysSales.ShouldNotBeNull(); - todaysSales.ComparisonSalesCount.ShouldBe(comparisonDateTransactions.Count); - todaysSales.ComparisonSalesValue.ShouldBe(comparisonDateTransactions.Sum(c => c.TransactionAmount)); - - todaysSales.TodaysSalesCount.ShouldBe(todaysTransactions.Count); - todaysSales.ComparisonSalesValue.ShouldBe(comparisonDateTransactions.Sum(c => c.TransactionAmount)); - } + [Fact] + public async Task FactTransactionsControllerController_TodaysSales_SalesReturned() { + List? todaysTransactions = new List(); + List comparisonDateTransactions = new List(); - [Fact] - public async Task FactTransactionsControllerController_ProductPerformance_SingleProduct_SalesReturned() - { - var todaysTransactions = new List(); - var comparisonDateTransactions = new List(); + DateTime todaysDateTime = DateTime.Now; + DateTime comparisonDate = DateTime.Now.AddDays(-1).AddHours(-1); - DateTime todaysDateTime = DateTime.Now; - DateTime comparisonDate = DateTime.Now.AddDays(-1).AddHours(-1); + Dictionary transactionCounts = new() { { "Test Merchant 1", 15 }, { "Test Merchant 2", 18 }, { "Test Merchant 3", 9 }, { "Test Merchant 4", 0 } }; - string merchantName = merchantsList.First(); - (string contract, string operatorname) contract = contractList.Single(c => c.operatorname == "Safaricom"); - List productList = contractProducts.Single(cp => cp.Key == contract.contract).Value; - - Dictionary transactionCounts = new(){ - { "200 KES Topup", 25 }, //5000 - { "100 KES Topup", 15 }, // 1500 - { "50 KES Topup", 45 }, // 2250 - { "Custom", 8 } // 600 - }; - foreach (string product in productList) - { - int transactionCount = transactionCounts.Single(m => m.Key == product).Value; - for (int i = 0; i < transactionCount; i++) - { - Transaction transaction = await helper.AddTransaction(todaysDateTime.AddHours(-1), merchantName, contract.contract, product, "0000"); - todaysTransactions.Add(transaction); + foreach (var merchant in merchantsList) { + foreach (var contract in contractList) { + var productList = contractProducts.Single(cp => cp.Key == contract.contractId).Value; + foreach ((Guid productId, String productName, Decimal? productValue) product in productList) { + var transactionCount = transactionCounts.Single(m => m.Key == merchant.Name).Value; + for (int i = 0; i < transactionCount; i++) { + Transaction transaction = await helper.BuildTransactionX(todaysDateTime.AddHours(-1), merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", product.productValue); + todaysTransactions.Add(transaction); + } + } + } } - } - foreach (string product in productList) - { - int transactionCount = transactionCounts.Single(m => m.Key == product).Value; - for (int i = 0; i < transactionCount; i++) - { - Transaction transaction = await helper.AddTransaction(comparisonDate, merchantName, contract.contract, product, "0000"); - comparisonDateTransactions.Add(transaction); + await this.helper.AddTransactionsX(todaysTransactions); + + // Comparison Date sales + foreach (var merchant in merchantsList) { + foreach (var contract in contractList) { + var productList = contractProducts.Single(cp => cp.Key == contract.contractId).Value; + foreach ((Guid productId, String productName, Decimal? productValue) product in productList) { + var transactionCount = transactionCounts.Single(m => m.Key == merchant.Name).Value; + for (int i = 0; i < transactionCount; i++) { + Transaction transaction = await helper.BuildTransactionX(comparisonDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", product.productValue); + comparisonDateTransactions.Add(transaction); + } + } + } } - } - List productFilterList = new List - { - 2 - }; + await this.helper.AddTransactionsX(comparisonDateTransactions); - var productIdsForVerify = await context.ContractProducts.Where(cp => cp.ContractProductReportingId == 2) - .Select(cp => cp.ContractProductId).ToListAsync(CancellationToken.None); + await helper.RunTodaysTransactionsSummaryProcessing(comparisonDate.Date); + await helper.RunHistoricTransactionsSummaryProcessing(comparisonDate.Date); + await helper.RunTodaysTransactionsSummaryProcessing(todaysDateTime.Date); - await helper.RunTodaysTransactionsSummaryProcessing(comparisonDate.Date); - await helper.RunHistoricTransactionsSummaryProcessing(comparisonDate.Date); - await helper.RunTodaysTransactionsSummaryProcessing(todaysDateTime.Date); + var result = await ApiClient.GetTodaysSales(string.Empty, Guid.NewGuid(), 0, 0, comparisonDate, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + var todaysSales = result.Data; - string serializedArray = string.Join(",", productFilterList); - - var result = await ApiClient.GetProductPerformance(string.Empty, Guid.NewGuid(), comparisonDate, productFilterList, CancellationToken.None); - result.IsSuccess.ShouldBeTrue(); - TodaysSales? todaysSales = result.Data; - todaysSales.ShouldNotBeNull(); - todaysSales.ComparisonSalesCount.ShouldBe(comparisonDateTransactions.Count(c => productIdsForVerify.Contains(c.ContractProductId))); - todaysSales.ComparisonSalesValue.ShouldBe(comparisonDateTransactions.Where(c => productIdsForVerify.Contains(c.ContractProductId)).Sum(c => c.TransactionAmount)); + todaysSales.ShouldNotBeNull(); + todaysSales.ComparisonSalesCount.ShouldBe(comparisonDateTransactions.Count); + todaysSales.ComparisonSalesValue.ShouldBe(comparisonDateTransactions.Sum(c => c.TransactionAmount)); - todaysSales.TodaysSalesCount.ShouldBe(todaysTransactions.Count(c => productIdsForVerify.Contains(c.ContractProductId))); - todaysSales.ComparisonSalesValue.ShouldBe(comparisonDateTransactions.Where(c => productIdsForVerify.Contains(c.ContractProductId)).Sum(c => c.TransactionAmount)); - } + todaysSales.TodaysSalesCount.ShouldBe(todaysTransactions.Count); + todaysSales.ComparisonSalesValue.ShouldBe(comparisonDateTransactions.Sum(c => c.TransactionAmount)); + } - [Fact] - public async Task FactTransactionsControllerController_ProductPerformance_MultipleProducts_SalesReturned() - { - var todaysTransactions = new List(); - var comparisonDateTransactions = new List(); + [Fact] + public async Task FactTransactionsControllerController_TodaysSales_OperatorFilter_SalesReturned() { + List todaysTransactions = new(); + List comparisonDateTransactions = new(); - DateTime todaysDateTime = DateTime.Now; - DateTime comparisonDate = DateTime.Now.AddDays(-1).AddHours(-1); + DateTime todaysDateTime = DateTime.Now; + DateTime comparisonDate = DateTime.Now.AddDays(-1).AddHours(-1); - string merchantName = merchantsList.First(); - (string contract, string operatorname) contract = contractList.Single(c => c.operatorname == "Safaricom"); - List productList = contractProducts.Single(cp => cp.Key == contract.contract).Value; - - Dictionary transactionCounts = new(){ - { "200 KES Topup", 25 }, //5000 - { "100 KES Topup", 15 }, // 1500 - { "50 KES Topup", 45 }, // 2250 - { "Custom", 8 } // 600 - }; - foreach (string product in productList) - { - int transactionCount = transactionCounts.Single(m => m.Key == product).Value; - for (int i = 0; i < transactionCount; i++) - { - Transaction transaction = await helper.AddTransaction(todaysDateTime.AddHours(-1), merchantName, contract.contract, product, "0000"); - todaysTransactions.Add(transaction); - } - } + Dictionary transactionCounts = new() { { "Test Merchant 1", 15 }, { "Test Merchant 2", 18 }, { "Test Merchant 3", 9 }, { "Test Merchant 4", 0 } }; - foreach (string product in productList) - { - int transactionCount = transactionCounts.Single(m => m.Key == product).Value; - for (int i = 0; i < transactionCount; i++) - { - Transaction transaction = await helper.AddTransaction(comparisonDate, merchantName, contract.contract, product, "0000"); - comparisonDateTransactions.Add(transaction); + // Todays sales + foreach (var merchant in merchantsList) { + foreach (var contract in contractList) { + var productList = contractProducts.Single(cp => cp.Key == contract.contractId).Value; + foreach ((Guid productId, String productName, Decimal? productValue) product in productList) { + var transactionCount = transactionCounts.Single(m => m.Key == merchant.Name).Value; + for (int i = 0; i < transactionCount; i++) { + Transaction transaction = await helper.BuildTransactionX(todaysDateTime.AddHours(-1), merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", product.productValue); + todaysTransactions.Add(transaction); + } + } + } } - } - List productFilterList = new List{ - 2, - 3 - }; - var productIdsForVerify = await context.ContractProducts.Where(cp => cp.ContractProductReportingId >= 2 && cp.ContractProductReportingId <= 3) - .Select(cp => cp.ContractProductId).ToListAsync(CancellationToken.None); + await this.helper.AddTransactionsX(todaysTransactions); - string serializedArray = string.Join(",", productFilterList); + // Comparison Date sales + foreach (var merchant in merchantsList) { + foreach (var contract in contractList) { + var productList = contractProducts.Single(cp => cp.Key == contract.contractId).Value; + foreach ((Guid productId, String productName, Decimal? productValue) product in productList) { + var transactionCount = transactionCounts.Single(m => m.Key == merchant.Name).Value; + for (int i = 0; i < transactionCount; i++) { + Transaction transaction = await helper.BuildTransactionX(comparisonDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", product.productValue); + comparisonDateTransactions.Add(transaction); + } + } + } + } - await helper.RunTodaysTransactionsSummaryProcessing(comparisonDate.Date); - await helper.RunHistoricTransactionsSummaryProcessing(comparisonDate.Date); - await helper.RunTodaysTransactionsSummaryProcessing(todaysDateTime.Date); - - var result = await ApiClient.GetProductPerformance(string.Empty, Guid.NewGuid(), comparisonDate, productFilterList, CancellationToken.None); - result.IsSuccess.ShouldBeTrue(); - TodaysSales? todaysSales = result.Data; - todaysSales.ShouldNotBeNull(); - todaysSales.ComparisonSalesCount.ShouldBe(comparisonDateTransactions.Count(c => productIdsForVerify.Contains(c.ContractProductId))); - todaysSales.ComparisonSalesValue.ShouldBe(comparisonDateTransactions.Where(c => productIdsForVerify.Contains(c.ContractProductId)).Sum(c => c.TransactionAmount)); + await this.helper.AddTransactionsX(comparisonDateTransactions); - todaysSales.TodaysSalesCount.ShouldBe(todaysTransactions.Count(c => productIdsForVerify.Contains(c.ContractProductId))); - todaysSales.ComparisonSalesValue.ShouldBe(comparisonDateTransactions.Where(c => productIdsForVerify.Contains(c.ContractProductId)).Sum(c => c.TransactionAmount)); - } + await helper.RunTodaysTransactionsSummaryProcessing(comparisonDate.Date); + await helper.RunHistoricTransactionsSummaryProcessing(comparisonDate.Date); + await helper.RunTodaysTransactionsSummaryProcessing(todaysDateTime.Date); - [Fact] - public async Task FactTransactionsControllerController_OperatorPerformance_SingleOperator_SalesReturned() - { - var todaysTransactions = new List(); - var comparisonDateTransactions = new List(); + var result = await ApiClient.GetTodaysSales(string.Empty, Guid.NewGuid(), 0, 1, comparisonDate, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + var todaysSales = result.Data; - DateTime todaysDateTime = DateTime.Now; - DateTime comparisonDate = DateTime.Now.AddDays(-1).AddHours(-1); + todaysSales.ShouldNotBeNull(); - Dictionary transactionCounts = new(){ - { "Safaricom", 25 }, // 5000 - { "Voucher", 15 }, // 150 - { "PataPawa PostPay", 45 }, // 3375 - { "PataPawa PrePay", 8 } // 600 - }; + var operatorId = await this.helper.GetOperatorId(1, CancellationToken.None); + todaysSales.ComparisonSalesCount.ShouldBe(comparisonDateTransactions.Count(c => c.OperatorId == operatorId)); + todaysSales.ComparisonSalesValue.ShouldBe(comparisonDateTransactions.Where(c => c.OperatorId == operatorId).Sum(c => c.TransactionAmount)); - string merchantName = merchantsList.First(); - foreach (KeyValuePair transactionCount in transactionCounts) - { - var contract = contractList.Single(s => s.operatorname == transactionCount.Key); - var products = contractProducts.Single(p => p.Key == contract.contract); - var productname = products.Value.First(); - for (int i = 0; i < transactionCount.Value; i++) - { - Transaction transaction = await helper.AddTransaction(todaysDateTime.AddHours(-1), merchantName, contract.contract, productname, "0000"); - todaysTransactions.Add(transaction); - } + todaysSales.TodaysSalesCount.ShouldBe(todaysTransactions.Count(c => c.OperatorId == operatorId)); + todaysSales.ComparisonSalesValue.ShouldBe(comparisonDateTransactions.Where(c => c.OperatorId == operatorId).Sum(c => c.TransactionAmount)); } - foreach (KeyValuePair transactionCount in transactionCounts) - { - var contract = contractList.Single(s => s.operatorname == transactionCount.Key); - var products = contractProducts.Single(p => p.Key == contract.contract); - var productname = products.Value.First(); - for (int i = 0; i < transactionCount.Value; i++) - { - Transaction transaction = await helper.AddTransaction(comparisonDate.AddHours(-1), merchantName, contract.contract, productname, "0000"); - comparisonDateTransactions.Add(transaction); - } - } + [Fact] + public async Task FactTransactionsControllerController_TodaysSales_MerchantFilter_SalesReturned() { + List todaysTransactions = new(); + List comparisonDateTransactions = new(); - List operatorFilterList = new List{ - 2 - }; - var operatorIdsForVerify = await context.Operators.Where(cp => cp.OperatorReportingId == 2) - .Select(cp => cp.OperatorId).ToListAsync(CancellationToken.None); + DateTime todaysDateTime = DateTime.Now; + DateTime comparisonDate = DateTime.Now.AddDays(-1).AddHours(-1); + Dictionary transactionCounts = new() { { "Test Merchant 1", 15 }, { "Test Merchant 2", 18 }, { "Test Merchant 3", 9 }, { "Test Merchant 4", 0 } }; - string serializedArray = string.Join(",", operatorFilterList); - await helper.RunTodaysTransactionsSummaryProcessing(comparisonDate.Date); - await helper.RunHistoricTransactionsSummaryProcessing(comparisonDate.Date); - await helper.RunTodaysTransactionsSummaryProcessing(todaysDateTime.Date); + foreach (var merchant in merchantsList) { + foreach (var contract in contractList) { + var productList = contractProducts.Single(cp => cp.Key == contract.contractId).Value; + foreach ((Guid productId, String productName, Decimal? productValue) product in productList) { + var transactionCount = transactionCounts.Single(m => m.Key == merchant.Name).Value; + for (int i = 0; i < transactionCount; i++) { + Transaction transaction = await helper.BuildTransactionX(todaysDateTime.AddHours(-1), merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", product.productValue); + todaysTransactions.Add(transaction); + } + } + } + } - var result = await ApiClient.GetOperatorPerformance(string.Empty, Guid.NewGuid(), comparisonDate, operatorFilterList, CancellationToken.None); - result.IsSuccess.ShouldBeTrue(); - TodaysSales? todaysSales = result.Data; - todaysSales.ShouldNotBeNull(); - todaysSales.ComparisonSalesCount.ShouldBe(comparisonDateTransactions.Count(c => operatorIdsForVerify.Contains(c.OperatorId))); - todaysSales.ComparisonSalesValue.ShouldBe(comparisonDateTransactions.Where(c => operatorIdsForVerify.Contains(c.OperatorId)).Sum(c => c.TransactionAmount)); + await this.helper.AddTransactionsX(todaysTransactions); - todaysSales.TodaysSalesCount.ShouldBe(todaysTransactions.Count(c => operatorIdsForVerify.Contains(c.OperatorId))); - todaysSales.ComparisonSalesValue.ShouldBe(comparisonDateTransactions.Where(c => operatorIdsForVerify.Contains(c.OperatorId)).Sum(c => c.TransactionAmount)); - } - [Fact] - public async Task FactTransactionsControllerController_OperatorPerformance_MultipleOperators_SalesReturned() - { - var todaysTransactions = new List(); - var comparisonDateTransactions = new List(); + foreach (var merchant in merchantsList) { + foreach (var contract in contractList) { + var productList = contractProducts.Single(cp => cp.Key == contract.contractId).Value; + foreach ((Guid productId, String productName, Decimal? productValue) product in productList) { + var transactionCount = transactionCounts.Single(m => m.Key == merchant.Name).Value; + for (int i = 0; i < transactionCount; i++) { + Transaction transaction = await helper.BuildTransactionX(comparisonDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", product.productValue); + comparisonDateTransactions.Add(transaction); + } + } + } + } - DateTime todaysDateTime = DateTime.Now; - DateTime comparisonDate = DateTime.Now.AddDays(-1).AddHours(-1); + await this.helper.AddTransactionsX(comparisonDateTransactions); - Dictionary transactionCounts = new(){ - { "Safaricom", 25 }, // 5000 - { "Voucher", 15 }, // 150 - { "PataPawa PostPay", 45 }, // 3375 - { "PataPawa PrePay", 8 } // 600 - }; + await helper.RunTodaysTransactionsSummaryProcessing(comparisonDate.Date); + await helper.RunHistoricTransactionsSummaryProcessing(comparisonDate.Date); + await helper.RunTodaysTransactionsSummaryProcessing(todaysDateTime.Date); - string merchantName = merchantsList.First(); - foreach (KeyValuePair transactionCount in transactionCounts) - { - var contract = contractList.Single(s => s.operatorname == transactionCount.Key); - var products = contractProducts.Single(p => p.Key == contract.contract); - var productname = products.Value.First(); - for (int i = 0; i < transactionCount.Value; i++) - { - Transaction transaction = await helper.AddTransaction(todaysDateTime.AddHours(-1), merchantName, contract.contract, productname, "0000"); - todaysTransactions.Add(transaction); - } - } + var result = await ApiClient.GetTodaysSales(string.Empty, Guid.NewGuid(), 1, 0, comparisonDate, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + var todaysSales = result.Data; - foreach (KeyValuePair transactionCount in transactionCounts) - { - var contract = contractList.Single(s => s.operatorname == transactionCount.Key); - var products = contractProducts.Single(p => p.Key == contract.contract); - var productname = products.Value.First(); - for (int i = 0; i < transactionCount.Value; i++) - { - Transaction transaction = await helper.AddTransaction(comparisonDate.AddHours(-1), merchantName, contract.contract, productname, "0000"); - comparisonDateTransactions.Add(transaction); - } - } + todaysSales.ShouldNotBeNull(); - List operatorFilterList = new List{ - 2,3 - }; + var merchantId = await this.helper.GetMerchantId(1, CancellationToken.None); + todaysSales.ComparisonSalesCount.ShouldBe(comparisonDateTransactions.Count(c => c.MerchantId == merchantId)); + todaysSales.ComparisonSalesValue.ShouldBe(comparisonDateTransactions.Where(c => c.MerchantId == merchantId).Sum(c => c.TransactionAmount)); - var operatorIdsForVerify = await context.Operators.Where(cp => cp.OperatorReportingId >= 2 && cp.OperatorReportingId <= 3) - .Select(cp => cp.OperatorId).ToListAsync(CancellationToken.None); + todaysSales.TodaysSalesCount.ShouldBe(todaysTransactions.Count(c => c.MerchantId == merchantId)); + todaysSales.ComparisonSalesValue.ShouldBe(comparisonDateTransactions.Where(c => c.MerchantId == merchantId).Sum(c => c.TransactionAmount)); + } - string serializedArray = string.Join(",", operatorFilterList); + [Fact] + public async Task FactTransactionsControllerController_TodaysSalesCountByHour_SalesReturned() { + Stopwatch sw = Stopwatch.StartNew(); + + List todaysTransactions = new List(); + List comparisonDateTransactions = new List(); + + Dictionary transactionCounts = new() { { "Test Merchant 1", 3 }, { "Test Merchant 2", 6 }, { "Test Merchant 3", 2 }, { "Test Merchant 4", 0 } }; + + // TODO: make counts dynamic + DateTime todaysDateTime = DateTime.Now; + + for (int hour = 0; hour < 24; hour++) { + List localList = new List(); + DateTime date = new DateTime(todaysDateTime.Year, todaysDateTime.Month, todaysDateTime.Day, hour, 0, 0); + foreach (var merchant in merchantsList) { + foreach (var contract in contractList) { + var productList = contractProducts.Single(cp => cp.Key == contract.contractId).Value; + foreach ((Guid productId, String productName, Decimal? productValue) product in productList) { + var transactionCount = transactionCounts.Single(m => m.Key == merchant.Name).Value; + for (int i = 0; i < transactionCount; i++) { + Transaction transaction = await helper.BuildTransactionX(todaysDateTime.AddHours(-1), merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", product.productValue); + todaysTransactions.Add(transaction); + } + } + } + } - await helper.RunTodaysTransactionsSummaryProcessing(comparisonDate.Date); - await helper.RunHistoricTransactionsSummaryProcessing(comparisonDate.Date); - await helper.RunTodaysTransactionsSummaryProcessing(todaysDateTime.Date); + todaysTransactions.AddRange(localList); + } - var result = await ApiClient.GetOperatorPerformance(string.Empty, Guid.NewGuid(), comparisonDate, operatorFilterList, CancellationToken.None); - result.IsSuccess.ShouldBeTrue(); - TodaysSales? todaysSales = result.Data; - todaysSales.ShouldNotBeNull(); - todaysSales.ComparisonSalesCount.ShouldBe(comparisonDateTransactions.Count(c => operatorIdsForVerify.Contains(c.OperatorId))); - todaysSales.ComparisonSalesValue.ShouldBe(comparisonDateTransactions.Where(c => operatorIdsForVerify.Contains(c.OperatorId)).Sum(c => c.TransactionAmount)); + await this.helper.AddTransactionsX(todaysTransactions); + + sw.Stop(); + this.TestOutputHelper.WriteLine($"Setup Todays Txns {sw.ElapsedMilliseconds}ms"); + sw.Restart(); + + DateTime comparisonDate = todaysDateTime.AddDays(-1); + for (int hour = 0; hour < 24; hour++) { + List localList = new List(); + DateTime date = new DateTime(comparisonDate.Year, comparisonDate.Month, comparisonDate.Day, hour, 0, 0); + foreach (var merchant in merchantsList) { + foreach (var contract in contractList) { + var productList = contractProducts.Single(cp => cp.Key == contract.contractId).Value; + foreach ((Guid productId, String productName, Decimal? productValue) product in productList) { + var transactionCount = transactionCounts.Single(m => m.Key == merchant.Name).Value; + for (int i = 0; i < transactionCount; i++) { + Transaction transaction = await helper.BuildTransactionX(comparisonDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", product.productValue); + comparisonDateTransactions.Add(transaction); + } + } + } + } - todaysSales.TodaysSalesCount.ShouldBe(todaysTransactions.Count(c => operatorIdsForVerify.Contains(c.OperatorId))); - todaysSales.ComparisonSalesValue.ShouldBe(comparisonDateTransactions.Where(c => operatorIdsForVerify.Contains(c.OperatorId)).Sum(c => c.TransactionAmount)); - } + comparisonDateTransactions.AddRange(localList); - [Fact] - public async Task FactTransactionsControllerController_GetMerchantsTransactionKpis_SalesReturned() - { - DateTime todaysDateTime = DateTime.Now; + } - await ClearStandingData(); - - // Last Hour - await helper.AddMerchant("Test Estate", "Merchant 1", todaysDateTime.AddMinutes(-10)); - await helper.AddMerchant("Test Estate", "Merchant 2", todaysDateTime.AddMinutes(-10)); - await helper.AddMerchant("Test Estate", "Merchant 3", todaysDateTime.AddMinutes(-10)); - await helper.AddMerchant("Test Estate", "Merchant 4", todaysDateTime.AddMinutes(-10)); - - // Yesterday - await helper.AddMerchant("Test Estate", "Merchant 5", todaysDateTime.AddDays(-1)); - await helper.AddMerchant("Test Estate", "Merchant 6", todaysDateTime.AddDays(-1)); - await helper.AddMerchant("Test Estate", "Merchant 7", todaysDateTime.AddDays(-1)); - await helper.AddMerchant("Test Estate", "Merchant 8", todaysDateTime.AddDays(-1)); - await helper.AddMerchant("Test Estate", "Merchant 9", todaysDateTime.AddDays(-1)); - await helper.AddMerchant("Test Estate", "Merchant 10", todaysDateTime.AddDays(-1)); - - // 10 Days Ago - await helper.AddMerchant("Test Estate", "Merchant 11", todaysDateTime.AddDays(-10)); - await helper.AddMerchant("Test Estate", "Merchant 12", todaysDateTime.AddDays(-10)); - await helper.AddMerchant("Test Estate", "Merchant 13", todaysDateTime.AddDays(-10)); - await helper.AddMerchant("Test Estate", "Merchant 14", todaysDateTime.AddDays(-10)); - await helper.AddMerchant("Test Estate", "Merchant 15", todaysDateTime.AddDays(-10)); - await helper.AddMerchant("Test Estate", "Merchant 16", todaysDateTime.AddDays(-10)); - await helper.AddMerchant("Test Estate", "Merchant 17", todaysDateTime.AddDays(-10)); - await helper.AddMerchant("Test Estate", "Merchant 18", todaysDateTime.AddDays(-10)); - - var result = await ApiClient.GetMerchantKpi(string.Empty, Guid.NewGuid(), CancellationToken.None); - result.IsSuccess.ShouldBeTrue(); - MerchantKpi? merchantKpi = result.Data; - merchantKpi.ShouldNotBeNull(); - merchantKpi.MerchantsWithSaleInLastHour.ShouldBe(4); - merchantKpi.MerchantsWithNoSaleToday.ShouldBe(6); - merchantKpi.MerchantsWithNoSaleInLast7Days.ShouldBe(8); - } + await this.helper.AddTransactionsX(comparisonDateTransactions); - [Fact] - public async Task FactTransactionsController_TransactionSearch_NoAdditionalFiltering_TransactionReturned() - { + sw.Stop(); + this.TestOutputHelper.WriteLine($"Setup Comparison txns {sw.ElapsedMilliseconds}ms"); + sw.Restart(); - DateTime transactionDate = new DateTime(2024, 3, 19); - - // Add some transactions - await helper.AddTransaction(transactionDate, "Test Merchant 1", "Safaricom Contract", "200 KES Topup", "0000", transactionReportingId: 1); - await helper.AddTransaction(transactionDate, "Test Merchant 1", "Safaricom Contract", "100 KES Topup", "0000", transactionReportingId: 2); - await helper.AddTransaction(transactionDate, "Test Merchant 1", "Safaricom Contract", "200 KES Topup", "0000", transactionReportingId: 3); - await helper.AddTransaction(transactionDate, "Test Merchant 1", "Safaricom Contract", "100 KES Topup", "0000", transactionReportingId: 4); - await helper.AddTransaction(transactionDate, "Test Merchant 1", "Safaricom Contract", "200 KES Topup", "0000", transactionReportingId: 5); - await helper.AddTransaction(transactionDate, "Test Merchant 1", "Safaricom Contract", "100 KES Topup", "0000", transactionReportingId: 6); - await helper.AddTransaction(transactionDate, "Test Merchant 1", "Safaricom Contract", "200 KES Topup", "0000", transactionReportingId: 7); - await helper.AddTransaction(transactionDate, "Test Merchant 1", "Safaricom Contract", "100 KES Topup", "0000", transactionReportingId: 8); - await helper.AddTransaction(transactionDate, "Test Merchant 1", "Safaricom Contract", "200 KES Topup", "0000", transactionReportingId: 9); - await helper.AddTransaction(transactionDate, "Test Merchant 1", "Safaricom Contract", "100 KES Topup", "0000", transactionReportingId: 10); - - TransactionSearchRequest searchRequest = new TransactionSearchRequest - { - QueryDate = transactionDate - }; + await helper.RunTodaysTransactionsSummaryProcessing(comparisonDate.Date); + await helper.RunHistoricTransactionsSummaryProcessing(comparisonDate.Date); + await helper.RunTodaysTransactionsSummaryProcessing(todaysDateTime.Date); - // No Paging or Sorting - var result = await ApiClient.TransactionSearch(string.Empty, Guid.NewGuid(), searchRequest, null, null, null, null, CancellationToken.None); - result.IsSuccess.ShouldBeTrue(); - var searchResult = result.Data; - searchResult.Count.ShouldBe(10); - searchResult.Any(s => s.TransactionReportingId == 1).ShouldBeTrue(); - searchResult.Any(s => s.TransactionReportingId == 2).ShouldBeTrue(); - searchResult.Any(s => s.TransactionReportingId == 3).ShouldBeTrue(); - searchResult.Any(s => s.TransactionReportingId == 4).ShouldBeTrue(); - searchResult.Any(s => s.TransactionReportingId == 5).ShouldBeTrue(); - searchResult.Any(s => s.TransactionReportingId == 6).ShouldBeTrue(); - searchResult.Any(s => s.TransactionReportingId == 7).ShouldBeTrue(); - searchResult.Any(s => s.TransactionReportingId == 8).ShouldBeTrue(); - searchResult.Any(s => s.TransactionReportingId == 9).ShouldBeTrue(); - searchResult.Any(s => s.TransactionReportingId == 10).ShouldBeTrue(); - - - result = await ApiClient.TransactionSearch(string.Empty, Guid.NewGuid(), searchRequest, 1, 5, null, null, CancellationToken.None); - result.IsSuccess.ShouldBeTrue(); - searchResult = result.Data; - searchResult.Count.ShouldBe(5); - searchResult.Any(s => s.TransactionReportingId == 2).ShouldBeTrue(); - searchResult.Any(s => s.TransactionReportingId == 4).ShouldBeTrue(); - searchResult.Any(s => s.TransactionReportingId == 6).ShouldBeTrue(); - searchResult.Any(s => s.TransactionReportingId == 8).ShouldBeTrue(); - searchResult.Any(s => s.TransactionReportingId == 10).ShouldBeTrue(); - - result = await ApiClient.TransactionSearch(string.Empty, Guid.NewGuid(), searchRequest, 2, 5, null, null, CancellationToken.None); - result.IsSuccess.ShouldBeTrue(); - searchResult = result.Data; - searchResult.Count.ShouldBe(5); - searchResult.Any(s => s.TransactionReportingId == 1).ShouldBeTrue(); - searchResult.Any(s => s.TransactionReportingId == 3).ShouldBeTrue(); - searchResult.Any(s => s.TransactionReportingId == 5).ShouldBeTrue(); - searchResult.Any(s => s.TransactionReportingId == 7).ShouldBeTrue(); - searchResult.Any(s => s.TransactionReportingId == 9).ShouldBeTrue(); - } + sw.Stop(); + this.TestOutputHelper.WriteLine($"Setup Summaries {sw.ElapsedMilliseconds}ms"); + sw.Restart(); - [Fact] - public async Task FactTransactionsController_TransactionSearch_ValueRangeFiltering_TransactionReturned() - { + var result = await ApiClient.GetTodaysSalesCountByHour(string.Empty, Guid.NewGuid(), 0, 0, comparisonDate, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + var todaysSalesCountByHour = result.Data; - DateTime transactionDate = new DateTime(2024, 3, 19); - - // Add some transactions - await helper.AddTransaction(transactionDate, "Test Merchant 1", "Safaricom Contract", "Custom", "0000", 50, 1); - await helper.AddTransaction(transactionDate, "Test Merchant 1", "Safaricom Contract", "Custom", "0000", 123, 2); - await helper.AddTransaction(transactionDate, "Test Merchant 1", "Safaricom Contract", "Custom", "0000", 100, 3); - await helper.AddTransaction(transactionDate, "Test Merchant 1", "Safaricom Contract", "Custom", "0000", 101, 4); - await helper.AddTransaction(transactionDate, "Test Merchant 1", "Safaricom Contract", "Custom", "0000", 199, 5); - await helper.AddTransaction(transactionDate, "Test Merchant 1", "Safaricom Contract", "Custom", "0000", 150, 6); - await helper.AddTransaction(transactionDate, "Test Merchant 1", "Safaricom Contract", "Custom", "0000", 200, 7); - await helper.AddTransaction(transactionDate, "Test Merchant 1", "Safaricom Contract", "Custom", "0000", 201, 8); - await helper.AddTransaction(transactionDate, "Test Merchant 1", "Safaricom Contract", "Custom", "0000", 99, 9); - await helper.AddTransaction(transactionDate, "Test Merchant 1", "Safaricom Contract", "Custom", "0000", 111, 10); - - TransactionSearchRequest searchRequest = new TransactionSearchRequest - { - QueryDate = transactionDate, - ValueRange = new ValueRange - { - StartValue = 100, - EndValue = 200 + todaysSalesCountByHour.ShouldNotBeNull(); + foreach (DataTransferObjects.TodaysSalesCountByHour salesCountByHour in todaysSalesCountByHour) { + IEnumerable todayHour = todaysTransactions.Where(t => t.TransactionDateTime.Hour == salesCountByHour.Hour); + IEnumerable comparisonHour = comparisonDateTransactions.Where(t => t.TransactionDateTime.Hour == salesCountByHour.Hour); + salesCountByHour.ComparisonSalesCount.ShouldBe(comparisonHour.Count()); + salesCountByHour.TodaysSalesCount.ShouldBe(todayHour.Count()); } - }; + } - // No Paging - var result = await ApiClient.TransactionSearch(string.Empty, Guid.NewGuid(), searchRequest, null, null, null, null, CancellationToken.None); - result.IsSuccess.ShouldBeTrue(); - List searchResult = result.Data; - searchResult.Count.ShouldBe(7); - searchResult.Any(s => s.TransactionReportingId == 2).ShouldBeTrue(); - searchResult.Any(s => s.TransactionReportingId == 3).ShouldBeTrue(); - searchResult.Any(s => s.TransactionReportingId == 4).ShouldBeTrue(); - searchResult.Any(s => s.TransactionReportingId == 5).ShouldBeTrue(); - searchResult.Any(s => s.TransactionReportingId == 6).ShouldBeTrue(); - searchResult.Any(s => s.TransactionReportingId == 7).ShouldBeTrue(); - searchResult.Any(s => s.TransactionReportingId == 10).ShouldBeTrue(); - - result = await ApiClient.TransactionSearch(string.Empty, Guid.NewGuid(), searchRequest, 1, 3, null, null, CancellationToken.None); - result.IsSuccess.ShouldBeTrue(); - searchResult = result.Data; - searchResult.Count.ShouldBe(3); - searchResult.Any(s => s.TransactionReportingId == 10).ShouldBeTrue(); - searchResult.Any(s => s.TransactionReportingId == 3).ShouldBeTrue(); - searchResult.Any(s => s.TransactionReportingId == 4).ShouldBeTrue(); - - result = await ApiClient.TransactionSearch(string.Empty, Guid.NewGuid(), searchRequest, 2, 3, null, null, CancellationToken.None); - result.IsSuccess.ShouldBeTrue(); - searchResult = result.Data; - searchResult.Count.ShouldBe(3); - searchResult.Any(s => s.TransactionReportingId == 2).ShouldBeTrue(); - searchResult.Any(s => s.TransactionReportingId == 6).ShouldBeTrue(); - searchResult.Any(s => s.TransactionReportingId == 5).ShouldBeTrue(); + [Fact] + public async Task FactTransactionsControllerController_TodaysSalesValueByHour_SalesReturned() { + var todaysTransactions = new List(); + var comparisonDateTransactions = new List(); + + Dictionary transactionCounts = new() { { "Test Merchant 1", 3 }, { "Test Merchant 2", 6 }, { "Test Merchant 3", 2 }, { "Test Merchant 4", 0 } }; + + DateTime todaysDateTime = DateTime.Now; + + for (int hour = 0; hour < 24; hour++) { + DateTime date = new DateTime(todaysDateTime.Year, todaysDateTime.Month, todaysDateTime.Day, hour, 0, 0); + foreach (var merchant in merchantsList) { + foreach (var contract in contractList) { + var productList = contractProducts.Single(cp => cp.Key == contract.contractId).Value; + foreach ((Guid productId, String productName, Decimal? productValue) product in productList) { + var transactionCount = transactionCounts.Single(m => m.Key == merchant.Name).Value; + for (int i = 0; i < transactionCount; i++) { + Transaction transaction = await helper.BuildTransactionX(todaysDateTime.AddHours(-1), merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", product.productValue); + todaysTransactions.Add(transaction); + } + } + } + } + } - } + await this.helper.AddTransactionsX(todaysTransactions); + + DateTime comparisonDate = todaysDateTime.AddDays(-1); + for (int hour = 0; hour < 24; hour++) { + DateTime date = new DateTime(comparisonDate.Year, comparisonDate.Month, comparisonDate.Day, hour, 0, 0); + foreach (var merchant in merchantsList) { + foreach (var contract in contractList) { + var productList = contractProducts.Single(cp => cp.Key == contract.contractId).Value; + foreach ((Guid productId, String productName, Decimal? productValue) product in productList) { + var transactionCount = transactionCounts.Single(m => m.Key == merchant.Name).Value; + for (int i = 0; i < transactionCount; i++) { + Transaction transaction = await helper.BuildTransactionX(comparisonDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", product.productValue); + comparisonDateTransactions.Add(transaction); + } + } + } + } + } - [Fact] - public async Task FactTransactionsController_TransactionSearch_AuthCodeFiltering_TransactionReturned() - { + await this.helper.AddTransactionsX(comparisonDateTransactions); - DateTime transactionDate = new DateTime(2024, 3, 19); + await helper.RunTodaysTransactionsSummaryProcessing(comparisonDate.Date); + await helper.RunHistoricTransactionsSummaryProcessing(comparisonDate.Date); + await helper.RunTodaysTransactionsSummaryProcessing(todaysDateTime.Date); - await helper.AddTransaction(transactionDate, "Test Merchant 1", "Safaricom Contract", "200 KES Topup", "0000", transactionReportingId: 1, authCode: "AUTH231"); - await helper.AddTransaction(transactionDate, "Test Merchant 1", "Safaricom Contract", "200 KES Topup", "0000", transactionReportingId: 2, authCode: "AUTH1232"); - await helper.AddTransaction(transactionDate, "Test Merchant 1", "Safaricom Contract", "200 KES Topup", "0000", transactionReportingId: 3, authCode: "AUTH1233"); - await helper.AddTransaction(transactionDate, "Test Merchant 1", "Safaricom Contract", "200 KES Topup", "0000", transactionReportingId: 4, authCode: "AUTH1234"); - await helper.AddTransaction(transactionDate, "Test Merchant 1", "Safaricom Contract", "200 KES Topup", "0000", transactionReportingId: 5, authCode: "AUTH1235"); - await helper.AddTransaction(transactionDate, "Test Merchant 1", "Safaricom Contract", "200 KES Topup", "0000", transactionReportingId: 6, authCode: "AUTH1236"); - await helper.AddTransaction(transactionDate, "Test Merchant 1", "Safaricom Contract", "200 KES Topup", "0000", transactionReportingId: 7, authCode: "AUTH1237"); - await helper.AddTransaction(transactionDate, "Test Merchant 1", "Safaricom Contract", "200 KES Topup", "0000", transactionReportingId: 8, authCode: "AUTH1228"); - await helper.AddTransaction(transactionDate, "Test Merchant 1", "Safaricom Contract", "200 KES Topup", "0000", transactionReportingId: 9, authCode: "AUTH1229"); - await helper.AddTransaction(transactionDate, "Test Merchant 1", "Safaricom Contract", "200 KES Topup", "0000", transactionReportingId: 10, authCode: "AUTH1123"); + var result = await ApiClient.GetTodaysSalesValueByHour(string.Empty, Guid.NewGuid(), 0, 0, comparisonDate, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + List? todaysSalesValueByHour = result.Data; + foreach (DataTransferObjects.TodaysSalesValueByHour salesValueByHour in todaysSalesValueByHour) { + IEnumerable todayHour = todaysTransactions.Where(t => t.TransactionDateTime.Hour == salesValueByHour.Hour); + IEnumerable comparisonHour = comparisonDateTransactions.Where(t => t.TransactionDateTime.Hour == salesValueByHour.Hour); + salesValueByHour.ComparisonSalesValue.ShouldBe(comparisonHour.Sum(c => c.TransactionAmount)); + salesValueByHour.TodaysSalesValue.ShouldBe(todayHour.Sum(c => c.TransactionAmount)); + } + } - TransactionSearchRequest searchRequest = new TransactionSearchRequest - { - QueryDate = transactionDate, - AuthCode = "AUTH1235" - }; + #endregion - // No Paging - var result = await ApiClient.TransactionSearch(string.Empty, Guid.NewGuid(), searchRequest, null, null, null, null, CancellationToken.None); - result.IsSuccess.ShouldBeTrue(); - List searchResult = result.Data; - searchResult.Count.ShouldBe(1); - searchResult.Any(s => s.TransactionReportingId == 5).ShouldBeTrue(); - } - [Fact] - public async Task FactTransactionsController_TransactionSearch_TransactionNumberFiltering_TransactionReturned() - { + [Fact] + public async Task FactTransactionsControllerController_TodaysFailedSales_SalesReturned() { + EstateManagementGenericContext context = new EstateManagementSqlServerContext(GetLocalConnectionString($"EstateReportingReadModel{TestId.ToString()}")); + var todaysTransactions = new List(); + var comparisonDateTransactions = new List(); + DatabaseHelper helper = new DatabaseHelper(context); + // TODO: make counts dynamic + DateTime todaysDateTime = DateTime.Now; - DateTime transactionDate = new DateTime(2024, 3, 19); + Dictionary transactionCounts = new() { { "Test Merchant 1", 3 }, { "Test Merchant 2", 6 }, { "Test Merchant 3", 2 }, { "Test Merchant 4", 0 } }; - await helper.AddTransaction(transactionDate, "Test Merchant 1", "Safaricom Contract", "200 KES Topup", "0000", transactionReportingId: 1, authCode: "AUTH231"); - await helper.AddTransaction(transactionDate, "Test Merchant 1", "Safaricom Contract", "200 KES Topup", "0000", transactionReportingId: 2, authCode: "AUTH1232"); - await helper.AddTransaction(transactionDate, "Test Merchant 1", "Safaricom Contract", "200 KES Topup", "0000", transactionReportingId: 3, authCode: "AUTH1233"); - await helper.AddTransaction(transactionDate, "Test Merchant 1", "Safaricom Contract", "200 KES Topup", "0000", transactionReportingId: 4, authCode: "AUTH1234"); - await helper.AddTransaction(transactionDate, "Test Merchant 1", "Safaricom Contract", "200 KES Topup", "0000", transactionReportingId: 5, authCode: "AUTH1235"); - await helper.AddTransaction(transactionDate, "Test Merchant 1", "Safaricom Contract", "200 KES Topup", "0000", transactionReportingId: 6, authCode: "AUTH1236"); - await helper.AddTransaction(transactionDate, "Test Merchant 1", "Safaricom Contract", "200 KES Topup", "0000", transactionReportingId: 7, authCode: "AUTH1237"); - await helper.AddTransaction(transactionDate, "Test Merchant 1", "Safaricom Contract", "200 KES Topup", "0000", transactionReportingId: 8, authCode: "AUTH1228"); - await helper.AddTransaction(transactionDate, "Test Merchant 1", "Safaricom Contract", "200 KES Topup", "0000", transactionReportingId: 9, authCode: "AUTH1229"); - await helper.AddTransaction(transactionDate, "Test Merchant 1", "Safaricom Contract", "200 KES Topup", "0000", transactionReportingId: 10, authCode: "AUTH1123"); + DateTime comparisonDate = todaysDateTime.AddDays(-1); - TransactionSearchRequest searchRequest = new TransactionSearchRequest - { - QueryDate = transactionDate, - TransactionNumber = "0004" - }; + foreach (var merchant in merchantsList) { + foreach (var contract in contractList) { + var productList = contractProducts.Single(cp => cp.Key == contract.contractId).Value; + foreach ((Guid productId, String productName, Decimal? productValue) product in productList) { + var transactionCount = transactionCounts.Single(m => m.Key == merchant.Name).Value; + for (int i = 0; i < transactionCount; i++) { + Transaction transaction = await helper.BuildTransactionX(todaysDateTime.AddHours(-1), merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "1009", product.productValue); + todaysTransactions.Add(transaction); + } + } + } + } - // No Paging - var result = await ApiClient.TransactionSearch(string.Empty, Guid.NewGuid(), searchRequest, null, null, null, null, CancellationToken.None); - result.IsSuccess.ShouldBeTrue(); - List searchResult = result.Data; + await this.helper.AddTransactionsX(todaysTransactions); - searchResult.Count.ShouldBe(1); - searchResult.Any(s => s.TransactionReportingId == 4).ShouldBeTrue(); - } + // Comparison Date sales + foreach (var merchant in merchantsList) { + foreach (var contract in contractList) { + var productList = contractProducts.Single(cp => cp.Key == contract.contractId).Value; + foreach ((Guid productId, String productName, Decimal? productValue) product in productList) { + var transactionCount = transactionCounts.Single(m => m.Key == merchant.Name).Value; + for (int i = 0; i < transactionCount; i++) { + Transaction transaction = await helper.BuildTransactionX(comparisonDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "1009", product.productValue); + comparisonDateTransactions.Add(transaction); + } + } + } + } - [Fact] - public async Task FactTransactionsController_TransactionSearch_ResponseCodeFiltering_TransactionReturned() - { + await this.helper.AddTransactionsX(comparisonDateTransactions); - DateTime transactionDate = new DateTime(2024, 3, 19); - await helper.AddTransaction(transactionDate, "Test Merchant 1", "Safaricom Contract", "200 KES Topup", "0000", transactionReportingId: 1, authCode: "AUTH231"); - await helper.AddTransaction(transactionDate, "Test Merchant 1", "Safaricom Contract", "200 KES Topup", "0001", transactionReportingId: 2, authCode: "AUTH1232"); - await helper.AddTransaction(transactionDate, "Test Merchant 1", "Safaricom Contract", "200 KES Topup", "0000", transactionReportingId: 3, authCode: "AUTH1233"); - await helper.AddTransaction(transactionDate, "Test Merchant 1", "Safaricom Contract", "200 KES Topup", "0001", transactionReportingId: 4, authCode: "AUTH1234"); - await helper.AddTransaction(transactionDate, "Test Merchant 1", "Safaricom Contract", "200 KES Topup", "0000", transactionReportingId: 5, authCode: "AUTH1235"); - await helper.AddTransaction(transactionDate, "Test Merchant 1", "Safaricom Contract", "200 KES Topup", "0001", transactionReportingId: 6, authCode: "AUTH1236"); - await helper.AddTransaction(transactionDate, "Test Merchant 1", "Safaricom Contract", "200 KES Topup", "0000", transactionReportingId: 7, authCode: "AUTH1237"); - await helper.AddTransaction(transactionDate, "Test Merchant 1", "Safaricom Contract", "200 KES Topup", "0001", transactionReportingId: 8, authCode: "AUTH1228"); - await helper.AddTransaction(transactionDate, "Test Merchant 1", "Safaricom Contract", "200 KES Topup", "0000", transactionReportingId: 9, authCode: "AUTH1229"); - await helper.AddTransaction(transactionDate, "Test Merchant 1", "Safaricom Contract", "200 KES Topup", "0001", transactionReportingId: 10, authCode: "AUTH1123"); + await helper.RunTodaysTransactionsSummaryProcessing(comparisonDate.Date); + await helper.RunHistoricTransactionsSummaryProcessing(comparisonDate.Date); + await helper.RunTodaysTransactionsSummaryProcessing(todaysDateTime.Date); - TransactionSearchRequest searchRequest = new TransactionSearchRequest - { - QueryDate = transactionDate, - ResponseCode = "0001" - }; + var result = await ApiClient.GetTodaysFailedSales(string.Empty, Guid.NewGuid(), 1, 1, "1009", comparisonDate, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + DataTransferObjects.TodaysSales? todaysSales = result.Data; - // No Paging - var result = await ApiClient.TransactionSearch(string.Empty, Guid.NewGuid(), searchRequest, null, null, null, null, CancellationToken.None); - result.IsSuccess.ShouldBeTrue(); - List searchResult = result.Data; - searchResult.Count.ShouldBe(5); - searchResult.Any(s => s.TransactionReportingId == 2).ShouldBeTrue(); - searchResult.Any(s => s.TransactionReportingId == 4).ShouldBeTrue(); - searchResult.Any(s => s.TransactionReportingId == 6).ShouldBeTrue(); - searchResult.Any(s => s.TransactionReportingId == 8).ShouldBeTrue(); - searchResult.Any(s => s.TransactionReportingId == 10).ShouldBeTrue(); - - result= await ApiClient.TransactionSearch(string.Empty, Guid.NewGuid(), searchRequest, 1, 3, null, null, CancellationToken.None); - result.IsSuccess.ShouldBeTrue(); - searchResult = result.Data; - searchResult.Count.ShouldBe(3); - searchResult.Any(s => s.TransactionReportingId == 2).ShouldBeTrue(); - searchResult.Any(s => s.TransactionReportingId == 4).ShouldBeTrue(); - searchResult.Any(s => s.TransactionReportingId == 6).ShouldBeTrue(); + todaysSales.ShouldNotBeNull(); + todaysSales.ComparisonSalesCount.ShouldBe(comparisonDateTransactions.Count); + todaysSales.ComparisonSalesValue.ShouldBe(comparisonDateTransactions.Sum(c => c.TransactionAmount)); - result= await ApiClient.TransactionSearch(string.Empty, Guid.NewGuid(), searchRequest, 2, 3, null, null, CancellationToken.None); - result.IsSuccess.ShouldBeTrue(); - searchResult = result.Data; - searchResult.Count.ShouldBe(2); - searchResult.Any(s => s.TransactionReportingId == 8).ShouldBeTrue(); - searchResult.Any(s => s.TransactionReportingId == 10).ShouldBeTrue(); - } + todaysSales.TodaysSalesCount.ShouldBe(todaysTransactions.Count); + todaysSales.ComparisonSalesValue.ShouldBe(comparisonDateTransactions.Sum(c => c.TransactionAmount)); + } - [Fact] - public async Task FactTransactionsController_TransactionSearch_MerchantFiltering_TransactionReturned() - { - DateTime transactionDate = new DateTime(2024, 3, 19); - - await helper.AddTransaction(transactionDate, "Test Merchant 1", "Safaricom Contract", "200 KES Topup", "0000", transactionReportingId: 1, authCode: "AUTH231"); - await helper.AddTransaction(transactionDate, "Test Merchant 1", "Safaricom Contract", "200 KES Topup", "0000", transactionReportingId: 2, authCode: "AUTH1232"); - await helper.AddTransaction(transactionDate, "Test Merchant 1", "Safaricom Contract", "200 KES Topup", "0000", transactionReportingId: 3, authCode: "AUTH1233"); - await helper.AddTransaction(transactionDate, "Test Merchant 1", "Safaricom Contract", "200 KES Topup", "0000", transactionReportingId: 4, authCode: "AUTH1234"); - await helper.AddTransaction(transactionDate, "Test Merchant 1", "Safaricom Contract", "200 KES Topup", "0000", transactionReportingId: 5, authCode: "AUTH1235"); - await helper.AddTransaction(transactionDate, "Test Merchant 1", "Safaricom Contract", "200 KES Topup", "0000", transactionReportingId: 6, authCode: "AUTH1236"); - await helper.AddTransaction(transactionDate, "Test Merchant 1", "Safaricom Contract", "200 KES Topup", "0000", transactionReportingId: 7, authCode: "AUTH1237"); - await helper.AddTransaction(transactionDate, "Test Merchant 1", "Safaricom Contract", "200 KES Topup", "0000", transactionReportingId: 8, authCode: "AUTH1228"); - await helper.AddTransaction(transactionDate, "Test Merchant 1", "Safaricom Contract", "200 KES Topup", "0000", transactionReportingId: 9, authCode: "AUTH1229"); - await helper.AddTransaction(transactionDate, "Test Merchant 1", "Safaricom Contract", "200 KES Topup", "0000", transactionReportingId: 10, authCode: "AUTH1123"); - - await helper.AddTransaction(transactionDate, "Test Merchant 2", "Safaricom Contract", "200 KES Topup", "0000", transactionReportingId: 11, authCode: "AUTH231"); - await helper.AddTransaction(transactionDate, "Test Merchant 2", "Safaricom Contract", "200 KES Topup", "0000", transactionReportingId: 12, authCode: "AUTH1232"); - await helper.AddTransaction(transactionDate, "Test Merchant 2", "Safaricom Contract", "200 KES Topup", "0000", transactionReportingId: 13, authCode: "AUTH1233"); - await helper.AddTransaction(transactionDate, "Test Merchant 2", "Safaricom Contract", "200 KES Topup", "0000", transactionReportingId: 14, authCode: "AUTH1234"); - await helper.AddTransaction(transactionDate, "Test Merchant 2", "Safaricom Contract", "200 KES Topup", "0000", transactionReportingId: 15, authCode: "AUTH1235"); - await helper.AddTransaction(transactionDate, "Test Merchant 2", "Safaricom Contract", "200 KES Topup", "0000", transactionReportingId: 16, authCode: "AUTH1236"); - - await helper.AddTransaction(transactionDate, "Test Merchant 3", "Safaricom Contract", "200 KES Topup", "0000", transactionReportingId: 17, authCode: "AUTH231"); - await helper.AddTransaction(transactionDate, "Test Merchant 3", "Safaricom Contract", "200 KES Topup", "0000", transactionReportingId: 18, authCode: "AUTH1232"); - await helper.AddTransaction(transactionDate, "Test Merchant 3", "Safaricom Contract", "200 KES Topup", "0000", transactionReportingId: 19, authCode: "AUTH1233"); - await helper.AddTransaction(transactionDate, "Test Merchant 3", "Safaricom Contract", "200 KES Topup", "0000", transactionReportingId: 20, authCode: "AUTH1234"); - await helper.AddTransaction(transactionDate, "Test Merchant 3", "Safaricom Contract", "200 KES Topup", "0000", transactionReportingId: 21, authCode: "AUTH1235"); - await helper.AddTransaction(transactionDate, "Test Merchant 3", "Safaricom Contract", "200 KES Topup", "0000", transactionReportingId: 22, authCode: "AUTH1236"); - await helper.AddTransaction(transactionDate, "Test Merchant 3", "Safaricom Contract", "200 KES Topup", "0000", transactionReportingId: 23, authCode: "AUTH1237"); - await helper.AddTransaction(transactionDate, "Test Merchant 3", "Safaricom Contract", "200 KES Topup", "0000", transactionReportingId: 24, authCode: "AUTH1228"); - await helper.AddTransaction(transactionDate, "Test Merchant 3", "Safaricom Contract", "200 KES Topup", "0000", transactionReportingId: 25, authCode: "AUTH1229"); - await helper.AddTransaction(transactionDate, "Test Merchant 3", "Safaricom Contract", "200 KES Topup", "0000", transactionReportingId: 26, authCode: "AUTH1123"); - await helper.AddTransaction(transactionDate, "Test Merchant 3", "Safaricom Contract", "200 KES Topup", "0000", transactionReportingId: 27, authCode: "AUTH1236"); - await helper.AddTransaction(transactionDate, "Test Merchant 3", "Safaricom Contract", "200 KES Topup", "0000", transactionReportingId: 28, authCode: "AUTH1237"); - - TransactionSearchRequest searchRequest = new TransactionSearchRequest - { - QueryDate = transactionDate, - Merchants = new List(){ - 2,3 - } - }; + [Fact] + public async Task FactTransactionsController_TransactionSearch_NoAdditionalFiltering_TransactionReturned() { + + DateTime transactionDate = new DateTime(2024, 3, 19); + var merchant = merchantsList.Single(m => m.Name == "Test Merchant 1"); + var contract = contractList.Single(c => c.operatorName == "Safaricom"); + var contractProducts = this.contractProducts.Where(cp => cp.Key == contract.contractId); + (Guid productId, String productName, Decimal? productValue) product = contractProducts.First().Value.Where(x => x.productName == "200 KES Topup").Single(); + + // Add some transactions + List transactions = new List(); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 1, transactionAmount: product.productValue)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 2, transactionAmount: product.productValue)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 3, transactionAmount: product.productValue)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 4, transactionAmount: product.productValue)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 5, transactionAmount: product.productValue)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 6, transactionAmount: product.productValue)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 7, transactionAmount: product.productValue)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 8, transactionAmount: product.productValue)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 9, transactionAmount: product.productValue)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 10, transactionAmount: product.productValue)); + await this.helper.AddTransactionsX(transactions); + + DataTransferObjects.TransactionSearchRequest searchRequest = new() { QueryDate = transactionDate }; + + // No Paging or Sorting + var result = await ApiClient.TransactionSearch(string.Empty, Guid.NewGuid(), searchRequest, null, null, null, null, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + var searchResult = result.Data; + searchResult.Count.ShouldBe(10); + searchResult.Any(s => s.TransactionReportingId == 1).ShouldBeTrue(); + searchResult.Any(s => s.TransactionReportingId == 2).ShouldBeTrue(); + searchResult.Any(s => s.TransactionReportingId == 3).ShouldBeTrue(); + searchResult.Any(s => s.TransactionReportingId == 4).ShouldBeTrue(); + searchResult.Any(s => s.TransactionReportingId == 5).ShouldBeTrue(); + searchResult.Any(s => s.TransactionReportingId == 6).ShouldBeTrue(); + searchResult.Any(s => s.TransactionReportingId == 7).ShouldBeTrue(); + searchResult.Any(s => s.TransactionReportingId == 8).ShouldBeTrue(); + searchResult.Any(s => s.TransactionReportingId == 9).ShouldBeTrue(); + searchResult.Any(s => s.TransactionReportingId == 10).ShouldBeTrue(); + + + result = await ApiClient.TransactionSearch(string.Empty, Guid.NewGuid(), searchRequest, 1, 5, null, null, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + searchResult = result.Data; + searchResult.Count.ShouldBe(5); + searchResult.Any(s => s.TransactionReportingId == 1).ShouldBeTrue(); + searchResult.Any(s => s.TransactionReportingId == 2).ShouldBeTrue(); + searchResult.Any(s => s.TransactionReportingId == 3).ShouldBeTrue(); + searchResult.Any(s => s.TransactionReportingId == 4).ShouldBeTrue(); + searchResult.Any(s => s.TransactionReportingId == 5).ShouldBeTrue(); + + result = await ApiClient.TransactionSearch(string.Empty, Guid.NewGuid(), searchRequest, 2, 5, null, null, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + searchResult = result.Data; + searchResult.Count.ShouldBe(5); + searchResult.Any(s => s.TransactionReportingId == 6).ShouldBeTrue(); + searchResult.Any(s => s.TransactionReportingId == 7).ShouldBeTrue(); + searchResult.Any(s => s.TransactionReportingId == 8).ShouldBeTrue(); + searchResult.Any(s => s.TransactionReportingId == 9).ShouldBeTrue(); + searchResult.Any(s => s.TransactionReportingId == 10).ShouldBeTrue(); + } - // No Paging - var result = await ApiClient.TransactionSearch(string.Empty, Guid.NewGuid(), searchRequest, null, null, null, null, CancellationToken.None); - result.IsSuccess.ShouldBeTrue(); - List searchResult = result.Data; - - searchResult.Count.ShouldBe(10); - searchResult.Any(s => s.TransactionReportingId == 11).ShouldBeTrue(); - searchResult.Any(s => s.TransactionReportingId == 12).ShouldBeTrue(); - searchResult.Any(s => s.TransactionReportingId == 13).ShouldBeTrue(); - searchResult.Any(s => s.TransactionReportingId == 14).ShouldBeTrue(); - searchResult.Any(s => s.TransactionReportingId == 15).ShouldBeTrue(); - searchResult.Any(s => s.TransactionReportingId == 16).ShouldBeTrue(); - searchResult.Any(s => s.TransactionReportingId == 17).ShouldBeTrue(); - searchResult.Any(s => s.TransactionReportingId == 18).ShouldBeTrue(); - searchResult.Any(s => s.TransactionReportingId == 19).ShouldBeTrue(); - searchResult.Any(s => s.TransactionReportingId == 20).ShouldBeTrue(); - searchResult.Count(s => s.MerchantName == "Test Merchant 2").ShouldBe(6); - searchResult.Count(s => s.MerchantName == "Test Merchant 3").ShouldBe(4); - - result = await ApiClient.TransactionSearch(string.Empty, Guid.NewGuid(), searchRequest, 1, 5, null, null, CancellationToken.None); - result.IsSuccess.ShouldBeTrue(); - searchResult = result.Data; - - searchResult.Count.ShouldBe(5); - searchResult.Any(s => s.TransactionReportingId == 11).ShouldBeTrue(); - searchResult.Any(s => s.TransactionReportingId == 12).ShouldBeTrue(); - searchResult.Any(s => s.TransactionReportingId == 13).ShouldBeTrue(); - searchResult.Any(s => s.TransactionReportingId == 14).ShouldBeTrue(); - searchResult.Any(s => s.TransactionReportingId == 15).ShouldBeTrue(); - searchResult.Count(s => s.MerchantName == "Test Merchant 2").ShouldBe(5); - searchResult.Count(s => s.MerchantName == "Test Merchant 3").ShouldBe(0); - - result = await ApiClient.TransactionSearch(string.Empty, Guid.NewGuid(), searchRequest, 2, 5, null, null, CancellationToken.None); - result.IsSuccess.ShouldBeTrue(); - searchResult = result.Data; - - searchResult.Count.ShouldBe(5); - searchResult.Any(s => s.TransactionReportingId == 16).ShouldBeTrue(); - searchResult.Any(s => s.TransactionReportingId == 17).ShouldBeTrue(); - searchResult.Any(s => s.TransactionReportingId == 18).ShouldBeTrue(); - searchResult.Any(s => s.TransactionReportingId == 19).ShouldBeTrue(); - searchResult.Any(s => s.TransactionReportingId == 20).ShouldBeTrue(); - searchResult.Count(s => s.MerchantName == "Test Merchant 2").ShouldBe(1); - searchResult.Count(s => s.MerchantName == "Test Merchant 3").ShouldBe(4); - } + [Fact] + public async Task FactTransactionsController_TransactionSearch_ValueRangeFiltering_TransactionReturned() { + + DateTime transactionDate = new DateTime(2024, 3, 19); + + var merchant = merchantsList.Single(m => m.Name == "Test Merchant 1"); + var contract = contractList.Single(c => c.operatorName == "Safaricom"); + var contractProducts = this.contractProducts.Where(cp => cp.Key == contract.contractId); + var product = contractProducts.First().Value.Where(x => x.productName == "Custom").Single(); + + // Add some transactions + List transactions = new List(); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 1, transactionAmount: 50)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 2, transactionAmount: 123)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 3, transactionAmount: 100)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 4, transactionAmount: 101)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 5, transactionAmount: 199)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 6, transactionAmount: 150)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 7, transactionAmount: 200)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 8, transactionAmount: 201)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 9, transactionAmount: 99)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 10, transactionAmount: 111)); + await this.helper.AddTransactionsX(transactions); + + DataTransferObjects.TransactionSearchRequest searchRequest = new() { QueryDate = transactionDate, ValueRange = new() { StartValue = 100, EndValue = 200 } }; + + // No Paging + var result = await ApiClient.TransactionSearch(string.Empty, Guid.NewGuid(), searchRequest, null, null, null, null, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + List searchResult = result.Data; + searchResult.Count.ShouldBe(7); + searchResult.Any(s => s.TransactionReportingId == 2).ShouldBeTrue(); + searchResult.Any(s => s.TransactionReportingId == 3).ShouldBeTrue(); + searchResult.Any(s => s.TransactionReportingId == 4).ShouldBeTrue(); + searchResult.Any(s => s.TransactionReportingId == 5).ShouldBeTrue(); + searchResult.Any(s => s.TransactionReportingId == 6).ShouldBeTrue(); + searchResult.Any(s => s.TransactionReportingId == 7).ShouldBeTrue(); + searchResult.Any(s => s.TransactionReportingId == 10).ShouldBeTrue(); + + result = await ApiClient.TransactionSearch(string.Empty, Guid.NewGuid(), searchRequest, 1, 3, null, null, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + searchResult = result.Data; + searchResult.Count.ShouldBe(3); + searchResult.Any(s => s.TransactionReportingId == 10).ShouldBeTrue(); + searchResult.Any(s => s.TransactionReportingId == 3).ShouldBeTrue(); + searchResult.Any(s => s.TransactionReportingId == 4).ShouldBeTrue(); + + result = await ApiClient.TransactionSearch(string.Empty, Guid.NewGuid(), searchRequest, 2, 3, null, null, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + searchResult = result.Data; + searchResult.Count.ShouldBe(3); + searchResult.Any(s => s.TransactionReportingId == 2).ShouldBeTrue(); + searchResult.Any(s => s.TransactionReportingId == 6).ShouldBeTrue(); + searchResult.Any(s => s.TransactionReportingId == 5).ShouldBeTrue(); - [Fact] - public async Task FactTransactionsController_TransactionSearch_OperatorFiltering_TransactionReturned() - { + } - DateTime transactionDate = new DateTime(2024, 3, 19); + [Fact] + public async Task FactTransactionsController_TransactionSearch_AuthCodeFiltering_TransactionReturned() { + + DateTime transactionDate = new DateTime(2024, 3, 19); + var merchant = merchantsList.Single(m => m.Name == "Test Merchant 1"); + var contract = contractList.Single(c => c.operatorName == "Safaricom"); + var contractProducts = this.contractProducts.Where(cp => cp.Key == contract.contractId); + var product = contractProducts.First().Value.Where(x => x.productName == "200 KES Topup").Single(); + + List transactions = new List(); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 1, authCode: "AUTH231", transactionAmount: product.productValue)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 2, authCode: "AUTH1232", transactionAmount: product.productValue)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 3, authCode: "AUTH1233", transactionAmount: product.productValue)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 4, authCode: "AUTH1234", transactionAmount: product.productValue)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 5, authCode: "AUTH1235", transactionAmount: product.productValue)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 6, authCode: "AUTH1236", transactionAmount: product.productValue)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 7, authCode: "AUTH1237", transactionAmount: product.productValue)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 8, authCode: "AUTH1228", transactionAmount: product.productValue)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 9, authCode: "AUTH1229", transactionAmount: product.productValue)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 10, authCode: "AUTH1123", transactionAmount: product.productValue)); + await this.helper.AddTransactionsX(transactions); + + DataTransferObjects.TransactionSearchRequest searchRequest = new() { QueryDate = transactionDate, AuthCode = "AUTH1235" }; + + // No Paging + var result = await ApiClient.TransactionSearch(string.Empty, Guid.NewGuid(), searchRequest, null, null, null, null, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + List searchResult = result.Data; + searchResult.Count.ShouldBe(1); + searchResult.Any(s => s.TransactionReportingId == 5).ShouldBeTrue(); + } - await helper.AddTransaction(transactionDate, "Test Merchant 1", "Safaricom Contract", "200 KES Topup", "0000", transactionReportingId: 1, authCode: "AUTH231"); - await helper.AddTransaction(transactionDate, "Test Merchant 1", "Healthcare Centre 1 Contract", "10 KES Voucher", "0000", transactionReportingId: 2, authCode: "AUTH1232"); - await helper.AddTransaction(transactionDate, "Test Merchant 1", "PataPawa PostPay Contract", "Post Pay Bill Pay", "0000", transactionReportingId: 3, authCode: "AUTH1233"); - await helper.AddTransaction(transactionDate, "Test Merchant 1", "Safaricom Contract", "200 KES Topup", "0000", transactionReportingId: 4, authCode: "AUTH1234"); - await helper.AddTransaction(transactionDate, "Test Merchant 1", "Safaricom Contract", "200 KES Topup", "0000", transactionReportingId: 5, authCode: "AUTH1235"); - await helper.AddTransaction(transactionDate, "Test Merchant 1", "Healthcare Centre 1 Contract", "10 KES Voucher", "0000", transactionReportingId: 6, authCode: "AUTH1236"); - await helper.AddTransaction(transactionDate, "Test Merchant 1", "Safaricom Contract", "200 KES Topup", "0000", transactionReportingId: 7, authCode: "AUTH1237"); - await helper.AddTransaction(transactionDate, "Test Merchant 1", "PataPawa PostPay Contract", "Post Pay Bill Pay", "0000", transactionReportingId: 8, authCode: "AUTH1228"); - await helper.AddTransaction(transactionDate, "Test Merchant 1", "PataPawa PrePay Contract", "Pre Pay Bill Pay", "0000", transactionReportingId: 9, authCode: "AUTH1229"); - await helper.AddTransaction(transactionDate, "Test Merchant 1", "PataPawa PrePay Contract", "Pre Pay Bill Pay", "0000", transactionReportingId: 10, authCode: "AUTH1123"); - TransactionSearchRequest searchRequest = new TransactionSearchRequest - { - QueryDate = transactionDate, - Operators = new List(){ - 2,4 - } - }; + [Fact] + public async Task FactTransactionsController_TransactionSearch_TransactionNumberFiltering_TransactionReturned() { - // No Paging - var result = await ApiClient.TransactionSearch(string.Empty, Guid.NewGuid(), searchRequest, null, null, null, null, CancellationToken.None); - result.IsSuccess.ShouldBeTrue(); - List searchResult = result.Data; - searchResult.Count.ShouldBe(4); - searchResult.Any(s => s.TransactionReportingId == 2).ShouldBeTrue(); - searchResult.Any(s => s.TransactionReportingId == 6).ShouldBeTrue(); - searchResult.Any(s => s.TransactionReportingId == 9).ShouldBeTrue(); - searchResult.Any(s => s.TransactionReportingId == 10).ShouldBeTrue(); - searchResult.Count(s => s.OperatorName == "Voucher").ShouldBe(2); - searchResult.Count(s => s.OperatorName == "PataPawa PrePay").ShouldBe(2); - - result = await ApiClient.TransactionSearch(string.Empty, Guid.NewGuid(), searchRequest, 1, 2, null, null, CancellationToken.None); - result.IsSuccess.ShouldBeTrue(); - searchResult = result.Data; - searchResult.Count.ShouldBe(2); - searchResult.Any(s => s.TransactionReportingId == 2).ShouldBeTrue(); - searchResult.Any(s => s.TransactionReportingId == 6).ShouldBeTrue(); - searchResult.Count(s => s.OperatorName == "Voucher").ShouldBe(2); - searchResult.Count(s => s.OperatorName == "PataPawa PrePay").ShouldBe(0); - - result= await ApiClient.TransactionSearch(string.Empty, Guid.NewGuid(), searchRequest, 2, 2, null, null, CancellationToken.None); - result.IsSuccess.ShouldBeTrue(); - searchResult = result.Data; - searchResult.Count.ShouldBe(2); - searchResult.Any(s => s.TransactionReportingId == 9).ShouldBeTrue(); - searchResult.Any(s => s.TransactionReportingId == 10).ShouldBeTrue(); - searchResult.Count(s => s.OperatorName == "Voucher").ShouldBe(0); - searchResult.Count(s => s.OperatorName == "PataPawa PrePay").ShouldBe(2); - } + DateTime transactionDate = new DateTime(2024, 3, 19); - [Fact] - public async Task FactTransactionsController_TransactionSearch_SortingTest_TransactionReturned() - { + var merchant = merchantsList.Single(m => m.Name == "Test Merchant 1"); + var contract = contractList.Single(c => c.operatorName == "Safaricom"); + var contractProducts = this.contractProducts.Where(cp => cp.Key == contract.contractId); + var product = contractProducts.First().Value.Where(x => x.productName == "200 KES Topup").Single(); - DateTime transactionDate = new DateTime(2024, 3, 19); + List transactions = new List(); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 1, authCode: "AUTH231", transactionAmount: product.productValue)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 2, authCode: "AUTH1232", transactionAmount: product.productValue)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 3, authCode: "AUTH1233", transactionAmount: product.productValue)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 4, authCode: "AUTH1234", transactionAmount: product.productValue)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 5, authCode: "AUTH1235", transactionAmount: product.productValue)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 6, authCode: "AUTH1236", transactionAmount: product.productValue)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 7, authCode: "AUTH1237", transactionAmount: product.productValue)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 8, authCode: "AUTH1228", transactionAmount: product.productValue)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 9, authCode: "AUTH1229", transactionAmount: product.productValue)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 10, authCode: "AUTH1123", transactionAmount: product.productValue)); + await this.helper.AddTransactionsX(transactions); - // Add some transactions - await helper.AddTransaction(transactionDate, "Test Merchant 1", "Safaricom Contract", "Custom", "0000", transactionAmount: 100); - await helper.AddTransaction(transactionDate, "Test Merchant 2", "Healthcare Centre 1 Contract", "Custom", "0000", transactionAmount: 200); - await helper.AddTransaction(transactionDate, "Test Merchant 3", "PataPawa PostPay Contract", "Post Pay Bill Pay", "0000", transactionAmount: 300); + DataTransferObjects.TransactionSearchRequest searchRequest = new() { QueryDate = transactionDate, TransactionNumber = "0004" }; - TransactionSearchRequest searchRequest = new TransactionSearchRequest - { - QueryDate = transactionDate - }; - // Default Sort - var result = await ApiClient.TransactionSearch(string.Empty, Guid.NewGuid(), searchRequest, null, null, null, null, CancellationToken.None); - result.IsSuccess.ShouldBeTrue(); - var searchResult = result.Data; - searchResult.Count.ShouldBe(3); - searchResult[0].TransactionAmount.ShouldBe(100); - searchResult[1].TransactionAmount.ShouldBe(200); - searchResult[2].TransactionAmount.ShouldBe(300); - - // Sort By merchant Name ascending - result = await ApiClient.TransactionSearch(string.Empty, Guid.NewGuid(), searchRequest, null, null, SortField.MerchantName, SortDirection.Ascending, CancellationToken.None); - result.IsSuccess.ShouldBeTrue(); - searchResult = result.Data; - searchResult.Count.ShouldBe(3); - searchResult[0].MerchantName.ShouldBe("Test Merchant 1"); - searchResult[1].MerchantName.ShouldBe("Test Merchant 2"); - searchResult[2].MerchantName.ShouldBe("Test Merchant 3"); - - // Sort By merchant Name descending - result = await ApiClient.TransactionSearch(string.Empty, Guid.NewGuid(), searchRequest, null, null, SortField.MerchantName, SortDirection.Descending, CancellationToken.None); - result.IsSuccess.ShouldBeTrue(); - searchResult = result.Data; - searchResult.Count.ShouldBe(3); - searchResult[0].MerchantName.ShouldBe("Test Merchant 3"); - searchResult[1].MerchantName.ShouldBe("Test Merchant 2"); - searchResult[2].MerchantName.ShouldBe("Test Merchant 1"); - - // Sort By operator Name ascending - result = await ApiClient.TransactionSearch(string.Empty, Guid.NewGuid(), searchRequest, null, null, SortField.OperatorName, SortDirection.Ascending, CancellationToken.None); - result.IsSuccess.ShouldBeTrue(); - searchResult = result.Data; + // No Paging + var result = await ApiClient.TransactionSearch(string.Empty, Guid.NewGuid(), searchRequest, null, null, null, null, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + List searchResult = result.Data; - searchResult.Count.ShouldBe(3); - searchResult[0].OperatorName.ShouldBe("PataPawa PostPay"); - searchResult[1].OperatorName.ShouldBe("Safaricom"); - searchResult[2].OperatorName.ShouldBe("Voucher"); + searchResult.Count.ShouldBe(1); + searchResult.Any(s => s.TransactionReportingId == 4).ShouldBeTrue(); + } - // Sort By operator Name descending - result = await ApiClient.TransactionSearch(string.Empty, Guid.NewGuid(), searchRequest, null, null, SortField.OperatorName, SortDirection.Descending, CancellationToken.None); - result.IsSuccess.ShouldBeTrue(); - searchResult = result.Data; - searchResult.Count.ShouldBe(3); - searchResult[0].OperatorName.ShouldBe("Voucher"); - searchResult[1].OperatorName.ShouldBe("Safaricom"); - searchResult[2].OperatorName.ShouldBe("PataPawa PostPay"); - - // Sort By transaction amount ascending - result= await ApiClient.TransactionSearch(string.Empty, Guid.NewGuid(), searchRequest, null, null, SortField.TransactionAmount, SortDirection.Ascending, CancellationToken.None); - result.IsSuccess.ShouldBeTrue(); - searchResult = result.Data; - searchResult.Count.ShouldBe(3); - searchResult[0].TransactionAmount.ShouldBe(100); - searchResult[1].TransactionAmount.ShouldBe(200); - searchResult[2].TransactionAmount.ShouldBe(300); - - // Sort By transaction amount descending - result= await ApiClient.TransactionSearch(string.Empty, Guid.NewGuid(), searchRequest, null, null, SortField.TransactionAmount, SortDirection.Descending, CancellationToken.None); - result.IsSuccess.ShouldBeTrue(); - searchResult = result.Data; - searchResult.Count.ShouldBe(3); - searchResult[0].TransactionAmount.ShouldBe(300); - searchResult[1].TransactionAmount.ShouldBe(200); - searchResult[2].TransactionAmount.ShouldBe(100); - } + [Fact] + public async Task FactTransactionsController_TransactionSearch_ResponseCodeFiltering_TransactionReturned() { + + DateTime transactionDate = new DateTime(2024, 3, 19); + var merchant = merchantsList.Single(m => m.Name == "Test Merchant 1"); + var contract = contractList.Single(c => c.operatorName == "Safaricom"); + var contractProducts = this.contractProducts.Where(cp => cp.Key == contract.contractId); + var product = contractProducts.First().Value.Where(x => x.productName == "200 KES Topup").Single(); + + List transactions = new List(); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 1, authCode: "AUTH231", transactionAmount: product.productValue)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0001", transactionReportingId: 2, authCode: "AUTH1232", transactionAmount: product.productValue)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 3, authCode: "AUTH1233", transactionAmount: product.productValue)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0001", transactionReportingId: 4, authCode: "AUTH1234", transactionAmount: product.productValue)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 5, authCode: "AUTH1235", transactionAmount: product.productValue)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0001", transactionReportingId: 6, authCode: "AUTH1236", transactionAmount: product.productValue)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 7, authCode: "AUTH1237", transactionAmount: product.productValue)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0001", transactionReportingId: 8, authCode: "AUTH1228", transactionAmount: product.productValue)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 9, authCode: "AUTH1229", transactionAmount: product.productValue)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0001", transactionReportingId: 10, authCode: "AUTH1123", transactionAmount: product.productValue)); + await this.helper.AddTransactionsX(transactions); + + DataTransferObjects.TransactionSearchRequest searchRequest = new() { QueryDate = transactionDate, ResponseCode = "0001" }; + + // No Paging + var result = await ApiClient.TransactionSearch(string.Empty, Guid.NewGuid(), searchRequest, null, null, null, null, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + List searchResult = result.Data; + searchResult.Count.ShouldBe(5); + searchResult.Any(s => s.TransactionReportingId == 2).ShouldBeTrue(); + searchResult.Any(s => s.TransactionReportingId == 4).ShouldBeTrue(); + searchResult.Any(s => s.TransactionReportingId == 6).ShouldBeTrue(); + searchResult.Any(s => s.TransactionReportingId == 8).ShouldBeTrue(); + searchResult.Any(s => s.TransactionReportingId == 10).ShouldBeTrue(); + + result = await ApiClient.TransactionSearch(string.Empty, Guid.NewGuid(), searchRequest, 1, 3, null, null, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + searchResult = result.Data; + searchResult.Count.ShouldBe(3); + searchResult.Any(s => s.TransactionReportingId == 2).ShouldBeTrue(); + searchResult.Any(s => s.TransactionReportingId == 4).ShouldBeTrue(); + searchResult.Any(s => s.TransactionReportingId == 6).ShouldBeTrue(); + + result = await ApiClient.TransactionSearch(string.Empty, Guid.NewGuid(), searchRequest, 2, 3, null, null, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + searchResult = result.Data; + searchResult.Count.ShouldBe(2); + searchResult.Any(s => s.TransactionReportingId == 8).ShouldBeTrue(); + searchResult.Any(s => s.TransactionReportingId == 10).ShouldBeTrue(); + } - [Fact] - public async Task FactTransactionsControllerController_GetMerchantsByLastDaleDate_MerchantsReturned() - { - DateTime todaysDateTime = DateTime.Now; + [Fact] + public async Task FactTransactionsController_TransactionSearch_MerchantFiltering_TransactionReturned() { + + DateTime transactionDate = new DateTime(2024, 3, 19); + + var merchant = merchantsList.Single(m => m.Name == "Test Merchant 1"); + var contract = contractList.Single(c => c.operatorName == "Safaricom"); + var contractProducts = this.contractProducts.Where(cp => cp.Key == contract.contractId); + var product = contractProducts.First().Value.Where(x => x.productName == "200 KES Topup").Single(); + + List transactions = new List(); + + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 1, authCode: "AUTH231", transactionAmount: product.productValue)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 2, authCode: "AUTH231", transactionAmount: product.productValue)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 3, authCode: "AUTH231", transactionAmount: product.productValue)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 4, authCode: "AUTH231", transactionAmount: product.productValue)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 5, authCode: "AUTH231", transactionAmount: product.productValue)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 6, authCode: "AUTH231", transactionAmount: product.productValue)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 7, authCode: "AUTH231", transactionAmount: product.productValue)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 8, authCode: "AUTH231", transactionAmount: product.productValue)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 9, authCode: "AUTH231", transactionAmount: product.productValue)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 10, authCode: "AUTH231", transactionAmount: product.productValue)); + + merchant = merchantsList.Single(m => m.Name == "Test Merchant 2"); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 11, authCode: "AUTH231", transactionAmount: product.productValue)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 12, authCode: "AUTH231", transactionAmount: product.productValue)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 13, authCode: "AUTH231", transactionAmount: product.productValue)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 14, authCode: "AUTH231", transactionAmount: product.productValue)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 15, authCode: "AUTH231", transactionAmount: product.productValue)); + + merchant = merchantsList.Single(m => m.Name == "Test Merchant 3"); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 16, authCode: "AUTH231", transactionAmount: product.productValue)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 17, authCode: "AUTH231", transactionAmount: product.productValue)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 18, authCode: "AUTH231", transactionAmount: product.productValue)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 19, authCode: "AUTH231", transactionAmount: product.productValue)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 20, authCode: "AUTH231", transactionAmount: product.productValue)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 21, authCode: "AUTH231", transactionAmount: product.productValue)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 22, authCode: "AUTH231", transactionAmount: product.productValue)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 23, authCode: "AUTH231", transactionAmount: product.productValue)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 24, authCode: "AUTH231", transactionAmount: product.productValue)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 25, authCode: "AUTH231", transactionAmount: product.productValue)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 26, authCode: "AUTH231", transactionAmount: product.productValue)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 27, authCode: "AUTH231", transactionAmount: product.productValue)); + + await this.helper.AddTransactionsX(transactions); + + DataTransferObjects.TransactionSearchRequest searchRequest = new() { QueryDate = transactionDate, Merchants = new List() { 2, 3 } }; + + // No Paging + var result = await ApiClient.TransactionSearch(string.Empty, Guid.NewGuid(), searchRequest, null, null, null, null, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + List searchResult = result.Data; + + searchResult.Count.ShouldBe(10); + searchResult.Any(s => s.TransactionReportingId == 11).ShouldBeTrue(); + searchResult.Any(s => s.TransactionReportingId == 12).ShouldBeTrue(); + searchResult.Any(s => s.TransactionReportingId == 13).ShouldBeTrue(); + searchResult.Any(s => s.TransactionReportingId == 14).ShouldBeTrue(); + searchResult.Any(s => s.TransactionReportingId == 15).ShouldBeTrue(); + searchResult.Any(s => s.TransactionReportingId == 16).ShouldBeTrue(); + searchResult.Any(s => s.TransactionReportingId == 17).ShouldBeTrue(); + searchResult.Any(s => s.TransactionReportingId == 18).ShouldBeTrue(); + searchResult.Any(s => s.TransactionReportingId == 19).ShouldBeTrue(); + searchResult.Any(s => s.TransactionReportingId == 20).ShouldBeTrue(); + searchResult.Count(s => s.MerchantName == "Test Merchant 2").ShouldBe(5); + searchResult.Count(s => s.MerchantName == "Test Merchant 3").ShouldBe(5); + + result = await ApiClient.TransactionSearch(string.Empty, Guid.NewGuid(), searchRequest, 1, 5, null, null, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + searchResult = result.Data; + + searchResult.Count.ShouldBe(5); + searchResult.Any(s => s.TransactionReportingId == 11).ShouldBeTrue(); + searchResult.Any(s => s.TransactionReportingId == 12).ShouldBeTrue(); + searchResult.Any(s => s.TransactionReportingId == 13).ShouldBeTrue(); + searchResult.Any(s => s.TransactionReportingId == 14).ShouldBeTrue(); + searchResult.Any(s => s.TransactionReportingId == 15).ShouldBeTrue(); + searchResult.Count(s => s.MerchantName == "Test Merchant 2").ShouldBe(5); + searchResult.Count(s => s.MerchantName == "Test Merchant 3").ShouldBe(0); + + result = await ApiClient.TransactionSearch(string.Empty, Guid.NewGuid(), searchRequest, 2, 5, null, null, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + searchResult = result.Data; + + searchResult.Count.ShouldBe(5); + searchResult.Any(s => s.TransactionReportingId == 16).ShouldBeTrue(); + searchResult.Any(s => s.TransactionReportingId == 17).ShouldBeTrue(); + searchResult.Any(s => s.TransactionReportingId == 18).ShouldBeTrue(); + searchResult.Any(s => s.TransactionReportingId == 19).ShouldBeTrue(); + searchResult.Any(s => s.TransactionReportingId == 20).ShouldBeTrue(); + searchResult.Count(s => s.MerchantName == "Test Merchant 2").ShouldBe(0); + searchResult.Count(s => s.MerchantName == "Test Merchant 3").ShouldBe(5); + } - await ClearStandingData(); - - // Last Hour - await helper.AddMerchant("Test Estate", "Merchant 1", todaysDateTime.AddMinutes(-10)); - await helper.AddMerchant("Test Estate", "Merchant 2", todaysDateTime.AddMinutes(-10)); - await helper.AddMerchant("Test Estate", "Merchant 3", todaysDateTime.AddMinutes(-10)); - await helper.AddMerchant("Test Estate", "Merchant 4", todaysDateTime.AddMinutes(-10)); - - // Yesterday - await helper.AddMerchant("Test Estate", "Merchant 5", todaysDateTime.AddDays(-1)); - await helper.AddMerchant("Test Estate", "Merchant 6", todaysDateTime.AddDays(-1)); - await helper.AddMerchant("Test Estate", "Merchant 7", todaysDateTime.AddDays(-1)); - await helper.AddMerchant("Test Estate", "Merchant 8", todaysDateTime.AddDays(-1)); - await helper.AddMerchant("Test Estate", "Merchant 9", todaysDateTime.AddDays(-1)); - await helper.AddMerchant("Test Estate", "Merchant 10", todaysDateTime.AddDays(-1)); - - // 5 days ago - await helper.AddMerchant("Test Estate", "Merchant 11", todaysDateTime.AddDays(-5)); - await helper.AddMerchant("Test Estate", "Merchant 12", todaysDateTime.AddDays(-5)); - await helper.AddMerchant("Test Estate", "Merchant 13", todaysDateTime.AddDays(-5)); - - // 10 Days Ago - await helper.AddMerchant("Test Estate", "Merchant 14", todaysDateTime.AddDays(-10)); - await helper.AddMerchant("Test Estate", "Merchant 15", todaysDateTime.AddDays(-10)); - await helper.AddMerchant("Test Estate", "Merchant 16", todaysDateTime.AddDays(-10)); - await helper.AddMerchant("Test Estate", "Merchant 17", todaysDateTime.AddDays(-10)); - await helper.AddMerchant("Test Estate", "Merchant 18", todaysDateTime.AddDays(-10)); - - DateTime startDate = DateTime.Now; - DateTime endDate = DateTime.Now; - - // Test 1 - sale in last hour - startDate = DateTime.Now.AddHours(-1); - endDate = DateTime.Now; - var result = await this.ApiClient.GetMerchantsByLastSaleDate(String.Empty, Guid.NewGuid(), startDate, endDate, CancellationToken.None); - result.IsSuccess.ShouldBeTrue(); - var searchResult = result.Data; - searchResult.ShouldNotBeNull(); - searchResult.Count.ShouldBe(4); - searchResult.SingleOrDefault(s => s.Name == "Merchant 1").ShouldNotBeNull(); - searchResult.SingleOrDefault(s => s.Name == "Merchant 2").ShouldNotBeNull(); - searchResult.SingleOrDefault(s => s.Name == "Merchant 3").ShouldNotBeNull(); - searchResult.SingleOrDefault(s => s.Name == "Merchant 4").ShouldNotBeNull(); - - // Test 2 - sale in last day but over an hour ago - startDate = DateTime.Now.Date.AddDays(-1); - endDate = DateTime.Now.AddHours(-1); - result = await this.ApiClient.GetMerchantsByLastSaleDate(String.Empty, Guid.NewGuid(), startDate, endDate, CancellationToken.None); - result.IsSuccess.ShouldBeTrue(); - searchResult = result.Data; - searchResult.ShouldNotBeNull(); - searchResult.Count.ShouldBe(6); - searchResult.SingleOrDefault(s => s.Name == "Merchant 5").ShouldNotBeNull(); - searchResult.SingleOrDefault(s => s.Name == "Merchant 6").ShouldNotBeNull(); - searchResult.SingleOrDefault(s => s.Name == "Merchant 7").ShouldNotBeNull(); - searchResult.SingleOrDefault(s => s.Name == "Merchant 8").ShouldNotBeNull(); - searchResult.SingleOrDefault(s => s.Name == "Merchant 9").ShouldNotBeNull(); - searchResult.SingleOrDefault(s => s.Name == "Merchant 10").ShouldNotBeNull(); - - // Test 3 - sale in last 7 days but non yesterday - startDate = DateTime.Now.Date.AddDays(-7); - endDate = DateTime.Now.Date.AddDays(-1); - result = await this.ApiClient.GetMerchantsByLastSaleDate(String.Empty, Guid.NewGuid(), startDate, endDate, CancellationToken.None); - result.IsSuccess.ShouldBeTrue(); - searchResult = result.Data; - searchResult.ShouldNotBeNull(); - searchResult.Count.ShouldBe(3); - searchResult.SingleOrDefault(s => s.Name == "Merchant 11").ShouldNotBeNull(); - searchResult.SingleOrDefault(s => s.Name == "Merchant 12").ShouldNotBeNull(); - searchResult.SingleOrDefault(s => s.Name == "Merchant 13").ShouldNotBeNull(); - - // Test 4 - sale more than 7 days ago - startDate = DateTime.Now.Date.AddYears(-1); - endDate = DateTime.Now.Date.AddDays(-7); - result = await this.ApiClient.GetMerchantsByLastSaleDate(String.Empty, Guid.NewGuid(), startDate, endDate, CancellationToken.None); - result.IsSuccess.ShouldBeTrue(); - searchResult = result.Data; - searchResult.ShouldNotBeNull(); - searchResult.Count.ShouldBe(5); - searchResult.SingleOrDefault(s => s.Name == "Merchant 14").ShouldNotBeNull(); - searchResult.SingleOrDefault(s => s.Name == "Merchant 15").ShouldNotBeNull(); - searchResult.SingleOrDefault(s => s.Name == "Merchant 16").ShouldNotBeNull(); - searchResult.SingleOrDefault(s => s.Name == "Merchant 17").ShouldNotBeNull(); - searchResult.SingleOrDefault(s => s.Name == "Merchant 18").ShouldNotBeNull(); - } -} + [Fact] + public async Task FactTransactionsController_TransactionSearch_OperatorFiltering_TransactionReturned() { + + DateTime transactionDate = new DateTime(2024, 3, 19); + + var merchant = merchantsList.Single(m => m.Name == "Test Merchant 1"); + var contract = contractList.Single(c => c.operatorName == "Safaricom"); + var contractProducts = this.contractProducts.Where(cp => cp.Key == contract.contractId); + var product = contractProducts.First().Value.Where(x => x.productName == "200 KES Topup").Single(); + + List transactions = new List(); + + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 1, authCode: "AUTH231", transactionAmount: product.productValue)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 4, authCode: "AUTH231", transactionAmount: product.productValue)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 5, authCode: "AUTH231", transactionAmount: product.productValue)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 7, authCode: "AUTH231", transactionAmount: product.productValue)); + + contract = contractList.Single(c => c.operatorName == "Voucher"); + contractProducts = this.contractProducts.Where(cp => cp.Key == contract.contractId); + product = contractProducts.First().Value.Where(x => x.productName == "10 KES Voucher").Single(); + + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 2, authCode: "AUTH231", transactionAmount: product.productValue)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 6, authCode: "AUTH231", transactionAmount: product.productValue)); + + contract = contractList.Single(c => c.operatorName == "PataPawa PostPay"); + contractProducts = this.contractProducts.Where(cp => cp.Key == contract.contractId); + product = contractProducts.First().Value.Where(x => x.productName == "Post Pay Bill Pay").Single(); + + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 3, authCode: "AUTH231", transactionAmount: product.productValue)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 8, authCode: "AUTH231", transactionAmount: product.productValue)); + + contract = contractList.Single(c => c.operatorName == "PataPawa PrePay"); + contractProducts = this.contractProducts.Where(cp => cp.Key == contract.contractId); + product = contractProducts.First().Value.Where(x => x.productName == "Pre Pay Bill Pay").Single(); + + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 9, authCode: "AUTH231", transactionAmount: product.productValue)); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 10, authCode: "AUTH231", transactionAmount: product.productValue)); + + await this.helper.AddTransactionsX(transactions); + + DataTransferObjects.TransactionSearchRequest searchRequest = new() { QueryDate = transactionDate, Operators = new List() { 2, 4 } }; + + // No Paging + var result = await ApiClient.TransactionSearch(string.Empty, Guid.NewGuid(), searchRequest, null, null, null, null, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + List searchResult = result.Data; + searchResult.Count.ShouldBe(4); + searchResult.Any(s => s.TransactionReportingId == 2).ShouldBeTrue(); + searchResult.Any(s => s.TransactionReportingId == 6).ShouldBeTrue(); + searchResult.Any(s => s.TransactionReportingId == 9).ShouldBeTrue(); + searchResult.Any(s => s.TransactionReportingId == 10).ShouldBeTrue(); + searchResult.Count(s => s.OperatorName == "Voucher").ShouldBe(2); + searchResult.Count(s => s.OperatorName == "PataPawa PrePay").ShouldBe(2); + + result = await ApiClient.TransactionSearch(string.Empty, Guid.NewGuid(), searchRequest, 1, 2, null, null, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + searchResult = result.Data; + searchResult.Count.ShouldBe(2); + searchResult.Any(s => s.TransactionReportingId == 2).ShouldBeTrue(); + searchResult.Any(s => s.TransactionReportingId == 6).ShouldBeTrue(); + searchResult.Count(s => s.OperatorName == "Voucher").ShouldBe(2); + searchResult.Count(s => s.OperatorName == "PataPawa PrePay").ShouldBe(0); + + result = await ApiClient.TransactionSearch(string.Empty, Guid.NewGuid(), searchRequest, 2, 2, null, null, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + searchResult = result.Data; + searchResult.Count.ShouldBe(2); + searchResult.Any(s => s.TransactionReportingId == 9).ShouldBeTrue(); + searchResult.Any(s => s.TransactionReportingId == 10).ShouldBeTrue(); + searchResult.Count(s => s.OperatorName == "Voucher").ShouldBe(0); + searchResult.Count(s => s.OperatorName == "PataPawa PrePay").ShouldBe(2); + } + [Fact] + public async Task FactTransactionsController_TransactionSearch_SortingTest_TransactionReturned() { + + DateTime transactionDate = new DateTime(2024, 3, 19); + + var merchant = merchantsList.Single(m => m.Name == "Test Merchant 1"); + var contract = contractList.Single(c => c.operatorName == "Safaricom"); + var contractProducts = this.contractProducts.Where(cp => cp.Key == contract.contractId); + var product = contractProducts.First().Value.Where(x => x.productName == "Custom").Single(); + + List transactions = new List(); + // Add some transactions + //await helper.AddTransaction(transactionDate, "Test Merchant 1", "Safaricom Contract", "Custom", "0000", transactionAmount: 100); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 1, authCode: "AUTH231", transactionAmount: 100)); + + merchant = merchantsList.Single(m => m.Name == "Test Merchant 2"); + contract = contractList.Single(c => c.operatorName == "Voucher"); + contractProducts = this.contractProducts.Where(cp => cp.Key == contract.contractId); + product = contractProducts.First().Value.Where(x => x.productName == "Custom").Single(); + + //await helper.AddTransaction(transactionDate, "Test Merchant 2", "Healthcare Centre 1 Contract", "Custom", "0000", transactionAmount: 200); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 2, authCode: "AUTH231", transactionAmount: 200)); + + merchant = merchantsList.Single(m => m.Name == "Test Merchant 3"); + contract = contractList.Single(c => c.operatorName == "PataPawa PostPay"); + contractProducts = this.contractProducts.Where(cp => cp.Key == contract.contractId); + product = contractProducts.First().Value.Where(x => x.productName == "Post Pay Bill Pay").Single(); + + //await helper.AddTransaction(transactionDate, "Test Merchant 3", "PataPawa PostPay Contract", "Post Pay Bill Pay", "0000", transactionAmount: 300); + transactions.Add(await helper.BuildTransactionX(transactionDate, merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "0000", transactionReportingId: 3, authCode: "AUTH231", transactionAmount: 300)); + + await this.helper.AddTransactionsX(transactions); + + DataTransferObjects.TransactionSearchRequest searchRequest = new() { QueryDate = transactionDate }; + // Default Sort + var result = await ApiClient.TransactionSearch(string.Empty, Guid.NewGuid(), searchRequest, null, null, null, null, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + var searchResult = result.Data; + searchResult.Count.ShouldBe(3); + searchResult[0].TransactionAmount.ShouldBe(100); + searchResult[1].TransactionAmount.ShouldBe(200); + searchResult[2].TransactionAmount.ShouldBe(300); + + // Sort By merchant Name ascending + result = await ApiClient.TransactionSearch(string.Empty, Guid.NewGuid(), searchRequest, null, null, DataTransferObjects.SortField.MerchantName, SortDirection.Ascending, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + searchResult = result.Data; + searchResult.Count.ShouldBe(3); + searchResult[0].MerchantName.ShouldBe("Test Merchant 1"); + searchResult[1].MerchantName.ShouldBe("Test Merchant 2"); + searchResult[2].MerchantName.ShouldBe("Test Merchant 3"); + + // Sort By merchant Name descending + result = await ApiClient.TransactionSearch(string.Empty, Guid.NewGuid(), searchRequest, null, null, DataTransferObjects.SortField.MerchantName, SortDirection.Descending, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + searchResult = result.Data; + searchResult.Count.ShouldBe(3); + searchResult[0].MerchantName.ShouldBe("Test Merchant 3"); + searchResult[1].MerchantName.ShouldBe("Test Merchant 2"); + searchResult[2].MerchantName.ShouldBe("Test Merchant 1"); + + // Sort By operator Name ascending + result = await ApiClient.TransactionSearch(string.Empty, Guid.NewGuid(), searchRequest, null, null, DataTransferObjects.SortField.OperatorName, SortDirection.Ascending, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + searchResult = result.Data; + + searchResult.Count.ShouldBe(3); + searchResult[0].OperatorName.ShouldBe("PataPawa PostPay"); + searchResult[1].OperatorName.ShouldBe("Safaricom"); + searchResult[2].OperatorName.ShouldBe("Voucher"); + + // Sort By operator Name descending + result = await ApiClient.TransactionSearch(string.Empty, Guid.NewGuid(), searchRequest, null, null, DataTransferObjects.SortField.OperatorName, SortDirection.Descending, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + searchResult = result.Data; + searchResult.Count.ShouldBe(3); + searchResult[0].OperatorName.ShouldBe("Voucher"); + searchResult[1].OperatorName.ShouldBe("Safaricom"); + searchResult[2].OperatorName.ShouldBe("PataPawa PostPay"); + + // Sort By transaction amount ascending + result = await ApiClient.TransactionSearch(string.Empty, Guid.NewGuid(), searchRequest, null, null, DataTransferObjects.SortField.TransactionAmount, SortDirection.Ascending, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + searchResult = result.Data; + searchResult.Count.ShouldBe(3); + searchResult[0].TransactionAmount.ShouldBe(100); + searchResult[1].TransactionAmount.ShouldBe(200); + searchResult[2].TransactionAmount.ShouldBe(300); + + // Sort By transaction amount descending + result = await ApiClient.TransactionSearch(string.Empty, Guid.NewGuid(), searchRequest, null, null, DataTransferObjects.SortField.TransactionAmount, SortDirection.Descending, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + searchResult = result.Data; + searchResult.Count.ShouldBe(3); + searchResult[0].TransactionAmount.ShouldBe(300); + searchResult[1].TransactionAmount.ShouldBe(200); + searchResult[2].TransactionAmount.ShouldBe(100); + } + } \ No newline at end of file diff --git a/EstateReportingAPI.IntegrationTests/xunit.runner.json b/EstateReportingAPI.IntegrationTests/xunit.runner.json index 1a74a1d..c565a6f 100644 --- a/EstateReportingAPI.IntegrationTests/xunit.runner.json +++ b/EstateReportingAPI.IntegrationTests/xunit.runner.json @@ -1,3 +1,5 @@ { - "maxParallelThreads": 1 + "maxParallelThreads": 10, + "parallelizeTestCollections": true + } \ No newline at end of file diff --git a/EstateReportingAPI.Models/EstateReportingAPI.Models.csproj b/EstateReportingAPI.Models/EstateReportingAPI.Models.csproj index 30402ac..bf8ca4e 100644 --- a/EstateReportingAPI.Models/EstateReportingAPI.Models.csproj +++ b/EstateReportingAPI.Models/EstateReportingAPI.Models.csproj @@ -4,6 +4,7 @@ net8.0 enable enable + None diff --git a/EstateReportingAPI.Tests/EstateReportingApiClientTests.cs b/EstateReportingAPI.Tests/EstateReportingApiClientTests.cs new file mode 100644 index 0000000..8157599 --- /dev/null +++ b/EstateReportingAPI.Tests/EstateReportingApiClientTests.cs @@ -0,0 +1,587 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Net; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using EstateReportingAPI.Client; +using EstateReportingAPI.DataTransferObjects; +using Moq.Protected; +using Moq; +using Xunit; +using EstateReportingAPI.DataTrasferObjects; +using Newtonsoft.Json; +using Shouldly; +using SimpleResults; +using SortDirection = EstateReportingAPI.DataTransferObjects.SortDirection; + +namespace EstateReportingAPI.Tests { + public class EstateReportingApiClientTests { + private readonly IEstateReportingApiClient EstateReportingApiClient; + private readonly IProtectedMock HttpMessageHandler; + + public EstateReportingApiClientTests() { + var baseAddressResolver = new Func(s => "http://localhost"); + var mockHandler = new Mock(); + EstateReportingApiClient = new EstateReportingApiClient(baseAddressResolver, new HttpClient(mockHandler.Object)); + this.HttpMessageHandler = mockHandler.Protected(); + } + + [Fact] + public async Task EstateReportingApiClient_GetCalendarDates_DatesReturned() { + var resultData = Result.Success(TestData.CalendarDateList); + + this.HttpMessageHandler.Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny()).ReturnsAsync(new HttpResponseMessage { StatusCode = HttpStatusCode.OK, Content = new StringContent(JsonConvert.SerializeObject(resultData)) }); + + var result = await this.EstateReportingApiClient.GetCalendarDates("", Guid.NewGuid(), 2024, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + result.Data.Count.ShouldBe(TestData.CalendarDateList.Count); + } + + [Fact] + public async Task EstateReportingApiClient_GetCalendarDates_SendAsyncReturnsFailureResponse_ResultFailed() + { + var resultData = Result.Success(TestData.CalendarDateList); + + this.HttpMessageHandler.Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny()).ReturnsAsync(new HttpResponseMessage { StatusCode = HttpStatusCode.BadRequest }); + + var result = await this.EstateReportingApiClient.GetCalendarDates("", Guid.NewGuid(), 2024, CancellationToken.None); + result.IsFailed.ShouldBeTrue(); + } + + [Fact] + public async Task EstateReportingApiClient_GetCalendarDates_SendAsyncThrowsException_ResultFailed() { + this.HttpMessageHandler.Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny()).ThrowsAsync(new Exception()); + + var result = await this.EstateReportingApiClient.GetCalendarDates("", Guid.NewGuid(), 2024, CancellationToken.None); + result.IsFailed.ShouldBeTrue(); + } + + [Fact] + public async Task EstateReportingApiClient_GetCalendarYears_YearsReturned() { + var resultData = Result.Success(TestData.CalendarYearList); + + this.HttpMessageHandler.Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny()).ReturnsAsync(new HttpResponseMessage { StatusCode = HttpStatusCode.OK, Content = new StringContent(JsonConvert.SerializeObject(resultData)) }); + + var result = await this.EstateReportingApiClient.GetCalendarYears("", Guid.NewGuid(), CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + result.Data.Count.ShouldBe(TestData.CalendarYearList.Count); + } + + [Fact] + public async Task EstateReportingApiClient_GetCalendarYears_SendAsyncThrowsException_ResultFailed() { + this.HttpMessageHandler.Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny()).ThrowsAsync(new Exception()); + + var result = await this.EstateReportingApiClient.GetCalendarYears("", Guid.NewGuid(), CancellationToken.None); + result.IsFailed.ShouldBeTrue(); + } + + [Fact] + public async Task EstateReportingApiClient_GetComparisonDates_DatesReturned() { + var resultData = Result.Success(TestData.ComparisonDateList); + + this.HttpMessageHandler.Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny()).ReturnsAsync(new HttpResponseMessage { StatusCode = HttpStatusCode.OK, Content = new StringContent(JsonConvert.SerializeObject(resultData)) }); + + var result = await this.EstateReportingApiClient.GetComparisonDates("", Guid.NewGuid(), CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + result.Data.Count.ShouldBe(TestData.ComparisonDateList.Count); + } + + [Fact] + public async Task EstateReportingApiClient_GetComparisonDates_SendAsyncThrowsException_ResultFailed() { + this.HttpMessageHandler.Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny()).ThrowsAsync(new Exception()); + + var result = await this.EstateReportingApiClient.GetComparisonDates("", Guid.NewGuid(), CancellationToken.None); + result.IsFailed.ShouldBeTrue(); + } + + [Fact] + public async Task EstateReportingApiClient_GetLastSettlement_LastSettlementReturned() { + var resultData = Result.Success(TestData.LastSettlement); + + this.HttpMessageHandler.Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny()).ReturnsAsync(new HttpResponseMessage { StatusCode = HttpStatusCode.OK, Content = new StringContent(JsonConvert.SerializeObject(resultData)) }); + + var result = await this.EstateReportingApiClient.GetLastSettlement("", Guid.NewGuid(), CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + result.Data.FeesValue.ShouldBe(TestData.LastSettlement.FeesValue); + result.Data.SalesCount.ShouldBe(TestData.LastSettlement.SalesCount); + result.Data.SalesValue.ShouldBe(TestData.LastSettlement.SalesValue); + result.Data.SettlementDate.ShouldBe(TestData.LastSettlement.SettlementDate); + } + + [Fact] + public async Task EstateReportingApiClient_GetLastSettlement_SendAsyncThrowsException_ResultFailed() { + this.HttpMessageHandler.Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny()).ThrowsAsync(new Exception()); + + var result = await this.EstateReportingApiClient.GetLastSettlement("", Guid.NewGuid(), CancellationToken.None); + result.IsFailed.ShouldBeTrue(); + } + + [Fact] + public async Task EstateReportingApiClient_GetResponseCodes_ResponseCodesReturned() { + var resultData = Result.Success(TestData.ResponseCodeList); + + this.HttpMessageHandler.Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny()).ReturnsAsync(new HttpResponseMessage { StatusCode = HttpStatusCode.OK, Content = new StringContent(JsonConvert.SerializeObject(resultData)) }); + + var result = await this.EstateReportingApiClient.GetResponseCodes("", Guid.NewGuid(), CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + result.Data.Count.ShouldBe(TestData.ResponseCodeList.Count); + } + + [Fact] + public async Task EstateReportingApiClient_GetResponseCodes_SendAsyncThrowsException_ResultFailed() { + this.HttpMessageHandler.Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny()).ThrowsAsync(new Exception()); + + var result = await this.EstateReportingApiClient.GetResponseCodes("", Guid.NewGuid(), CancellationToken.None); + result.IsFailed.ShouldBeTrue(); + } + + [Fact] + public async Task EstateReportingApiClient_GetMerchantPerformance_PerformanceReturned() { + var resultData = Result.Success(TestData.TodaysSales); + + this.HttpMessageHandler.Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny()).ReturnsAsync(new HttpResponseMessage { StatusCode = HttpStatusCode.OK, Content = new StringContent(JsonConvert.SerializeObject(resultData)) }); + + var result = await this.EstateReportingApiClient.GetMerchantPerformance("", Guid.NewGuid(), DateTime.Now, new List { 1, 2 }, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + result.Data.ComparisonAverageSalesValue.ShouldBe(TestData.TodaysSales.ComparisonAverageSalesValue); + result.Data.ComparisonSalesCount.ShouldBe(TestData.TodaysSales.ComparisonSalesCount); + result.Data.ComparisonSalesValue.ShouldBe(TestData.TodaysSales.ComparisonSalesValue); + result.Data.TodaysAverageSalesValue.ShouldBe(TestData.TodaysSales.TodaysAverageSalesValue); + result.Data.TodaysSalesCount.ShouldBe(TestData.TodaysSales.TodaysSalesCount); + result.Data.TodaysSalesValue.ShouldBe(TestData.TodaysSales.TodaysSalesValue); + } + + [Fact] + public async Task EstateReportingApiClient_GetMerchantPerformance_SendAsyncThrowsException_ResultFailed() { + this.HttpMessageHandler.Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny()).ThrowsAsync(new Exception()); + + var result = await this.EstateReportingApiClient.GetMerchantPerformance("", Guid.NewGuid(), DateTime.Now, new List { 1, 2 }, CancellationToken.None); + result.IsFailed.ShouldBeTrue(); + } + + [Fact] + public async Task EstateReportingApiClient_GetProductPerformance_PerformanceReturned() { + var resultData = Result.Success(TestData.TodaysSales); + + this.HttpMessageHandler.Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny()).ReturnsAsync(new HttpResponseMessage { StatusCode = HttpStatusCode.OK, Content = new StringContent(JsonConvert.SerializeObject(resultData)) }); + + var result = await this.EstateReportingApiClient.GetProductPerformance("", Guid.NewGuid(), DateTime.Now, new List { 1, 2 }, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + result.Data.ComparisonAverageSalesValue.ShouldBe(TestData.TodaysSales.ComparisonAverageSalesValue); + result.Data.ComparisonSalesCount.ShouldBe(TestData.TodaysSales.ComparisonSalesCount); + result.Data.ComparisonSalesValue.ShouldBe(TestData.TodaysSales.ComparisonSalesValue); + result.Data.TodaysAverageSalesValue.ShouldBe(TestData.TodaysSales.TodaysAverageSalesValue); + result.Data.TodaysSalesCount.ShouldBe(TestData.TodaysSales.TodaysSalesCount); + result.Data.TodaysSalesValue.ShouldBe(TestData.TodaysSales.TodaysSalesValue); + } + + [Fact] + public async Task EstateReportingApiClient_GetProductPerformance_SendAsyncThrowsException_ResultFailed() { + this.HttpMessageHandler.Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny()).ThrowsAsync(new Exception()); + + var result = await this.EstateReportingApiClient.GetProductPerformance("", Guid.NewGuid(), DateTime.Now, new List { 1, 2 }, CancellationToken.None); + result.IsFailed.ShouldBeTrue(); + } + + [Fact] + public async Task EstateReportingApiClient_GetMerchantsByLastSaleDate_MerchantsReturned() { + var resultData = Result.Success(TestData.MerchantList); + + this.HttpMessageHandler.Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny()).ReturnsAsync(new HttpResponseMessage { StatusCode = HttpStatusCode.OK, Content = new StringContent(JsonConvert.SerializeObject(resultData)) }); + + var result = await this.EstateReportingApiClient.GetMerchantsByLastSaleDate("", Guid.NewGuid(), DateTime.Now.AddDays(-1), DateTime.Now, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + result.Data.Count.ShouldBe(TestData.MerchantList.Count); + } + + [Fact] + public async Task EstateReportingApiClient_GetMerchantsByLastSaleDate_SendAsyncThrowsException_ResultFailed() { + this.HttpMessageHandler.Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny()).ThrowsAsync(new Exception()); + + var result = await this.EstateReportingApiClient.GetMerchantsByLastSaleDate("", Guid.NewGuid(), DateTime.Now.AddDays(-1), DateTime.Now, CancellationToken.None); + result.IsFailed.ShouldBeTrue(); + } + + [Fact] + public async Task EstateReportingApiClient_GetOperatorPerformance_PerformanceReturned() { + var resultData = Result.Success(TestData.TodaysSales); + + this.HttpMessageHandler.Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny()).ReturnsAsync(new HttpResponseMessage { StatusCode = HttpStatusCode.OK, Content = new StringContent(JsonConvert.SerializeObject(resultData)) }); + + var result = await this.EstateReportingApiClient.GetOperatorPerformance("", Guid.NewGuid(), DateTime.Now, new List { 1, 2 }, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + result.Data.ComparisonAverageSalesValue.ShouldBe(TestData.TodaysSales.ComparisonAverageSalesValue); + result.Data.ComparisonSalesCount.ShouldBe(TestData.TodaysSales.ComparisonSalesCount); + result.Data.ComparisonSalesValue.ShouldBe(TestData.TodaysSales.ComparisonSalesValue); + result.Data.TodaysAverageSalesValue.ShouldBe(TestData.TodaysSales.TodaysAverageSalesValue); + result.Data.TodaysSalesCount.ShouldBe(TestData.TodaysSales.TodaysSalesCount); + result.Data.TodaysSalesValue.ShouldBe(TestData.TodaysSales.TodaysSalesValue); + } + + [Fact] + public async Task EstateReportingApiClient_GetOperatorPerformance_SendAsyncThrowsException_ResultFailed() { + this.HttpMessageHandler.Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny()).ThrowsAsync(new Exception()); + + var result = await this.EstateReportingApiClient.GetOperatorPerformance("", Guid.NewGuid(), DateTime.Now, new List { 1, 2 }, CancellationToken.None); + result.IsFailed.ShouldBeTrue(); + } + + [Fact] + public async Task EstateReportingApiClient_TransactionSearch_TransactionsReturned() { + var resultData = Result.Success(TestData.TransactionResultList); + + this.HttpMessageHandler.Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny()).ReturnsAsync(new HttpResponseMessage { StatusCode = HttpStatusCode.OK, Content = new StringContent(JsonConvert.SerializeObject(resultData)) }); + + var result = await this.EstateReportingApiClient.TransactionSearch("", Guid.NewGuid(), new TransactionSearchRequest(), null, null, null, null, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + result.Data.Count.ShouldBe(TestData.TransactionResultList.Count); + } + + [Fact] + public async Task EstateReportingApiClient_TransactionSearch_WithPageNumber_TransactionsReturned() + { + var resultData = Result.Success(TestData.TransactionResultList); + + this.HttpMessageHandler.Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny()).ReturnsAsync(new HttpResponseMessage { StatusCode = HttpStatusCode.OK, Content = new StringContent(JsonConvert.SerializeObject(resultData)) }); + + var result = await this.EstateReportingApiClient.TransactionSearch("", Guid.NewGuid(), new TransactionSearchRequest(), 1, null, null, null, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + result.Data.Count.ShouldBe(TestData.TransactionResultList.Count); + } + + [Fact] + public async Task EstateReportingApiClient_TransactionSearch_WithPageSize_TransactionsReturned() + { + var resultData = Result.Success(TestData.TransactionResultList); + + this.HttpMessageHandler.Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny()).ReturnsAsync(new HttpResponseMessage { StatusCode = HttpStatusCode.OK, Content = new StringContent(JsonConvert.SerializeObject(resultData)) }); + + var result = await this.EstateReportingApiClient.TransactionSearch("", Guid.NewGuid(), new TransactionSearchRequest(), null, 1, null, null, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + result.Data.Count.ShouldBe(TestData.TransactionResultList.Count); + } + + [Fact] + public async Task EstateReportingApiClient_TransactionSearch_WithSort_TransactionsReturned() + { + var resultData = Result.Success(TestData.TransactionResultList); + + this.HttpMessageHandler.Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny()).ReturnsAsync(new HttpResponseMessage { StatusCode = HttpStatusCode.OK, Content = new StringContent(JsonConvert.SerializeObject(resultData)) }); + + var result = await this.EstateReportingApiClient.TransactionSearch("", Guid.NewGuid(), new TransactionSearchRequest(), null, null, SortField.MerchantName, SortDirection.Ascending, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + result.Data.Count.ShouldBe(TestData.TransactionResultList.Count); + } + + [Fact] + public async Task EstateReportingApiClient_TransactionSearch_SendAsyncThrowsException_ResultFailed() { + this.HttpMessageHandler.Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny()).ThrowsAsync(new Exception()); + + var result = await this.EstateReportingApiClient.TransactionSearch("", Guid.NewGuid(), new TransactionSearchRequest(), null, null, null, null, CancellationToken.None); + result.IsFailed.ShouldBeTrue(); + } + + [Fact] + public async Task EstateReportingApiClient_GetUnsettledFees_FeesReturned() { + var resultData = Result.Success(TestData.UnsettledFeeList); + + this.HttpMessageHandler.Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny()).ReturnsAsync(new HttpResponseMessage { StatusCode = HttpStatusCode.OK, Content = new StringContent(JsonConvert.SerializeObject(resultData)) }); + + var result = await this.EstateReportingApiClient.GetUnsettledFees("", Guid.NewGuid(), DateTime.Now.AddDays(-1), DateTime.Now, new List { 1, 2 }, new List { 1, 2 }, new List { 1, 2 }, GroupByOption.Merchant, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + result.Data.Count.ShouldBe(TestData.UnsettledFeeList.Count); + } + + [Fact] + public async Task EstateReportingApiClient_GetUnsettledFees_SendAsyncThrowsException_ResultFailed() { + this.HttpMessageHandler.Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny()).ThrowsAsync(new Exception()); + + var result = await this.EstateReportingApiClient.GetUnsettledFees("", Guid.NewGuid(), DateTime.Now.AddDays(-1), DateTime.Now, new List { 1, 2 }, new List { 1, 2 }, new List { 1, 2 }, GroupByOption.Merchant, CancellationToken.None); + result.IsFailed.ShouldBeTrue(); + } + + [Fact] + public async Task EstateReportingApiClient_GetMerchantKpi_KpiReturned() { + var resultData = Result.Success(TestData.MerchantKpi); + + this.HttpMessageHandler.Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny()).ReturnsAsync(new HttpResponseMessage { StatusCode = HttpStatusCode.OK, Content = new StringContent(JsonConvert.SerializeObject(resultData)) }); + + var result = await this.EstateReportingApiClient.GetMerchantKpi("", Guid.NewGuid(), CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + result.Data.MerchantsWithNoSaleToday.ShouldBe(TestData.MerchantKpi.MerchantsWithNoSaleToday); + result.Data.MerchantsWithNoSaleInLast7Days.ShouldBe(TestData.MerchantKpi.MerchantsWithNoSaleInLast7Days); + result.Data.MerchantsWithSaleInLastHour.ShouldBe(TestData.MerchantKpi.MerchantsWithSaleInLastHour); + } + + [Fact] + public async Task EstateReportingApiClient_GetMerchantKpi_SendAsyncThrowsException_ResultFailed() { + this.HttpMessageHandler.Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny()).ThrowsAsync(new Exception()); + + var result = await this.EstateReportingApiClient.GetMerchantKpi("", Guid.NewGuid(), CancellationToken.None); + result.IsFailed.ShouldBeTrue(); + } + + [Fact] + public async Task EstateReportingApiClient_GetMerchants_MerchantsReturned() { + var resultData = Result.Success(TestData.MerchantList); + + this.HttpMessageHandler.Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny()).ReturnsAsync(new HttpResponseMessage { StatusCode = HttpStatusCode.OK, Content = new StringContent(JsonConvert.SerializeObject(resultData)) }); + + var result = await this.EstateReportingApiClient.GetMerchants("", Guid.NewGuid(), CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + result.Data.Count.ShouldBe(TestData.MerchantList.Count); + } + + [Fact] + public async Task EstateReportingApiClient_GetMerchants_SendAsyncThrowsException_ResultFailed() { + this.HttpMessageHandler.Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny()).ThrowsAsync(new Exception()); + + var result = await this.EstateReportingApiClient.GetMerchants("", Guid.NewGuid(), CancellationToken.None); + result.IsFailed.ShouldBeTrue(); + } + + [Fact] + public async Task EstateReportingApiClient_GetOperators_OperatorsReturned() { + var resultData = Result.Success(TestData.OperatorList); + + this.HttpMessageHandler.Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny()).ReturnsAsync(new HttpResponseMessage { StatusCode = HttpStatusCode.OK, Content = new StringContent(JsonConvert.SerializeObject(resultData)) }); + + var result = await this.EstateReportingApiClient.GetOperators("", Guid.NewGuid(), CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + result.Data.Count.ShouldBe(TestData.OperatorList.Count); + } + + [Fact] + public async Task EstateReportingApiClient_GetOperators_SendAsyncThrowsException_ResultFailed() { + this.HttpMessageHandler.Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny()).ThrowsAsync(new Exception()); + + var result = await this.EstateReportingApiClient.GetOperators("", Guid.NewGuid(), CancellationToken.None); + result.IsFailed.ShouldBeTrue(); + } + + [Fact] + public async Task EstateReportingApiClient_GetTodaysFailedSales_FailedSalesReturned() { + var resultData = Result.Success(TestData.TodaysSales); + + this.HttpMessageHandler.Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny()).ReturnsAsync(new HttpResponseMessage { StatusCode = HttpStatusCode.OK, Content = new StringContent(JsonConvert.SerializeObject(resultData)) }); + + var result = await this.EstateReportingApiClient.GetTodaysFailedSales("", Guid.NewGuid(), 1, 1, "00", DateTime.Now, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + result.Data.ComparisonAverageSalesValue.ShouldBe(TestData.TodaysSales.ComparisonAverageSalesValue); + result.Data.ComparisonSalesCount.ShouldBe(TestData.TodaysSales.ComparisonSalesCount); + result.Data.ComparisonSalesValue.ShouldBe(TestData.TodaysSales.ComparisonSalesValue); + result.Data.TodaysAverageSalesValue.ShouldBe(TestData.TodaysSales.TodaysAverageSalesValue); + result.Data.TodaysSalesCount.ShouldBe(TestData.TodaysSales.TodaysSalesCount); + result.Data.TodaysSalesValue.ShouldBe(TestData.TodaysSales.TodaysSalesValue); + } + + [Fact] + public async Task EstateReportingApiClient_GetTodaysFailedSales_SendAsyncThrowsException_ResultFailed() { + this.HttpMessageHandler.Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny()).ThrowsAsync(new Exception()); + + var result = await this.EstateReportingApiClient.GetTodaysFailedSales("", Guid.NewGuid(), 1, 1, "00", DateTime.Now, CancellationToken.None); + result.IsFailed.ShouldBeTrue(); + } + + [Fact] + public async Task EstateReportingApiClient_GetTodaysSales_SalesReturned() { + var resultData = Result.Success(TestData.TodaysSales); + + this.HttpMessageHandler.Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny()).ReturnsAsync(new HttpResponseMessage { StatusCode = HttpStatusCode.OK, Content = new StringContent(JsonConvert.SerializeObject(resultData)) }); + + var result = await this.EstateReportingApiClient.GetTodaysSales("", Guid.NewGuid(), 1, 1, DateTime.Now, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + result.Data.ComparisonAverageSalesValue.ShouldBe(TestData.TodaysSales.ComparisonAverageSalesValue); + result.Data.ComparisonSalesCount.ShouldBe(TestData.TodaysSales.ComparisonSalesCount); + result.Data.ComparisonSalesValue.ShouldBe(TestData.TodaysSales.ComparisonSalesValue); + result.Data.TodaysAverageSalesValue.ShouldBe(TestData.TodaysSales.TodaysAverageSalesValue); + result.Data.TodaysSalesCount.ShouldBe(TestData.TodaysSales.TodaysSalesCount); + result.Data.TodaysSalesValue.ShouldBe(TestData.TodaysSales.TodaysSalesValue); + } + + [Fact] + public async Task EstateReportingApiClient_GetTodaysSales_SendAsyncThrowsException_ResultFailed() { + this.HttpMessageHandler.Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny()).ThrowsAsync(new Exception()); + + var result = await this.EstateReportingApiClient.GetTodaysSales("", Guid.NewGuid(), 1, 1, DateTime.Now, CancellationToken.None); + result.IsFailed.ShouldBeTrue(); + } + + [Fact] + public async Task EstateReportingApiClient_GetTodaysSalesCountByHour_CountsReturned() { + var resultData = Result.Success(TestData.TodaysSalesCountByHourList); + + this.HttpMessageHandler.Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny()).ReturnsAsync(new HttpResponseMessage { StatusCode = HttpStatusCode.OK, Content = new StringContent(JsonConvert.SerializeObject(resultData)) }); + + var result = await this.EstateReportingApiClient.GetTodaysSalesCountByHour("", Guid.NewGuid(), 1, 1, DateTime.Now, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + result.Data.Count.ShouldBe(TestData.TodaysSalesCountByHourList.Count); + } + + [Fact] + public async Task EstateReportingApiClient_GetTodaysSalesCountByHour_SendAsyncThrowsException_ResultFailed() { + this.HttpMessageHandler.Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny()).ThrowsAsync(new Exception()); + + var result = await this.EstateReportingApiClient.GetTodaysSalesCountByHour("", Guid.NewGuid(), 1, 1, DateTime.Now, CancellationToken.None); + result.IsFailed.ShouldBeTrue(); + } + + [Fact] + public async Task EstateReportingApiClient_GetTodaysSalesValueByHour_ValuesReturned() { + var resultData = Result.Success(TestData.TodaysSalesValueByHourList); + + this.HttpMessageHandler.Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny()).ReturnsAsync(new HttpResponseMessage { StatusCode = HttpStatusCode.OK, Content = new StringContent(JsonConvert.SerializeObject(resultData)) }); + + var result = await this.EstateReportingApiClient.GetTodaysSalesValueByHour("", Guid.NewGuid(), 1, 1, DateTime.Now, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + result.Data.Count.ShouldBe(TestData.TodaysSalesValueByHourList.Count); + } + + [Fact] + public async Task EstateReportingApiClient_GetTodaysSalesValueByHour_SendAsyncThrowsException_ResultFailed() { + this.HttpMessageHandler.Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny()).ThrowsAsync(new Exception()); + + var result = await this.EstateReportingApiClient.GetTodaysSalesValueByHour("", Guid.NewGuid(), 1, 1, DateTime.Now, CancellationToken.None); + result.IsFailed.ShouldBeTrue(); + } + + [Fact] + public async Task EstateReportingApiClient_GetTodaysSettlement_SettlementReturned() { + var resultData = Result.Success(TestData.TodaysSettlement); + + this.HttpMessageHandler.Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny()).ReturnsAsync(new HttpResponseMessage { StatusCode = HttpStatusCode.OK, Content = new StringContent(JsonConvert.SerializeObject(resultData)) }); + + var result = await this.EstateReportingApiClient.GetTodaysSettlement("", Guid.NewGuid(), 1, 1, DateTime.Now, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + result.Data.ComparisonPendingSettlementCount.ShouldBe(TestData.TodaysSettlement.ComparisonPendingSettlementCount); + result.Data.ComparisonPendingSettlementValue.ShouldBe(TestData.TodaysSettlement.ComparisonPendingSettlementValue); + result.Data.ComparisonSettlementCount.ShouldBe(TestData.TodaysSettlement.ComparisonSettlementCount); + result.Data.ComparisonSettlementValue.ShouldBe(TestData.TodaysSettlement.ComparisonSettlementValue); + result.Data.TodaysPendingSettlementCount.ShouldBe(TestData.TodaysSettlement.TodaysPendingSettlementCount); + result.Data.TodaysPendingSettlementValue.ShouldBe(TestData.TodaysSettlement.TodaysPendingSettlementValue); + result.Data.TodaysSettlementCount.ShouldBe(TestData.TodaysSettlement.TodaysSettlementCount); + result.Data.TodaysSettlementValue.ShouldBe(TestData.TodaysSettlement.TodaysSettlementValue); + } + + [Fact] + public async Task EstateReportingApiClient_GetTodaysSettlement_SendAsyncThrowsException_ResultFailed() { + this.HttpMessageHandler.Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny()).ThrowsAsync(new Exception()); + + var result = await this.EstateReportingApiClient.GetTodaysSettlement("", Guid.NewGuid(), 1, 1, DateTime.Now, CancellationToken.None); + result.IsFailed.ShouldBeTrue(); + } + + [Fact] + public async Task EstateReportingApiClient_GetTopBottomMerchantData_DataReturned() { + var resultData = Result.Success(TestData.TopBottomMerchantDataList); + + this.HttpMessageHandler.Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny()).ReturnsAsync(new HttpResponseMessage { StatusCode = HttpStatusCode.OK, Content = new StringContent(JsonConvert.SerializeObject(resultData)) }); + + var result = await this.EstateReportingApiClient.GetTopBottomMerchantData("", Guid.NewGuid(), TopBottom.Top, 5, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + result.Data.Count.ShouldBe(TestData.TopBottomMerchantDataList.Count); + } + + [Fact] + public async Task EstateReportingApiClient_GetTopBottomMerchantData_SendAsyncThrowsException_ResultFailed() { + this.HttpMessageHandler.Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny()).ThrowsAsync(new Exception()); + + var result = await this.EstateReportingApiClient.GetTopBottomMerchantData("", Guid.NewGuid(), TopBottom.Top, 5, CancellationToken.None); + result.IsFailed.ShouldBeTrue(); + } + + [Fact] + public async Task EstateReportingApiClient_GetTopBottomOperatorData_DataReturned() { + var resultData = Result.Success(TestData.TopBottomOperatorDataList); + + this.HttpMessageHandler.Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny()).ReturnsAsync(new HttpResponseMessage { StatusCode = HttpStatusCode.OK, Content = new StringContent(JsonConvert.SerializeObject(resultData)) }); + + var result = await this.EstateReportingApiClient.GetTopBottomOperatorData("", Guid.NewGuid(), TopBottom.Top, 5, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + result.Data.Count.ShouldBe(TestData.TopBottomOperatorDataList.Count); + } + + [Fact] + public async Task EstateReportingApiClient_GetTopBottomOperatorData_SendAsyncThrowsException_ResultFailed() { + this.HttpMessageHandler.Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny()).ThrowsAsync(new Exception()); + + var result = await this.EstateReportingApiClient.GetTopBottomOperatorData("", Guid.NewGuid(), TopBottom.Top, 5, CancellationToken.None); + result.IsFailed.ShouldBeTrue(); + } + + [Fact] + public async Task EstateReportingApiClient_GetTopBottomProductData_DataReturned() { + var resultData = Result.Success(TestData.TopBottomProductDataList); + + this.HttpMessageHandler.Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny()).ReturnsAsync(new HttpResponseMessage { StatusCode = HttpStatusCode.OK, Content = new StringContent(JsonConvert.SerializeObject(resultData)) }); + + var result = await this.EstateReportingApiClient.GetTopBottomProductData("", Guid.NewGuid(), TopBottom.Top, 5, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + result.Data.Count.ShouldBe(TestData.TopBottomProductDataList.Count); + } + + [Fact] + public async Task EstateReportingApiClient_GetTopBottomProductData_SendAsyncThrowsException_ResultFailed() { + this.HttpMessageHandler.Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny()).ThrowsAsync(new Exception()); + + var result = await this.EstateReportingApiClient.GetTopBottomProductData("", Guid.NewGuid(), TopBottom.Top, 5, CancellationToken.None); + result.IsFailed.ShouldBeTrue(); + } + } + + public static class TestData { + + public static TodaysSales TodaysSales = new TodaysSales { + TodaysSalesValue = 1000, + TodaysAverageSalesValue = 100, + TodaysSalesCount = 10, + ComparisonSalesValue = 900, + ComparisonAverageSalesValue = 90, + ComparisonSalesCount = 9 + }; + + public static LastSettlement LastSettlement = new LastSettlement { SalesCount = 1, FeesValue = 1, SalesValue = 1, SettlementDate = new DateTime(2024, 1, 1) }; + + public static List ComparisonDateList = new List { new ComparisonDate { Date = new DateTime(2024, 1, 1), Description = "Jan 1 2024" }, new ComparisonDate { Date = new DateTime(2024, 2, 1), Description = "Feb 1 2024" } }; + + public static List CalendarDateList = new List { new CalendarDate { Date = new DateTime(2024, 1, 1) }, new CalendarDate { Date = new DateTime(2024, 1, 2) }, new CalendarDate { Date = new DateTime(2024, 1, 3) } }; + + public static List CalendarYearList = new List { new CalendarYear { Year = 2024 }, new CalendarYear { Year = 2025 }, new CalendarYear { Year = 2026 } }; + + public static List ResponseCodeList = new List { new ResponseCode { Code = 1, Description = "Success" }, new ResponseCode { Code = 2, Description = "Failure" } }; + + public static List MerchantList = new List { new Merchant { MerchantId = Guid.NewGuid(), Name = "Merchant 1" }, new Merchant { MerchantId = Guid.NewGuid(), Name = "Merchant 2" } }; + + public static List OperatorList = new List { new Operator { OperatorId = Guid.NewGuid(), Name = "Operator 1" }, new Operator { OperatorId = Guid.NewGuid(), Name = "Operator 2" } }; + + public static List TransactionResultList = new List { new TransactionResult { TransactionId = Guid.NewGuid(), IsAuthorised = true, ResponseCode = "00", TransactionAmount = 100 }, new TransactionResult { TransactionId = Guid.NewGuid(), IsAuthorised = false, ResponseCode = "01", TransactionAmount = 200 } }; + + public static List UnsettledFeeList = new List { new UnsettledFee { DimensionName = "Fee 1", FeesValue = 100, FeesCount = 10 }, new UnsettledFee { DimensionName = "Fee 2", FeesValue = 200, FeesCount = 20 } }; + + public static MerchantKpi MerchantKpi = new MerchantKpi { MerchantsWithNoSaleInLast7Days = 1, MerchantsWithNoSaleToday = 2, MerchantsWithSaleInLastHour = 3 }; + + public static List TodaysSalesCountByHourList = new List { new TodaysSalesCountByHour { Hour = 1, TodaysSalesCount = 10, ComparisonSalesCount = 9 }, new TodaysSalesCountByHour { Hour = 2, TodaysSalesCount = 20, ComparisonSalesCount = 18 } }; + + public static List TodaysSalesValueByHourList = new List { new TodaysSalesValueByHour { Hour = 1, TodaysSalesValue = 100, ComparisonSalesValue = 90 }, new TodaysSalesValueByHour { Hour = 2, TodaysSalesValue = 200, ComparisonSalesValue = 180 } }; + + public static TodaysSettlement TodaysSettlement = new TodaysSettlement { + TodaysSettlementValue = 1000, + TodaysPendingSettlementValue = 500, + TodaysSettlementCount = 10, + TodaysPendingSettlementCount = 5, + ComparisonSettlementValue = 900, + ComparisonPendingSettlementValue = 450, + ComparisonSettlementCount = 9, + ComparisonPendingSettlementCount = 4 + }; + + public static List TopBottomMerchantDataList = new List { new TopBottomMerchantData { MerchantName = "Merchant 1", SalesValue = 1000 }, new TopBottomMerchantData { MerchantName = "Merchant 2", SalesValue = 900 } }; + + public static List TopBottomOperatorDataList = new List { new TopBottomOperatorData { OperatorName = "Operator 1", SalesValue = 1000 }, new TopBottomOperatorData { OperatorName = "Operator 2", SalesValue = 900 } }; + + public static List TopBottomProductDataList = new List { new TopBottomProductData { ProductName = "Product 1", SalesValue = 1000 }, new TopBottomProductData { ProductName = "Product 2", SalesValue = 900 } }; + } +} diff --git a/EstateReportingAPI.Tests/General/BootstrapperTests.cs b/EstateReportingAPI.Tests/General/BootstrapperTests.cs index 0b3b14e..6f13675 100644 --- a/EstateReportingAPI.Tests/General/BootstrapperTests.cs +++ b/EstateReportingAPI.Tests/General/BootstrapperTests.cs @@ -13,7 +13,6 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Moq; - using Shouldly; using Xunit; [Collection("TestCollection")] @@ -71,32 +70,4 @@ private void AddTestRegistrations(ServiceRegistry services, services.AddSingleton(Startup.Configuration); } } - - public class QueryStringBuilderTests - { - [Theory] - [InlineData("StringTest", "value",true)] - [InlineData("IntegerTest", 10, true)] - [InlineData("DecimalTest1", 1.00, true)] - [InlineData("DecimalTest2", 0.01, true)] - [InlineData("GuidTest", "69F35754-EB7D-441B-A62F-F50633AFBE91", true)] - [InlineData("NullTest", null, false)] - [InlineData("EmptyStringTest", "", false)] - [InlineData("ZeroIntegerTest", 0, false)] - [InlineData("ZeroDecimalTest1", 0, false)] - [InlineData("ZeroDecimalTest2", 0.00, false)] - [InlineData("EmptyGuidTest", "00000000-0000-0000-0000-000000000000", false)] - public void QueryStringBuilderTests_BuildQueryString_ValuesAddedAsExpected(String keyname, Object value, Boolean isAdded){ - if (keyname == "GuidTest" || keyname == "EmptyGuidTest") - value = Guid.Parse(value.ToString()); - - QueryStringBuilder builder = new QueryStringBuilder(); - builder.AddParameter(keyname, value); - - String queryString = builder.BuildQueryString(); - - queryString.Contains(keyname).ShouldBe(isAdded); - - } - } } diff --git a/EstateReportingAPI.Tests/General/QueryStringBuilderTests.cs b/EstateReportingAPI.Tests/General/QueryStringBuilderTests.cs new file mode 100644 index 0000000..cc31276 --- /dev/null +++ b/EstateReportingAPI.Tests/General/QueryStringBuilderTests.cs @@ -0,0 +1,33 @@ +using System; +using Shouldly; +using Xunit; + +namespace EstateReportingAPI.Tests.General; + +public class QueryStringBuilderTests +{ + [Theory] + [InlineData("StringTest", "value",true)] + [InlineData("IntegerTest", 10, true)] + [InlineData("DecimalTest1", 1.00, true)] + [InlineData("DecimalTest2", 0.01, true)] + [InlineData("GuidTest", "69F35754-EB7D-441B-A62F-F50633AFBE91", true)] + [InlineData("NullTest", null, false)] + [InlineData("EmptyStringTest", "", false)] + [InlineData("ZeroIntegerTest", 0, false)] + [InlineData("ZeroDecimalTest1", 0, false)] + [InlineData("ZeroDecimalTest2", 0.00, false)] + [InlineData("EmptyGuidTest", "00000000-0000-0000-0000-000000000000", false)] + public void QueryStringBuilderTests_BuildQueryString_ValuesAddedAsExpected(String keyname, Object value, Boolean isAdded){ + if (keyname == "GuidTest" || keyname == "EmptyGuidTest") + value = Guid.Parse(value.ToString()); + + QueryStringBuilder builder = new QueryStringBuilder(); + builder.AddParameter(keyname, value); + + String queryString = builder.BuildQueryString(); + + queryString.Contains(keyname).ShouldBe(isAdded); + + } +} \ No newline at end of file diff --git a/EstateReportingAPI/Bootstrapper/MediatorRegistry.cs b/EstateReportingAPI/Bootstrapper/MediatorRegistry.cs index 42e6759..a12e0a0 100644 --- a/EstateReportingAPI/Bootstrapper/MediatorRegistry.cs +++ b/EstateReportingAPI/Bootstrapper/MediatorRegistry.cs @@ -1,12 +1,14 @@ -using EstateReportingAPI.BusinessLogic.Queries; -using EstateReportingAPI.BusinessLogic.RequestHandlers; +using EstateReportingAPI.BusinessLogic.RequestHandlers; using EstateReportingAPI.Models; using Lamar; using MediatR; using SimpleResults; +using System.Diagnostics.CodeAnalysis; +using EstateReportingAPI.BusinessLogic.Queries; namespace EstateReportingAPI.Bootstrapper; +[ExcludeFromCodeCoverage] public class MediatorRegistry : ServiceRegistry { public MediatorRegistry() { this.AddTransient(); diff --git a/EstateReportingAPI/Bootstrapper/MiddlewareRegistry.cs b/EstateReportingAPI/Bootstrapper/MiddlewareRegistry.cs index ba7896e..97a71ae 100644 --- a/EstateReportingAPI/Bootstrapper/MiddlewareRegistry.cs +++ b/EstateReportingAPI/Bootstrapper/MiddlewareRegistry.cs @@ -100,9 +100,9 @@ public MiddlewareRegistry() Assembly assembly = this.GetType().GetTypeInfo().Assembly; this.AddMvcCore().AddApplicationPart(assembly).AddControllersAsServices(); - bool logRequests = ConfigurationReaderExtensions.GetValueOrDefault("MiddlewareLogging", "LogRequests", true); - bool logResponses = ConfigurationReaderExtensions.GetValueOrDefault("MiddlewareLogging", "LogResponses", true); - LogLevel middlewareLogLevel = ConfigurationReaderExtensions.GetValueOrDefault("MiddlewareLogging", "MiddlewareLogLevel", LogLevel.Warning); + bool logRequests = ConfigurationReader.GetValueOrDefault("MiddlewareLogging", "LogRequests", true); + bool logResponses = ConfigurationReader.GetValueOrDefault("MiddlewareLogging", "LogResponses", true); + LogLevel middlewareLogLevel = ConfigurationReader.GetValueOrDefault("MiddlewareLogging", "MiddlewareLogLevel", LogLevel.Warning); RequestResponseMiddlewareLoggingConfig config = new RequestResponseMiddlewareLoggingConfig(middlewareLogLevel, logRequests, logResponses); @@ -121,26 +121,4 @@ private HttpClientHandler ApiEndpointHttpHandler(IServiceProvider serviceProvide }; } } - - public static class ConfigurationReaderExtensions - { - public static T GetValueOrDefault(String sectionName, String keyName, T defaultValue) - { - try - { - var value = ConfigurationReader.GetValue(sectionName, keyName); - - if (String.IsNullOrEmpty(value)) - { - return defaultValue; - } - - return (T)Convert.ChangeType(value, typeof(T)); - } - catch (KeyNotFoundException kex) - { - return defaultValue; - } - } - } } \ No newline at end of file diff --git a/EstateReportingAPI/Bootstrapper/RepositoryRegistry.cs b/EstateReportingAPI/Bootstrapper/RepositoryRegistry.cs index 8766cae..ee97851 100644 --- a/EstateReportingAPI/Bootstrapper/RepositoryRegistry.cs +++ b/EstateReportingAPI/Bootstrapper/RepositoryRegistry.cs @@ -8,7 +8,9 @@ using Shared.EntityFramework.ConnectionStringConfiguration; using Shared.General; using Shared.Repositories; +using System.Diagnostics.CodeAnalysis; +[ExcludeFromCodeCoverage] public class RepositoryRegistry : ServiceRegistry{ public RepositoryRegistry(){ diff --git a/EstateReportingAPI/Controllers/FactSettlementController.cs b/EstateReportingAPI/Controllers/FactSettlementController.cs index efcb4af..3e67749 100644 --- a/EstateReportingAPI/Controllers/FactSettlementController.cs +++ b/EstateReportingAPI/Controllers/FactSettlementController.cs @@ -92,14 +92,14 @@ public async Task LastSettlement([FromHeader] Guid estateId, public async Task GetUnsettledFees([FromHeader] Guid estateId, [FromQuery] DateTime startDate, [FromQuery] DateTime endDate, - [FromQuery] string? merchantReportingIds, [FromQuery] string? operatorReportingIds, - [FromQuery] string? productReportingIds, + [FromQuery] string? merchantIds, [FromQuery] string? operatorIds, + [FromQuery] string? productIds, [FromQuery] GroupByOption? groupByOption, CancellationToken cancellationToken) { List merchantIdFilter = new List(); - if (String.IsNullOrEmpty(merchantReportingIds) == false) + if (String.IsNullOrEmpty(merchantIds) == false) { - List merchantListStrings = merchantReportingIds.Split(',').ToList(); + List merchantListStrings = merchantIds.Split(',').ToList(); foreach (String merchantListString in merchantListStrings) { merchantIdFilter.Add(Int32.Parse(merchantListString)); @@ -107,9 +107,9 @@ public async Task GetUnsettledFees([FromHeader] Guid estateId, } List operatorIdFilter = new List(); - if (String.IsNullOrEmpty(operatorReportingIds) == false) + if (String.IsNullOrEmpty(operatorIds) == false) { - List operatorListStrings = operatorReportingIds.Split(',').ToList(); + List operatorListStrings = operatorIds.Split(',').ToList(); foreach (String operatorListString in operatorListStrings) { operatorIdFilter.Add(Int32.Parse(operatorListString)); @@ -117,9 +117,9 @@ public async Task GetUnsettledFees([FromHeader] Guid estateId, } List productIdFilter = new List(); - if (String.IsNullOrEmpty(productReportingIds) == false) + if (String.IsNullOrEmpty(productIds) == false) { - List productListStrings = productReportingIds.Split(',').ToList(); + List productListStrings = productIds.Split(',').ToList(); foreach (String productListString in productListStrings) { productIdFilter.Add(Int32.Parse(productListString)); diff --git a/EstateReportingAPI/Startup.cs b/EstateReportingAPI/Startup.cs index 6e6d7a3..0c982e4 100644 --- a/EstateReportingAPI/Startup.cs +++ b/EstateReportingAPI/Startup.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using EstateReportingAPI.Bootstrapper; using HealthChecks.UI.Client; using Lamar; @@ -10,6 +11,7 @@ namespace EstateReportingAPI { using BusinessLogic; + [ExcludeFromCodeCoverage] public class Startup { public static IConfigurationRoot Configuration { get; set; } From 6fd45f8d83d09881aaa7220db132d12a27de1fd1 Mon Sep 17 00:00:00 2001 From: Stuart Ferguson Date: Mon, 23 Dec 2024 16:24:18 +0000 Subject: [PATCH 2/2] :| --- EstateReportingAPI.IntegrationTests/xunit.runner.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EstateReportingAPI.IntegrationTests/xunit.runner.json b/EstateReportingAPI.IntegrationTests/xunit.runner.json index c565a6f..73f7b83 100644 --- a/EstateReportingAPI.IntegrationTests/xunit.runner.json +++ b/EstateReportingAPI.IntegrationTests/xunit.runner.json @@ -1,5 +1,5 @@ { - "maxParallelThreads": 10, + "maxParallelThreads": 1, "parallelizeTestCollections": true } \ No newline at end of file