diff --git a/EstateReportingAPI.BusinessLogic/DatabaseProjections.cs b/EstateReportingAPI.BusinessLogic/DatabaseProjections.cs index 7d6fd5e..73d8cd3 100644 --- a/EstateReportingAPI.BusinessLogic/DatabaseProjections.cs +++ b/EstateReportingAPI.BusinessLogic/DatabaseProjections.cs @@ -9,6 +9,7 @@ using TransactionProcessor.Database.Contexts; using TransactionProcessor.Database.Entities; using TransactionProcessor.Database.Entities.Summary; +using ContractProduct = TransactionProcessor.Database.Entities.ContractProduct; using Merchant = TransactionProcessor.Database.Entities.Merchant; using Operator = TransactionProcessor.Database.Entities.Operator; diff --git a/EstateReportingAPI.BusinessLogic/EstateReportingAPI.BusinessLogic.csproj b/EstateReportingAPI.BusinessLogic/EstateReportingAPI.BusinessLogic.csproj index 57eb1c4..b77b8a9 100644 --- a/EstateReportingAPI.BusinessLogic/EstateReportingAPI.BusinessLogic.csproj +++ b/EstateReportingAPI.BusinessLogic/EstateReportingAPI.BusinessLogic.csproj @@ -10,7 +10,7 @@ - + diff --git a/EstateReportingAPI.BusinessLogic/IReportingManager.cs b/EstateReportingAPI.BusinessLogic/IReportingManager.cs deleted file mode 100644 index 7a51dee..0000000 --- a/EstateReportingAPI.BusinessLogic/IReportingManager.cs +++ /dev/null @@ -1,33 +0,0 @@ -namespace EstateReportingAPI.BusinessLogic; - -using Microsoft.VisualBasic.CompilerServices; -using Models; - -public interface IReportingManager{ - #region Methods - Task> GetUnsettledFees(Guid estateId, DateTime startDate, DateTime endDate, List merchantIds, List operatorIds, List productIds, GroupByOption? groupByOption, CancellationToken cancellationToken); - Task> GetCalendarComparisonDates(Guid estateId, CancellationToken cancellationToken); - Task> GetCalendarDates(Guid estateId, CancellationToken cancellationToken); - Task> GetCalendarYears(Guid estateId, CancellationToken cancellationToken); - Task GetLastSettlement(Guid estateId, CancellationToken cancellationToken); - Task> GetMerchants(Guid estateId, CancellationToken cancellationToken); - Task GetMerchantsTransactionKpis(Guid estateId, CancellationToken cancellationToken); - Task> GetMerchantsByLastSale(Guid estateId, DateTime startDateTime, DateTime endDateTime, CancellationToken cancellationToken); - Task> GetOperators(Guid estateId, CancellationToken cancellationToken); - Task> GetResponseCodes(Guid estateId, CancellationToken cancellationToken); - Task GetTodaysFailedSales(Guid estateId, DateTime comparisonDate, String responseCode, CancellationToken cancellationToken); - Task GetTodaysSales(Guid estateId, Int32 merchantReportingId, Int32 operatorReportingId, DateTime comparisonDate, CancellationToken cancellationToken); - Task> GetTodaysSalesCountByHour(Guid estateId, Int32 merchantReportingId, Int32 operatorReportingId, DateTime comparisonDate, CancellationToken cancellationToken); - Task> GetTodaysSalesValueByHour(Guid estateId, Int32 merchantReportingId, Int32 operatorReportingId, DateTime comparisonDate, CancellationToken cancellationToken); - Task GetTodaysSettlement(Guid estateId, Int32 merchantReportingId, Int32 operatorReportingId, DateTime comparisonDate, CancellationToken cancellationToken); - Task> GetTopBottomData(Guid estateId, TopBottom direction, Int32 resultCount, Dimension dimension, CancellationToken cancellationToken); - - Task GetMerchantPerformance(Guid estateId, DateTime comparisonDate, List merchantReportingIds, CancellationToken cancellationToken); - Task GetProductPerformance(Guid estateId, DateTime comparisonDate, List productReportingIds, CancellationToken cancellationToken); - - Task GetOperatorPerformance(Guid estateId, DateTime comparisonDate, List operatorReportingIds, CancellationToken cancellationToken); - - Task> TransactionSearch(Guid estateId, TransactionSearchRequest searchRequest, PagingRequest pagingRequest, SortingRequest sortingRequest, CancellationToken cancellationToken); - - #endregion -} \ No newline at end of file diff --git a/EstateReportingAPI.BusinessLogic/OldReportingManager.cs b/EstateReportingAPI.BusinessLogic/OldReportingManager.cs new file mode 100644 index 0000000..9df5765 --- /dev/null +++ b/EstateReportingAPI.BusinessLogic/OldReportingManager.cs @@ -0,0 +1,496 @@ +using EstateReportingAPI.Models; +using Microsoft.EntityFrameworkCore; +using Shared.EntityFramework; +using TransactionProcessor.Database.Contexts; +using TransactionProcessor.Database.Entities; +using TransactionProcessor.Database.Entities.Summary; +using Merchant = EstateReportingAPI.Models.Merchant; +using Operator = EstateReportingAPI.Models.Operator; + +namespace EstateReportingAPI.BusinessLogic; + +/* +public interface IOldReportingManager +{ + Task> GetUnsettledFees(Guid estateId, DateTime startDate, DateTime endDate, List merchantIds, List operatorIds, List productIds, GroupByOption? groupByOption, CancellationToken cancellationToken); + Task GetLastSettlement(Guid estateId, CancellationToken cancellationToken); + Task GetMerchantsTransactionKpis(Guid estateId, CancellationToken cancellationToken); + Task> GetMerchantsByLastSale(Guid estateId, DateTime startDateTime, DateTime endDateTime, CancellationToken cancellationToken); + + Task> GetResponseCodes(Guid estateId, CancellationToken cancellationToken); + Task> GetTodaysSalesCountByHour(Guid estateId, Int32 merchantReportingId, Int32 operatorReportingId, DateTime comparisonDate, CancellationToken cancellationToken); + Task> GetTodaysSalesValueByHour(Guid estateId, Int32 merchantReportingId, Int32 operatorReportingId, DateTime comparisonDate, CancellationToken cancellationToken); + Task GetTodaysSettlement(Guid estateId, Int32 merchantReportingId, Int32 operatorReportingId, DateTime comparisonDate, CancellationToken cancellationToken); + Task> GetTopBottomData(Guid estateId, TopBottom direction, Int32 resultCount, Dimension dimension, CancellationToken cancellationToken); + + Task GetMerchantPerformance(Guid estateId, DateTime comparisonDate, List merchantReportingIds, CancellationToken cancellationToken); + Task GetProductPerformance(Guid estateId, DateTime comparisonDate, List productReportingIds, CancellationToken cancellationToken); + + Task GetOperatorPerformance(Guid estateId, DateTime comparisonDate, List operatorReportingIds, CancellationToken cancellationToken); + + Task> TransactionSearch(Guid estateId, TransactionSearchRequest searchRequest, PagingRequest pagingRequest, SortingRequest sortingRequest, CancellationToken cancellationToken); + +} + +public class OldReportingManager : IOldReportingManager +{ + private readonly IDbContextResolver Resolver; + private static readonly String EstateManagementDatabaseName = "TransactionProcessorReadModel"; + + private IQueryable BuildTodaySalesQuery(EstateManagementContext context) + { + return from t in context.TodayTransactions where t.IsAuthorised && t.TransactionType == "Sale" && t.TransactionDate == DateTime.Now.Date && t.TransactionTime <= DateTime.Now.TimeOfDay select t; + } + + private IQueryable BuildComparisonSalesQuery(EstateManagementContext context, + DateTime comparisonDate) + { + return from t in context.TransactionHistory where t.IsAuthorised && t.TransactionType == "Sale" && t.TransactionDate == comparisonDate && t.TransactionTime <= DateTime.Now.TimeOfDay select t; + } + + private IQueryable BuildUnsettledFeesQuery(EstateManagementContext context, + DateTime startDate, + DateTime endDate) + { + return 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 DatabaseProjections.FeeTransactionProjection { Fee = merchantSettlementFee, Txn = transaction }; + } + + private IQueryable BuildTransactionSearchQuery(EstateManagementContext context, + DateTime queryDate) + { + return from txn in context.Transactions join merchant in context.Merchants on txn.MerchantId equals merchant.MerchantId join @operator in context.Operators on txn.OperatorId equals @operator.OperatorId join product in context.ContractProducts on txn.ContractProductId equals product.ContractProductId where txn.TransactionDate == queryDate select new DatabaseProjections.TransactionSearchProjection { Transaction = txn, Merchant = merchant, Operator = @operator, Product = product }; + } + + private IQueryable BuildTodaySettlementQuery(EstateManagementContext context, + DateTime settlementDate) + { + IQueryable settlementData = from s in context.Settlements join f in context.MerchantSettlementFees on s.SettlementId equals f.SettlementId join t in context.TodayTransactions on f.TransactionId equals t.TransactionId where s.SettlementDate == settlementDate select new DatabaseProjections.TodaySettlementTransactionProjection { Fee = f, Txn = t }; + return settlementData; + } + + private IQueryable BuildComparisonSettlementQuery(EstateManagementContext context, + DateTime settlementDate) + { + IQueryable settlementData = from s in context.Settlements join f in context.MerchantSettlementFees on s.SettlementId equals f.SettlementId join t in context.TransactionHistory on f.TransactionId equals t.TransactionId where s.SettlementDate == settlementDate.Date select new DatabaseProjections.ComparisonSettlementTransactionProjection { Fee = f, Txn = t }; + return settlementData; + } + + private async Task GetSettlementSummary(IQueryable query, + CancellationToken cancellationToken) + { + // Get the settleed fees summary + DatabaseProjections.SettlementGroupProjection summary = await BuildSettlementSummaryQuery(query).SingleOrDefaultAsync(cancellationToken); + + return new DatabaseProjections.SettlementGroupProjection + { + SettledCount = summary.SettledCount, + SettledValue = summary.SettledValue, + UnSettledCount = summary.UnSettledCount, + UnSettledValue = summary.UnSettledValue + }; + } + + private static IQueryable BuildSettlementSummaryQuery( + IQueryable query) + { + return query + .GroupBy(_ => 1) + .Select(g => new DatabaseProjections.SettlementGroupProjection + { + SettledCount = g.Count(x => x.Fee.IsSettled), + SettledValue = g.Where(x => x.Fee.IsSettled).Sum(x => x.Fee.CalculatedValue), + UnSettledCount = g.Count(x => !x.Fee.IsSettled), + UnSettledValue = g.Where(x => !x.Fee.IsSettled).Sum(x => x.Fee.CalculatedValue) + }); + } + + private static IQueryable BuildSettlementSummaryQuery( + IQueryable query) + { + return query + .GroupBy(_ => 1) + .Select(g => new DatabaseProjections.SettlementGroupProjection + { + SettledCount = g.Count(x => x.Fee.IsSettled), + SettledValue = g.Where(x => x.Fee.IsSettled).Sum(x => x.Fee.CalculatedValue), + UnSettledCount = g.Count(x => !x.Fee.IsSettled), + UnSettledValue = g.Where(x => !x.Fee.IsSettled).Sum(x => x.Fee.CalculatedValue) + }); + } + + private async Task GetSettlementSummary( + IQueryable query, + CancellationToken cancellationToken) + { + + // Get the settleed fees summary + DatabaseProjections.SettlementGroupProjection summary = await BuildSettlementSummaryQuery(query).SingleOrDefaultAsync(cancellationToken); + + return new DatabaseProjections.SettlementGroupProjection + { + SettledCount = summary.SettledCount, + SettledValue = summary.SettledValue, + UnSettledCount = summary.UnSettledCount, + UnSettledValue = summary.UnSettledValue + }; + } + private const String ConnectionStringIdentifier = "EstateReportingReadModel"; + + public async Task GetLastSettlement(Guid estateId, + CancellationToken cancellationToken) + { + using ResolvedDbContext? resolvedContext = this.Resolver.Resolve(EstateManagementDatabaseName, estateId.ToString()); + await using EstateManagementContext context = resolvedContext.Context; + + DateTime settlementDate = await context.SettlementSummary.Where(s => s.IsCompleted).OrderByDescending(s => s.SettlementDate).Select(s => s.SettlementDate).FirstOrDefaultAsync(cancellationToken); + + IQueryable settlements = from settlement in context.SettlementSummary where settlement.SettlementDate == settlementDate group new { settlement.SettlementDate, FeeValue = settlement.FeeValue.GetValueOrDefault(), SalesValue = settlement.SalesValue.GetValueOrDefault(), SalesCount = settlement.SalesCount.GetValueOrDefault() } by settlement.SettlementDate into grouped select new LastSettlement { FeesValue = grouped.Sum(g => g.FeeValue), SalesCount = grouped.Sum(g => g.SalesCount), SalesValue = grouped.Sum(g => g.SalesValue), SettlementDate = grouped.Key }; + + return await settlements.SingleOrDefaultAsync(cancellationToken); + } + + public async Task GetMerchantsTransactionKpis(Guid estateId, + CancellationToken cancellationToken) + { + using ResolvedDbContext? resolvedContext = this.Resolver.Resolve(EstateManagementDatabaseName, estateId.ToString()); + await using EstateManagementContext context = resolvedContext.Context; + + Int32 merchantsWithSaleInLastHour = (from m in context.Merchants where m.LastSaleDate == DateTime.Now.Date && m.LastSaleDateTime >= DateTime.Now.AddHours(-1) select m.MerchantReportingId).Count(); + + Int32 merchantsWithNoSaleToday = (from m in context.Merchants where m.LastSaleDate == DateTime.Now.Date.AddDays(-1) select m.MerchantReportingId).Count(); + + Int32 merchantsWithNoSaleInLast7Days = (from m in context.Merchants where m.LastSaleDate <= DateTime.Now.Date.AddDays(-7) select m.MerchantReportingId).Count(); + + MerchantKpi response = new() { MerchantsWithSaleInLastHour = merchantsWithSaleInLastHour, MerchantsWithNoSaleToday = merchantsWithNoSaleToday, MerchantsWithNoSaleInLast7Days = merchantsWithNoSaleInLast7Days }; + + return response; + } + + public async Task> GetMerchantsByLastSale(Guid estateId, + DateTime startDateTime, + DateTime endDateTime, + CancellationToken cancellationToken) + { + using ResolvedDbContext? resolvedContext = this.Resolver.Resolve(EstateManagementDatabaseName, estateId.ToString()); + await using EstateManagementContext context = resolvedContext.Context; + + List response = new(); + + var merchants = await context.Merchants.Where(m => m.LastSaleDateTime >= startDateTime && m.LastSaleDateTime <= endDateTime).Select(m => new { + MerchantReportingId = m.MerchantReportingId, + EstateReportingId = context.Estates.Single(e => e.EstateId == m.EstateId).EstateReportingId, + Name = m.Name, + LastSaleDateTime = m.LastSaleDateTime, + LastSale = m.LastSaleDate, + CreatedDateTime = m.CreatedDateTime, + LastStatement = m.LastStatementGenerated, + MerchantId = m.MerchantId, + Reference = m.Reference, + AddressInfo = context.MerchantAddresses.Where(ma => ma.MerchantId == m.MerchantId).OrderByDescending(ma => ma.CreatedDateTime).Select(ma => new { + PostCode = ma.PostalCode, + Region = ma.Region, + Town = ma.Town + // Add more properties as needed + }).FirstOrDefault() // Get the first matching MerchantAddress or null + }).ToListAsync(cancellationToken); + + merchants.ForEach(m => response.Add(new Merchant + { + LastSaleDateTime = m.LastSaleDateTime, + CreatedDateTime = m.CreatedDateTime, + EstateReportingId = m.EstateReportingId, + LastSale = m.LastSale, + LastStatement = m.LastStatement, + MerchantId = m.MerchantId, + MerchantReportingId = m.MerchantReportingId, + Name = m.Name, + Reference = m.Reference, + PostCode = m.AddressInfo?.PostCode, + Region = m.AddressInfo?.Region, + Town = m.AddressInfo?.Town + })); + + return response; + } + + public async Task> GetResponseCodes(Guid estateId, + CancellationToken cancellationToken) + { + using ResolvedDbContext? resolvedContext = this.Resolver.Resolve(EstateManagementDatabaseName, estateId.ToString()); + await using EstateManagementContext context = resolvedContext.Context; + List response = new(); + + List responseCodes = await context.ResponseCodes.ToListAsync(cancellationToken); + + responseCodes.ForEach(r => response.Add(new ResponseCode { Code = r.ResponseCode, Description = r.Description })); + + return response; + } + + public async Task> GetTodaysSalesCountByHour(Guid estateId, + Int32 merchantReportingId, + Int32 operatorReportingId, + DateTime comparisonDate, + CancellationToken cancellationToken) + { + using ResolvedDbContext? resolvedContext = this.Resolver.Resolve(EstateManagementDatabaseName, estateId.ToString()); + await using EstateManagementContext context = resolvedContext.Context; + + IQueryable todaysSales = this.BuildTodaySalesQuery(context); + IQueryable comparisonSales = this.BuildComparisonSalesQuery(context, comparisonDate); + + todaysSales = todaysSales.ApplyMerchantFilter(merchantReportingId).ApplyOperatorFilter(operatorReportingId); + comparisonSales = comparisonSales.ApplyMerchantFilter(merchantReportingId).ApplyOperatorFilter(operatorReportingId); + + // First we need to get a value of todays sales + var todaysSalesByHour = await (from t in todaysSales group t.TransactionAmount by t.Hour into g select new { Hour = g.Key, TotalSalesCount = g.Count() }).ToListAsync(cancellationToken); + + var comparisonSalesByHour = await (from t in comparisonSales group t.TransactionAmount by t.Hour into g select new { Hour = g.Key, TotalSalesCount = g.Count() }).ToListAsync(cancellationToken); + + List response = (from today in todaysSalesByHour join comparison in comparisonSalesByHour on today.Hour equals comparison.Hour select new TodaysSalesCountByHour { Hour = today.Hour.Value, TodaysSalesCount = today.TotalSalesCount, ComparisonSalesCount = comparison.TotalSalesCount }).ToList(); + + return response; + } + + public async Task> GetTodaysSalesValueByHour(Guid estateId, + Int32 merchantReportingId, + Int32 operatorReportingId, + DateTime comparisonDate, + CancellationToken cancellationToken) + { + using ResolvedDbContext? resolvedContext = this.Resolver.Resolve(EstateManagementDatabaseName, estateId.ToString()); + await using EstateManagementContext context = resolvedContext.Context; + + IQueryable todaysSales = this.BuildTodaySalesQuery(context); + IQueryable comparisonSales = this.BuildComparisonSalesQuery(context, comparisonDate); + + todaysSales = todaysSales.ApplyMerchantFilter(merchantReportingId).ApplyOperatorFilter(operatorReportingId); + comparisonSales = comparisonSales.ApplyMerchantFilter(merchantReportingId).ApplyOperatorFilter(operatorReportingId); + + // First we need to get a value of todays sales + var todaysSalesByHour = await (from t in todaysSales group t.TransactionAmount by t.Hour into g select new { Hour = g.Key, TotalSalesValue = g.Sum() }).ToListAsync(cancellationToken); + + var comparisonSalesByHour = await (from t in comparisonSales group t.TransactionAmount by t.Hour into g select new { Hour = g.Key, TotalSalesValue = g.Sum() }).ToListAsync(cancellationToken); + + List response = (from today in todaysSalesByHour join comparison in comparisonSalesByHour on today.Hour equals comparison.Hour select new TodaysSalesValueByHour { Hour = today.Hour.Value, TodaysSalesValue = today.TotalSalesValue, ComparisonSalesValue = comparison.TotalSalesValue }).ToList(); + + return response; + } + + public async Task> GetUnsettledFees(Guid estateId, + DateTime startDate, + DateTime endDate, + List merchantIds, + List operatorIds, + List productIds, + GroupByOption? groupByOption, + CancellationToken cancellationToken) + { + using ResolvedDbContext? resolvedContext = this.Resolver.Resolve(EstateManagementDatabaseName, estateId.ToString()); + await using EstateManagementContext context = resolvedContext.Context; + + IQueryable query = this.BuildUnsettledFeesQuery(context, startDate, endDate).ApplyMerchantFilter(context, merchantIds).ApplyOperatorFilter(context, operatorIds).ApplyProductFilter(context, productIds); + + // Perform grouping + IQueryable groupedQuery = groupByOption switch + { + GroupByOption.Merchant => query.ApplyMerchantGrouping(context), + GroupByOption.Operator => query.ApplyOperatorGrouping(context), + GroupByOption.Product => query.ApplyProductGrouping(context) + }; + return await groupedQuery.ToListAsync(cancellationToken); + } + + public async Task> TransactionSearch(Guid estateId, + TransactionSearchRequest searchRequest, + PagingRequest pagingRequest, + SortingRequest sortingRequest, + CancellationToken cancellationToken) + { + // Base query before any filtering is added + using ResolvedDbContext? resolvedContext = this.Resolver.Resolve(EstateManagementDatabaseName, estateId.ToString()); + await using EstateManagementContext context = resolvedContext.Context; + + IQueryable mainQuery = this.BuildTransactionSearchQuery(context, searchRequest.QueryDate); + + mainQuery = mainQuery.ApplyFilters(searchRequest); + mainQuery = mainQuery.ApplySorting(sortingRequest); + mainQuery = mainQuery.ApplyPagination(pagingRequest); + + // Now build the results + List queryResults = await mainQuery.ToListAsync(cancellationToken); + + List results = new(); + + queryResults.ForEach(qr => { + results.Add(new TransactionResult + { + MerchantReportingId = qr.Merchant.MerchantReportingId, + ResponseCode = qr.Transaction.ResponseCode, + IsAuthorised = qr.Transaction.IsAuthorised, + MerchantName = qr.Merchant.Name, + OperatorName = qr.Operator.Name, + OperatorReportingId = qr.Operator.OperatorReportingId, + Product = qr.Product.ProductName, + ProductReportingId = qr.Product.ContractProductReportingId, + ResponseMessage = qr.Transaction.ResponseMessage, + TransactionDateTime = qr.Transaction.TransactionDateTime, + TransactionId = qr.Transaction.TransactionId, + TransactionReportingId = qr.Transaction.TransactionReportingId, + TransactionSource = qr.Transaction.TransactionSource.ToString(), + TransactionAmount = qr.Transaction.TransactionAmount + }); + }); + + return results; + } + + public async Task GetMerchantPerformance(Guid estateId, + DateTime comparisonDate, + List merchantReportingIds, + CancellationToken cancellationToken) + { + using ResolvedDbContext? resolvedContext = this.Resolver.Resolve(EstateManagementDatabaseName, estateId.ToString()); + await using EstateManagementContext context = resolvedContext.Context; + + // First we need to get a value of todays sales + IQueryable todaysSalesQuery = this.BuildTodaySalesQuery(context); + IQueryable comparisonSalesQuery = this.BuildComparisonSalesQuery(context, comparisonDate); + + todaysSalesQuery = todaysSalesQuery.ApplyMerchantFilter(merchantReportingIds); + comparisonSalesQuery = comparisonSalesQuery.ApplyMerchantFilter(merchantReportingIds); + + // Build the response + TodaysSales response = new() { ComparisonSalesCount = await comparisonSalesQuery.CountAsync(cancellationToken), ComparisonSalesValue = await comparisonSalesQuery.SumAsync(t => t.TransactionAmount, cancellationToken), TodaysSalesCount = await todaysSalesQuery.CountAsync(cancellationToken), TodaysSalesValue = await todaysSalesQuery.SumAsync(t => t.TransactionAmount, cancellationToken) }; + response.ComparisonAverageSalesValue = this.SafeDivide(response.ComparisonSalesValue, response.ComparisonSalesCount); + response.TodaysAverageSalesValue = this.SafeDivide(response.TodaysSalesValue, response.TodaysSalesCount); + + return response; + } + + public async Task GetProductPerformance(Guid estateId, + DateTime comparisonDate, + List productReportingIds, + CancellationToken cancellationToken) + { + using ResolvedDbContext? resolvedContext = this.Resolver.Resolve(EstateManagementDatabaseName, estateId.ToString()); + await using EstateManagementContext context = resolvedContext.Context; + + // First we need to get a value of todays sales + IQueryable todaysSalesQuery = this.BuildTodaySalesQuery(context); + IQueryable comparisonSalesQuery = this.BuildComparisonSalesQuery(context, comparisonDate); + + todaysSalesQuery = todaysSalesQuery.ApplyProductFilter(productReportingIds); + comparisonSalesQuery = comparisonSalesQuery.ApplyProductFilter(productReportingIds); + + TodaysSales response = new() { ComparisonSalesCount = await comparisonSalesQuery.CountAsync(cancellationToken), ComparisonSalesValue = await comparisonSalesQuery.SumAsync(t => t.TransactionAmount, cancellationToken), TodaysSalesCount = await todaysSalesQuery.CountAsync(cancellationToken), TodaysSalesValue = await todaysSalesQuery.SumAsync(t => t.TransactionAmount, cancellationToken) }; + response.ComparisonAverageSalesValue = this.SafeDivide(response.ComparisonSalesValue, response.ComparisonSalesCount); + response.TodaysAverageSalesValue = this.SafeDivide(response.TodaysSalesValue, response.TodaysSalesCount); + + return response; + } + + public async Task GetOperatorPerformance(Guid estateId, + DateTime comparisonDate, + List operatorReportingIds, + CancellationToken cancellationToken) + { + using ResolvedDbContext? resolvedContext = this.Resolver.Resolve(EstateManagementDatabaseName, estateId.ToString()); + await using EstateManagementContext context = resolvedContext.Context; + + // First we need to get a value of todays sales + IQueryable todaysSalesQuery = this.BuildTodaySalesQuery(context); + IQueryable comparisonSalesQuery = this.BuildComparisonSalesQuery(context, comparisonDate); + + todaysSalesQuery = todaysSalesQuery.ApplyOperatorFilter(operatorReportingIds); + comparisonSalesQuery = comparisonSalesQuery.ApplyOperatorFilter(operatorReportingIds); + + TodaysSales response = new() { ComparisonSalesCount = await comparisonSalesQuery.CountAsync(cancellationToken), ComparisonSalesValue = await comparisonSalesQuery.SumAsync(t => t.TransactionAmount, cancellationToken), TodaysSalesCount = await todaysSalesQuery.CountAsync(cancellationToken), TodaysSalesValue = await todaysSalesQuery.SumAsync(t => t.TransactionAmount, cancellationToken) }; + response.ComparisonAverageSalesValue = this.SafeDivide(response.ComparisonSalesValue, response.ComparisonSalesCount); + response.TodaysAverageSalesValue = this.SafeDivide(response.TodaysSalesValue, response.TodaysSalesCount); + + return response; + } + + public async Task> GetTopBottomData(Guid estateId, + TopBottom direction, + Int32 resultCount, + Dimension dimension, + CancellationToken cancellationToken) + { + using ResolvedDbContext? resolvedContext = this.Resolver.Resolve(EstateManagementDatabaseName, estateId.ToString()); + await using EstateManagementContext context = resolvedContext.Context; + + IQueryable mainQuery = this.BuildTodaySalesQuery(context); + + IQueryable topBottomData = dimension switch + { + Dimension.Merchant => mainQuery.ApplyMerchantGrouping(context), + Dimension.Operator => mainQuery.ApplyOperatorGrouping(context), + Dimension.Product => mainQuery.ApplyProductGrouping(context) + }; + + topBottomData = direction switch + { + TopBottom.Top => topBottomData.OrderByDescending(g => g.SalesValue), + _ => topBottomData.OrderBy(g => g.SalesValue) + }; + + List queryResult = await topBottomData.Take(resultCount).ToListAsync(cancellationToken); + + List results = new(); + queryResult.ForEach(qr => results.Add(new Models.TopBottomData { DimensionName = qr.DimensionName, SalesValue = qr.SalesValue })); + return results; + } + + public async Task GetTodaysSettlement(Guid estateId, + Int32 merchantReportingId, + Int32 operatorReportingId, + DateTime comparisonDate, + CancellationToken cancellationToken) + { + using ResolvedDbContext? resolvedContext = this.Resolver.Resolve(EstateManagementDatabaseName, estateId.ToString()); + await using EstateManagementContext context = resolvedContext.Context; + + IQueryable todaySettlementData = this.BuildTodaySettlementQuery(context, DateTime.Now); + IQueryable comparisonSettlementData = this.BuildComparisonSettlementQuery(context, comparisonDate); + + todaySettlementData = todaySettlementData.ApplyMerchantFilter(merchantReportingId).ApplyOperatorFilter(operatorReportingId); + comparisonSettlementData = comparisonSettlementData.ApplyMerchantFilter(merchantReportingId).ApplyOperatorFilter(operatorReportingId); + + DatabaseProjections.SettlementGroupProjection todaySettlement = await this.GetSettlementSummary(todaySettlementData, cancellationToken); + DatabaseProjections.SettlementGroupProjection comparisonSettlement = await this.GetSettlementSummary(comparisonSettlementData, cancellationToken); + + TodaysSettlement response = new() + { + ComparisonSettlementCount = comparisonSettlement.SettledCount, + ComparisonSettlementValue = comparisonSettlement.SettledValue, + ComparisonPendingSettlementCount = comparisonSettlement.UnSettledCount, + ComparisonPendingSettlementValue = comparisonSettlement.UnSettledValue, + TodaysSettlementCount = todaySettlement.SettledCount, + TodaysSettlementValue = todaySettlement.SettledValue, + TodaysPendingSettlementCount = todaySettlement.UnSettledCount, + TodaysPendingSettlementValue = todaySettlement.UnSettledValue + }; + + return response; + } + + + + private Int32 SafeDivide(Int32 number, + Int32 divisor) + { + if (divisor == 0) return number; + + return number / divisor; + } + + private Decimal SafeDivide(Decimal number, + Int32 divisor) + { + if (divisor == 0) return number; + + return number / divisor; + } +}*/ \ No newline at end of file diff --git a/EstateReportingAPI.BusinessLogic/Queries/Archive/ArchiveCode.cs b/EstateReportingAPI.BusinessLogic/Queries/Archive/ArchiveCode.cs new file mode 100644 index 0000000..e4d6f2e --- /dev/null +++ b/EstateReportingAPI.BusinessLogic/Queries/Archive/ArchiveCode.cs @@ -0,0 +1,66 @@ +using EstateReportingAPI.Models; +using MediatR; +using SimpleResults; +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Text; + +namespace EstateReportingAPI.BusinessLogic.Queries.Archive +{ + public record MerchantQueries + { + public record GetMerchantsQuery(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>; + } + + + [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>>; + } + + [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>>; + } + + [ExcludeFromCodeCoverage] + public record ResponseCodeQueries + { + public record GetResponseCodesQuery(Guid EstateId) : IRequest>>; + } + + [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>>; + } + + [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>>; + } +} diff --git a/EstateReportingAPI.BusinessLogic/Queries/ContractQueries.cs b/EstateReportingAPI.BusinessLogic/Queries/ContractQueries.cs new file mode 100644 index 0000000..023b552 --- /dev/null +++ b/EstateReportingAPI.BusinessLogic/Queries/ContractQueries.cs @@ -0,0 +1,13 @@ +using EstateReportingAPI.Models; +using MediatR; +using SimpleResults; +using System.Diagnostics.CodeAnalysis; + +namespace EstateReportingAPI.BusinessLogic.Queries; + +[ExcludeFromCodeCoverage] +public record ContractQueries { + public record GetRecentContractsQuery(Guid EstateId) : IRequest>>; + public record GetContractsQuery(Guid EstateId) : IRequest>>; + public record GetContractQuery(Guid EstateId, Guid ContractId) : IRequest>; +} \ No newline at end of file diff --git a/EstateReportingAPI.BusinessLogic/Queries/EstateQueries.cs b/EstateReportingAPI.BusinessLogic/Queries/EstateQueries.cs new file mode 100644 index 0000000..ccfd036 --- /dev/null +++ b/EstateReportingAPI.BusinessLogic/Queries/EstateQueries.cs @@ -0,0 +1,12 @@ +using System.Diagnostics.CodeAnalysis; +using EstateReportingAPI.Models; +using MediatR; +using SimpleResults; + +namespace EstateReportingAPI.BusinessLogic.Queries; + +[ExcludeFromCodeCoverage] +public record EstateQueries { + public record GetEstateQuery(Guid EstateId) : IRequest>; + public record GetEstateOperatorsQuery(Guid EstateId) : IRequest>>; +} diff --git a/EstateReportingAPI.BusinessLogic/Queries/MerchantQueries.cs b/EstateReportingAPI.BusinessLogic/Queries/MerchantQueries.cs index a7bbe3d..4f42e68 100644 --- a/EstateReportingAPI.BusinessLogic/Queries/MerchantQueries.cs +++ b/EstateReportingAPI.BusinessLogic/Queries/MerchantQueries.cs @@ -7,10 +7,13 @@ namespace EstateReportingAPI.BusinessLogic.Queries; [ExcludeFromCodeCoverage] public record MerchantQueries { - public record GetMerchantsQuery(Guid EstateId) : IRequest>>; + public record GetRecentMerchantsQuery(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 GetMerchantsQuery(Guid EstateId, MerchantQueryOptions QueryOptions) : IRequest>>; + public record GetMerchantQuery(Guid EstateId, Guid MerchantId) : IRequest>; + public record GetMerchantOperatorsQuery(Guid EstateId, Guid MerchantId) : IRequest>>; + public record GetMerchantContractsQuery(Guid EstateId, Guid MerchantId) : IRequest>>; + public record GetMerchantDevicesQuery(Guid EstateId, Guid MerchantId) : IRequest>>; + + public record MerchantQueryOptions(String Name,String Reference,Int32? SettlementSchedule,String Region, String PostCode); } \ No newline at end of file diff --git a/EstateReportingAPI.BusinessLogic/Queries/OperatorQueries.cs b/EstateReportingAPI.BusinessLogic/Queries/OperatorQueries.cs index f95f401..9f3fded 100644 --- a/EstateReportingAPI.BusinessLogic/Queries/OperatorQueries.cs +++ b/EstateReportingAPI.BusinessLogic/Queries/OperatorQueries.cs @@ -8,7 +8,5 @@ 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>>; + public record GetOperatorQuery(Guid EstateId, Guid OperatorId) : IRequest>; } \ No newline at end of file diff --git a/EstateReportingAPI.BusinessLogic/Queries/ProductQueries.cs b/EstateReportingAPI.BusinessLogic/Queries/ProductQueries.cs deleted file mode 100644 index 8e44136..0000000 --- a/EstateReportingAPI.BusinessLogic/Queries/ProductQueries.cs +++ /dev/null @@ -1,14 +0,0 @@ -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/ResponseCodeQueries.cs b/EstateReportingAPI.BusinessLogic/Queries/ResponseCodeQueries.cs deleted file mode 100644 index a059a57..0000000 --- a/EstateReportingAPI.BusinessLogic/Queries/ResponseCodeQueries.cs +++ /dev/null @@ -1,11 +0,0 @@ -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 deleted file mode 100644 index 5fa5ade..0000000 --- a/EstateReportingAPI.BusinessLogic/Queries/SettlementQueries.cs +++ /dev/null @@ -1,17 +0,0 @@ -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 index 99cf91d..260dad4 100644 --- a/EstateReportingAPI.BusinessLogic/Queries/TransactionQueries.cs +++ b/EstateReportingAPI.BusinessLogic/Queries/TransactionQueries.cs @@ -7,11 +7,6 @@ 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>>; + public record TodaysSalesQuery(Guid EstateId, Int32 MerchantReportingId, Int32 OperatorReportingId, DateTime ComparisonDate) : IRequest>; + public record TodaysFailedSales(Guid EstateId, DateTime ComparisonDate, String ResponseCode) : IRequest>; } \ No newline at end of file diff --git a/EstateReportingAPI.BusinessLogic/ReportingManager.cs b/EstateReportingAPI.BusinessLogic/ReportingManager.cs index 820891b..8714352 100644 --- a/EstateReportingAPI.BusinessLogic/ReportingManager.cs +++ b/EstateReportingAPI.BusinessLogic/ReportingManager.cs @@ -1,4 +1,7 @@ -using TransactionProcessor.Database.Contexts; +using System.Linq.Expressions; +using EstateReportingAPI.BusinessLogic.Queries; +using SimpleResults; +using TransactionProcessor.Database.Contexts; using TransactionProcessor.Database.Entities; using TransactionProcessor.Database.Entities.Summary; @@ -10,15 +13,40 @@ namespace EstateReportingAPI.BusinessLogic; using System; using System.Linq; using System.Threading; -using static EstateReportingAPI.BusinessLogic.DatabaseProjections; using Calendar = Models.Calendar; using Merchant = Models.Merchant; -using Operator = Models.Operator; -public partial class ReportingManager : IReportingManager { - private readonly IDbContextResolver Resolver; +public interface IReportingManager +{ + #region Methods + Task> GetCalendarComparisonDates(CalendarQueries.GetComparisonDatesQuery request, CancellationToken cancellationToken); + Task> GetCalendarDates(CalendarQueries.GetAllDatesQuery request, CancellationToken cancellationToken); + Task> GetCalendarYears(CalendarQueries.GetYearsQuery request, CancellationToken cancellationToken); + Task> GetRecentMerchants(MerchantQueries.GetRecentMerchantsQuery request, CancellationToken cancellationToken); + Task> GetRecentContracts(ContractQueries.GetRecentContractsQuery request, CancellationToken cancellationToken); + Task> GetContracts(ContractQueries.GetContractsQuery request, CancellationToken cancellationToken); + Task GetContract(ContractQueries.GetContractQuery request, CancellationToken cancellationToken); + Task GetTodaysFailedSales(TransactionQueries.TodaysFailedSales request, CancellationToken cancellationToken); + Task GetTodaysSales(TransactionQueries.TodaysSalesQuery request, CancellationToken cancellationToken); + + Task GetEstate(EstateQueries.GetEstateQuery request, CancellationToken cancellationToken); + Task> GetEstateOperators(EstateQueries.GetEstateOperatorsQuery request, CancellationToken cancellationToken); + Task GetMerchantsTransactionKpis(MerchantQueries.GetTransactionKpisQuery request, CancellationToken cancellationToken); + Task> GetOperators(OperatorQueries.GetOperatorsQuery request, CancellationToken cancellationToken); + Task> GetMerchants(MerchantQueries.GetMerchantsQuery request, CancellationToken cancellationToken); + Task GetMerchant(MerchantQueries.GetMerchantQuery request, CancellationToken cancellationToken); + Task> GetMerchantOperators(MerchantQueries.GetMerchantOperatorsQuery request, CancellationToken cancellationToken); + Task> GetMerchantContracts(MerchantQueries.GetMerchantContractsQuery request, CancellationToken cancellationToken); + Task> GetMerchantDevices(MerchantQueries.GetMerchantDevicesQuery request, CancellationToken cancellationToken); + + Task GetOperator(OperatorQueries.GetOperatorQuery request, CancellationToken cancellationToken); + #endregion +} +public class ReportingManager : IReportingManager { + private readonly IDbContextResolver Resolver; + private Guid Id; private static readonly String EstateManagementDatabaseName = "TransactionProcessorReadModel"; @@ -32,9 +60,9 @@ public ReportingManager(IDbContextResolver resolver) { #region Methods - public async Task> GetCalendarComparisonDates(Guid estateId, + public async Task> GetCalendarComparisonDates(CalendarQueries.GetComparisonDatesQuery request, CancellationToken cancellationToken) { - using ResolvedDbContext? resolvedContext = this.Resolver.Resolve(EstateManagementDatabaseName, estateId.ToString()); + using ResolvedDbContext? resolvedContext = this.Resolver.Resolve(EstateManagementDatabaseName, request.EstateId.ToString()); await using EstateManagementContext context = resolvedContext.Context; //DateTime startOfYear = new(DateTime.Now.Year, 1, 1); @@ -71,9 +99,9 @@ public async Task> GetCalendarComparisonDates(Guid estateId, return result; } - public async Task> GetCalendarDates(Guid estateId, + public async Task> GetCalendarDates(CalendarQueries.GetAllDatesQuery request, CancellationToken cancellationToken) { - using ResolvedDbContext? resolvedContext = this.Resolver.Resolve(EstateManagementDatabaseName, estateId.ToString()); + using ResolvedDbContext? resolvedContext = this.Resolver.Resolve(EstateManagementDatabaseName, request.EstateId.ToString()); await using EstateManagementContext context = resolvedContext.Context; List entities = context.Calendar.Where(c => c.Date <= DateTime.Now.Date).ToList(); @@ -97,9 +125,9 @@ public async Task> GetCalendarDates(Guid estateId, return result; } - public async Task> GetCalendarYears(Guid estateId, + public async Task> GetCalendarYears(CalendarQueries.GetYearsQuery request, CancellationToken cancellationToken) { - using ResolvedDbContext? resolvedContext = this.Resolver.Resolve(EstateManagementDatabaseName, estateId.ToString()); + using ResolvedDbContext? resolvedContext = this.Resolver.Resolve(EstateManagementDatabaseName, request.EstateId.ToString()); await using EstateManagementContext context = resolvedContext.Context; List years = context.Calendar.Where(c => c.Date <= DateTime.Now.Date).GroupBy(c => c.Year).Select(y => y.Key).ToList(); @@ -107,100 +135,206 @@ public async Task> GetCalendarYears(Guid estateId, return years; } - public async Task GetLastSettlement(Guid estateId, - CancellationToken cancellationToken) { - using ResolvedDbContext? resolvedContext = this.Resolver.Resolve(EstateManagementDatabaseName, estateId.ToString()); + public async Task> GetContracts(ContractQueries.GetContractsQuery request, + CancellationToken cancellationToken) { + using ResolvedDbContext? resolvedContext = this.Resolver.Resolve(EstateManagementDatabaseName, request.EstateId.ToString()); await using EstateManagementContext context = resolvedContext.Context; + + // Step 1: load contracts with operator name via a left-join (translatable) + var baseContracts = await (from c in context.Contracts + join o in context.Operators on c.OperatorId equals o.OperatorId into ops + from o in ops.DefaultIfEmpty() + select new + { + c.ContractId, + c.ContractReportingId, + c.Description, + c.EstateId, + c.OperatorId, + OperatorName = o != null ? o.Name : null + }) + .OrderByDescending(x => x.Description) + .ToListAsync(cancellationToken); + + if (!baseContracts.Any()) + return new List(); + + var contractIds = baseContracts.Select(b => b.ContractId).ToList(); + + // Step 2: load related products for all contracts in one query + var products = await context.ContractProducts + .Where(cp => contractIds.Contains(cp.ContractId)) + .Select(cp => new + { + cp.ContractProductId, + cp.ContractId, + cp.DisplayText, + cp.ProductName, + cp.ProductType, + cp.Value + }) + .ToListAsync(cancellationToken); + + var productIds = products.Select(p => p.ContractProductId).ToList(); + + // Step 3: load fees for those products in one query + var fees = await context.ContractProductTransactionFees + .Where(tf => productIds.Contains(tf.ContractProductId)) + .Select(tf => new + { + tf.CalculationType, + tf.ContractProductTransactionFeeId, + tf.FeeType, + tf.Value, + tf.ContractProductId, + tf.Description, + tf.IsEnabled + }) + .ToListAsync(cancellationToken); + + // Assemble the model in memory + List result = baseContracts.Select(b => new Contract + { + ContractId = b.ContractId, + ContractReportingId = b.ContractReportingId, + Description = b.Description, + EstateId = b.EstateId, + OperatorName = b.OperatorName, + OperatorId = b.OperatorId, + Products = products + .Where(p => p.ContractId == b.ContractId) + .Select(p => new Models.ContractProduct + { + ContractId = p.ContractId, + ProductId = p.ContractProductId, + DisplayText = p.DisplayText, + ProductName = p.ProductName, + ProductType = p.ProductType, + Value = p.Value, + TransactionFees = fees + .Where(f => f.ContractProductId == p.ContractProductId && f.IsEnabled) + .Select(f => new ContractProductTransactionFee { Description = f.Description,Value = f.Value, CalculationType = f.CalculationType, FeeType = f.FeeType, TransactionFeeId = f.ContractProductTransactionFeeId}) + .ToList() + }) + .ToList() + }).ToList(); - DateTime settlementDate = await context.SettlementSummary.Where(s => s.IsCompleted).OrderByDescending(s => s.SettlementDate).Select(s => s.SettlementDate).FirstOrDefaultAsync(cancellationToken); - - IQueryable settlements = from settlement in context.SettlementSummary where settlement.SettlementDate == settlementDate group new { settlement.SettlementDate, FeeValue = settlement.FeeValue.GetValueOrDefault(), SalesValue = settlement.SalesValue.GetValueOrDefault(), SalesCount = settlement.SalesCount.GetValueOrDefault() } by settlement.SettlementDate into grouped select new LastSettlement { FeesValue = grouped.Sum(g => g.FeeValue), SalesCount = grouped.Sum(g => g.SalesCount), SalesValue = grouped.Sum(g => g.SalesValue), SettlementDate = grouped.Key }; - - return await settlements.SingleOrDefaultAsync(cancellationToken); + return result; } - public async Task GetMerchantsTransactionKpis(Guid estateId, - CancellationToken cancellationToken) { - using ResolvedDbContext? resolvedContext = this.Resolver.Resolve(EstateManagementDatabaseName, estateId.ToString()); + public async Task GetContract(ContractQueries.GetContractQuery request, + CancellationToken cancellationToken) + { + using ResolvedDbContext? resolvedContext = this.Resolver.Resolve(EstateManagementDatabaseName, request.EstateId.ToString()); await using EstateManagementContext context = resolvedContext.Context; - Int32 merchantsWithSaleInLastHour = (from m in context.Merchants where m.LastSaleDate == DateTime.Now.Date && m.LastSaleDateTime >= DateTime.Now.AddHours(-1) select m.MerchantReportingId).Count(); - - Int32 merchantsWithNoSaleToday = (from m in context.Merchants where m.LastSaleDate == DateTime.Now.Date.AddDays(-1) select m.MerchantReportingId).Count(); - - Int32 merchantsWithNoSaleInLast7Days = (from m in context.Merchants where m.LastSaleDate <= DateTime.Now.Date.AddDays(-7) select m.MerchantReportingId).Count(); - - MerchantKpi response = new() { MerchantsWithSaleInLastHour = merchantsWithSaleInLastHour, MerchantsWithNoSaleToday = merchantsWithNoSaleToday, MerchantsWithNoSaleInLast7Days = merchantsWithNoSaleInLast7Days }; + // Step 1: load contracts with operator name via a left-join (translatable) + var baseContract = await (from c in context.Contracts + join o in context.Operators on c.OperatorId equals o.OperatorId into ops + from o in ops.DefaultIfEmpty() + where c.ContractId == request.ContractId + select new + { + c.ContractId, + c.ContractReportingId, + c.Description, + c.EstateId, + c.OperatorId, + OperatorName = o != null ? o.Name : null + }) + .OrderByDescending(x => x.Description) + .SingleOrDefaultAsync(cancellationToken); + + if (baseContract == null) + return null; + + // Step 2: load related products for all contracts in one query + var products = await context.ContractProducts + .Where(cp => cp.ContractId == baseContract.ContractId) + .Select(cp => new + { + cp.ContractProductId, + cp.ContractId, + cp.DisplayText, + cp.ProductName, + cp.ProductType, + cp.Value + }) + .ToListAsync(cancellationToken); + + var productIds = products.Select(p => p.ContractProductId).ToList(); + + // Step 3: load fees for those products in one query + var fees = await context.ContractProductTransactionFees + .Where(tf => productIds.Contains(tf.ContractProductId)) + .Select(tf => new + { + tf.CalculationType, + tf.ContractProductTransactionFeeId, + tf.FeeType, + tf.Value, + tf.ContractProductId, + tf.Description + }) + .ToListAsync(cancellationToken); + + // Assemble the model in memory + Contract result = new Contract + { + ContractId = baseContract.ContractId, + ContractReportingId = baseContract.ContractReportingId, + Description = baseContract.Description, + EstateId = baseContract.EstateId, + OperatorName = baseContract.OperatorName, + OperatorId = baseContract.OperatorId, + Products = products + .Where(p => p.ContractId == baseContract.ContractId) + .Select(p => new Models.ContractProduct + { + ContractId = p.ContractId, + ProductId = p.ContractProductId, + DisplayText = p.DisplayText, + ProductName = p.ProductName, + ProductType = p.ProductType, + Value = p.Value, + TransactionFees = fees + .Where(f => f.ContractProductId == p.ContractProductId) + .Select(f => new ContractProductTransactionFee { Description = f.Description, Value = f.Value, CalculationType = f.CalculationType, FeeType = f.FeeType, TransactionFeeId = f.ContractProductTransactionFeeId }) + .ToList() + }) + .ToList() + }; - return response; + return result; } - public async Task> GetMerchantsByLastSale(Guid estateId, - DateTime startDateTime, - DateTime endDateTime, - CancellationToken cancellationToken) { - using ResolvedDbContext? resolvedContext = this.Resolver.Resolve(EstateManagementDatabaseName, estateId.ToString()); + public async Task> GetRecentContracts(ContractQueries.GetRecentContractsQuery request, + CancellationToken cancellationToken) { + using ResolvedDbContext? resolvedContext = this.Resolver.Resolve(EstateManagementDatabaseName, request.EstateId.ToString()); await using EstateManagementContext context = resolvedContext.Context; - List response = new(); - - var merchants = await context.Merchants.Where(m => m.LastSaleDateTime >= startDateTime && m.LastSaleDateTime <= endDateTime).Select(m => new { - MerchantReportingId = m.MerchantReportingId, - EstateReportingId = context.Estates.Single(e => e.EstateId == m.EstateId).EstateReportingId, - Name = m.Name, - LastSaleDateTime = m.LastSaleDateTime, - LastSale = m.LastSaleDate, - CreatedDateTime = m.CreatedDateTime, - LastStatement = m.LastStatementGenerated, - MerchantId = m.MerchantId, - Reference = m.Reference, - AddressInfo = context.MerchantAddresses.Where(ma => ma.MerchantId == m.MerchantId).OrderByDescending(ma => ma.CreatedDateTime).Select(ma => new { - PostCode = ma.PostalCode, Region = ma.Region, Town = ma.Town - // Add more properties as needed - }).FirstOrDefault() // Get the first matching MerchantAddress or null - }).ToListAsync(cancellationToken); - - merchants.ForEach(m => response.Add(new Merchant { - LastSaleDateTime = m.LastSaleDateTime, - CreatedDateTime = m.CreatedDateTime, - EstateReportingId = m.EstateReportingId, - LastSale = m.LastSale, - LastStatement = m.LastStatement, - MerchantId = m.MerchantId, - MerchantReportingId = m.MerchantReportingId, - Name = m.Name, - Reference = m.Reference, - PostCode = m.AddressInfo?.PostCode, - Region = m.AddressInfo?.Region, - Town = m.AddressInfo?.Town - })); + var contracts = context.Contracts.Select(c => new Contract(){ + ContractId = c.ContractId, + ContractReportingId = c.ContractReportingId, + Description = c.Description, + EstateId = c.EstateId, + OperatorName = context.Operators.Where(o => o.OperatorId == c.OperatorId).Select(o => o.Name).FirstOrDefault(), + OperatorId = c.OperatorId, + }).OrderByDescending(c => c.Description).Take(3); - return response; + return await contracts.ToListAsync(cancellationToken); } - public async Task> GetResponseCodes(Guid estateId, - CancellationToken cancellationToken) { - using ResolvedDbContext? resolvedContext = this.Resolver.Resolve(EstateManagementDatabaseName, estateId.ToString()); - await using EstateManagementContext context = resolvedContext.Context; - List response = new(); - - List responseCodes = await context.ResponseCodes.ToListAsync(cancellationToken); + - responseCodes.ForEach(r => response.Add(new ResponseCode { Code = r.ResponseCode, Description = r.Description })); - - return response; - } - - public async Task GetTodaysFailedSales(Guid estateId, - DateTime comparisonDate, - String responseCode, + public async Task GetTodaysFailedSales(TransactionQueries.TodaysFailedSales request, CancellationToken cancellationToken) { - using ResolvedDbContext? resolvedContext = this.Resolver.Resolve(EstateManagementDatabaseName, estateId.ToString()); + using ResolvedDbContext? resolvedContext = this.Resolver.Resolve(EstateManagementDatabaseName, request.EstateId.ToString()); await using EstateManagementContext context = resolvedContext.Context; - List todaysSales = await (from t in context.TodayTransactions where t.IsAuthorised == false && t.TransactionType == "Sale" && t.ResponseCode == responseCode select t.TransactionAmount).ToListAsync(cancellationToken); + List todaysSales = await (from t in context.TodayTransactions where t.IsAuthorised == false && t.TransactionType == "Sale" && t.ResponseCode == request.ResponseCode select t.TransactionAmount).ToListAsync(cancellationToken); - List comparisonSales = await (from t in context.TransactionHistory where t.IsAuthorised == false && t.TransactionType == "Sale" && t.TransactionDate == comparisonDate && t.TransactionTime <= DateTime.Now.TimeOfDay && t.ResponseCode == responseCode select t.TransactionAmount).ToListAsync(cancellationToken); + List comparisonSales = await (from t in context.TransactionHistory where t.IsAuthorised == false && t.TransactionType == "Sale" && t.TransactionDate == request.ComparisonDate && t.TransactionTime <= DateTime.Now.TimeOfDay && t.ResponseCode == request.ResponseCode select t.TransactionAmount).ToListAsync(cancellationToken); TodaysSales response = new() { ComparisonSalesCount = comparisonSales.Count, @@ -213,19 +347,16 @@ public async Task GetTodaysFailedSales(Guid estateId, return response; } - public async Task GetTodaysSales(Guid estateId, - Int32 merchantReportingId, - Int32 operatorReportingId, - DateTime comparisonDate, + public async Task GetTodaysSales(TransactionQueries.TodaysSalesQuery request, CancellationToken cancellationToken) { - using ResolvedDbContext? resolvedContext = this.Resolver.Resolve(EstateManagementDatabaseName, estateId.ToString()); + using ResolvedDbContext? resolvedContext = this.Resolver.Resolve(EstateManagementDatabaseName, request.EstateId.ToString()); await using EstateManagementContext context = resolvedContext.Context; IQueryable todaysSales = this.BuildTodaySalesQuery(context); - IQueryable comparisonSales = this.BuildComparisonSalesQuery(context, comparisonDate); + IQueryable comparisonSales = this.BuildComparisonSalesQuery(context, request.ComparisonDate); - todaysSales = todaysSales.ApplyMerchantFilter(merchantReportingId).ApplyOperatorFilter(operatorReportingId); - comparisonSales = comparisonSales.ApplyMerchantFilter(merchantReportingId).ApplyOperatorFilter(operatorReportingId); + todaysSales = todaysSales.ApplyMerchantFilter(request.MerchantReportingId).ApplyOperatorFilter(request.OperatorReportingId); + comparisonSales = comparisonSales.ApplyMerchantFilter(request.MerchantReportingId).ApplyOperatorFilter(request.OperatorReportingId); Decimal todaysSalesValue = await todaysSales.SumAsync(t => t.TransactionAmount, cancellationToken); Int32 todaysSalesCount = await todaysSales.CountAsync(cancellationToken); @@ -237,60 +368,93 @@ public async Task GetTodaysSales(Guid estateId, ComparisonSalesValue = comparisonSalesValue, TodaysSalesCount = todaysSalesCount, TodaysSalesValue = todaysSalesValue, - TodaysAverageSalesValue = todaysSalesValue / todaysSalesCount, - ComparisonAverageSalesValue = comparisonSalesValue / comparisonSalesCount + TodaysAverageSalesValue = SafeDivide(todaysSalesValue, todaysSalesCount), + ComparisonAverageSalesValue = SafeDivide(comparisonSalesValue,comparisonSalesCount) }; return response; } - public async Task> GetTodaysSalesCountByHour(Guid estateId, - Int32 merchantReportingId, - Int32 operatorReportingId, - DateTime comparisonDate, - CancellationToken cancellationToken) { - using ResolvedDbContext? resolvedContext = this.Resolver.Resolve(EstateManagementDatabaseName, estateId.ToString()); + public async Task> GetEstateOperators(EstateQueries.GetEstateOperatorsQuery request, + CancellationToken cancellationToken) { + using ResolvedDbContext? resolvedContext = this.Resolver.Resolve(EstateManagementDatabaseName, request.EstateId.ToString()); await using EstateManagementContext context = resolvedContext.Context; - IQueryable todaysSales = this.BuildTodaySalesQuery(context); - IQueryable comparisonSales = this.BuildComparisonSalesQuery(context, comparisonDate); - - todaysSales = todaysSales.ApplyMerchantFilter(merchantReportingId).ApplyOperatorFilter(operatorReportingId); - comparisonSales = comparisonSales.ApplyMerchantFilter(merchantReportingId).ApplyOperatorFilter(operatorReportingId); - - // First we need to get a value of todays sales - var todaysSalesByHour = await (from t in todaysSales group t.TransactionAmount by t.Hour into g select new { Hour = g.Key, TotalSalesCount = g.Count() }).ToListAsync(cancellationToken); - - var comparisonSalesByHour = await (from t in comparisonSales group t.TransactionAmount by t.Hour into g select new { Hour = g.Key, TotalSalesCount = g.Count() }).ToListAsync(cancellationToken); - - List response = (from today in todaysSalesByHour join comparison in comparisonSalesByHour on today.Hour equals comparison.Hour select new TodaysSalesCountByHour { Hour = today.Hour.Value, TodaysSalesCount = today.TotalSalesCount, ComparisonSalesCount = comparison.TotalSalesCount }).ToList(); + //var operatorEntities = context.EstateOperators.Where(e => e.EstateId == estateId).AsQueryable(); + //var x = operatorEntities.Join() + var operatorEntities = await context.EstateOperators + .Join( + context.Operators, + eo => new { eo.OperatorId, eo.EstateId }, + op => new { op.OperatorId, op.EstateId }, + (eo, op) => new + { + EstateOperator = eo, + Operator = op + }) + .Where(e => e.EstateOperator.EstateId == request.EstateId && (e.EstateOperator.IsDeleted ?? false) == false) + .ToListAsync(cancellationToken); + + List operators = new(); + + foreach (var operatorEntity in operatorEntities) { + operators.Add(new EstateOperator() { + Name = operatorEntity.Operator.Name, + OperatorId = operatorEntity.EstateOperator.OperatorId + }); + } - return response; + return operators; } - public async Task> GetTodaysSalesValueByHour(Guid estateId, - Int32 merchantReportingId, - Int32 operatorReportingId, - DateTime comparisonDate, - CancellationToken cancellationToken) { - using ResolvedDbContext? resolvedContext = this.Resolver.Resolve(EstateManagementDatabaseName, estateId.ToString()); + public async Task GetEstate(EstateQueries.GetEstateQuery request, + CancellationToken cancellationToken) { + using ResolvedDbContext? resolvedContext = this.Resolver.Resolve(EstateManagementDatabaseName, request.EstateId.ToString()); await using EstateManagementContext context = resolvedContext.Context; - IQueryable todaysSales = this.BuildTodaySalesQuery(context); - IQueryable comparisonSales = this.BuildComparisonSalesQuery(context, comparisonDate); - - todaysSales = todaysSales.ApplyMerchantFilter(merchantReportingId).ApplyOperatorFilter(operatorReportingId); - comparisonSales = comparisonSales.ApplyMerchantFilter(merchantReportingId).ApplyOperatorFilter(operatorReportingId); - - // First we need to get a value of todays sales - var todaysSalesByHour = await (from t in todaysSales group t.TransactionAmount by t.Hour into g select new { Hour = g.Key, TotalSalesValue = g.Sum() }).ToListAsync(cancellationToken); - - var comparisonSalesByHour = await (from t in comparisonSales group t.TransactionAmount by t.Hour into g select new { Hour = g.Key, TotalSalesValue = g.Sum() }).ToListAsync(cancellationToken); - - List response = (from today in todaysSalesByHour join comparison in comparisonSalesByHour on today.Hour equals comparison.Hour select new TodaysSalesValueByHour { Hour = today.Hour.Value, TodaysSalesValue = today.TotalSalesValue, ComparisonSalesValue = comparison.TotalSalesValue }).ToList(); + var estate = await context.Estates.Where(e => e.EstateId == request.EstateId).SingleOrDefaultAsync(cancellationToken); + + // Operators + var operators = await context.Operators.Where(e => e.EstateId == request.EstateId).ToListAsync(cancellationToken); + // Users + var users = await context.EstateSecurityUsers.Where(e => e.EstateId == request.EstateId).ToListAsync(cancellationToken); + // Merchants + var merchants = await context.Merchants.Where(e => e.EstateId == request.EstateId).ToListAsync(cancellationToken); + // Contracts + var contracts = await context.Contracts.Where(e => e.EstateId == request.EstateId).ToListAsync(cancellationToken); + + Estate result = new() + { + EstateId = estate.EstateId, + EstateName = estate.Name, + Reference = estate.Reference, + Operators = operators.Select(o => new Models.EstateOperator + { + OperatorId = o.OperatorId, + Name = o.Name, + }).ToList(), + Users = users.Select(u => new Models.EstateUser + { + UserId = u.SecurityUserId, + EmailAddress= u.EmailAddress, + CreatedDateTime = u.CreatedDateTime + }).ToList(), + Merchants = merchants.Select(m => new Models.EstateMerchant + { + MerchantId = m.MerchantId, + Name = m.Name, + Reference = m.Reference + }).ToList(), + Contracts = contracts.Select(c => new Models.EstateContract + { + ContractId = c.ContractId, + Name = c.Description, + }).ToList() + }; - return response; + return result; } + private Int32 SafeDivide(Int32 number, Int32 divisor) { if (divisor == 0) return number; @@ -305,9 +469,12 @@ private Decimal SafeDivide(Decimal number, return number / divisor; } - public async Task> GetMerchants(Guid estateId, - CancellationToken cancellationToken) { - using ResolvedDbContext? resolvedContext = this.Resolver.Resolve(EstateManagementDatabaseName, estateId.ToString()); + + + public async Task> GetRecentMerchants(MerchantQueries.GetRecentMerchantsQuery request, + CancellationToken cancellationToken) + { + using ResolvedDbContext? resolvedContext = this.Resolver.Resolve(EstateManagementDatabaseName, request.EstateId.ToString()); await using EstateManagementContext context = resolvedContext.Context; var merchants = context.Merchants.Select(m => new { @@ -320,327 +487,360 @@ public async Task> GetMerchants(Guid estateId, MerchantId = m.MerchantId, Reference = m.Reference, AddressInfo = context.MerchantAddresses.Where(ma => ma.MerchantId == m.MerchantId).OrderByDescending(ma => ma.CreatedDateTime).Select(ma => new { - PostCode = ma.PostalCode, Region = ma.Region, Town = ma.Town + ma.AddressLine1, + ma.AddressLine2, + ma.Country, + ma.PostalCode, + ma.Region, + ma.Town // Add more properties as needed }).FirstOrDefault(), // Get the first matching MerchantAddress or null + ContactInfo = context.MerchantContacts.Where(mc => mc.MerchantId == m.MerchantId).OrderByDescending(mc => mc.CreatedDateTime).Select(mc => new { + mc.Name, + mc.EmailAddress, + mc.PhoneNumber + }).FirstOrDefault(), // Get the first matching MerchantContact or null EstateReportingId = context.Estates.Single(e => e.EstateId == m.EstateId).EstateReportingId - }); + }).OrderByDescending(m => m.CreatedDateTime).Take(3); List merchantList = new(); - foreach (var result in merchants) { - Merchant model = new() { + foreach (var result in merchants) + { + Merchant model = new() + { MerchantId = result.MerchantId, Name = result.Name, Reference = result.Reference, MerchantReportingId = result.MerchantReportingId, CreatedDateTime = result.CreatedDateTime, - EstateReportingId = result.EstateReportingId, - LastSale = result.LastSale, - LastSaleDateTime = result.LastSaleDateTime, - LastStatement = result.LastStatement }; if (result.AddressInfo != null) { - model.PostCode = result.AddressInfo.PostCode; + model.AddressLine1 = result.AddressInfo.AddressLine1; + model.AddressLine2 = result.AddressInfo.AddressLine2; + model.Country = result.AddressInfo.Country; + model.PostCode = result.AddressInfo.PostalCode; model.Town = result.AddressInfo.Town; model.Region = result.AddressInfo.Region; } + if (result.ContactInfo != null) { + model.ContactName = result.ContactInfo.Name; + model.ContactEmail = result.ContactInfo.EmailAddress; + model.ContactPhone = result.ContactInfo.PhoneNumber; + } + merchantList.Add(model); } return merchantList; } - private IQueryable BuildUnsettledFeesQuery(EstateManagementContext context, - DateTime startDate, - DateTime endDate) { - return 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 DatabaseProjections.FeeTransactionProjection { Fee = merchantSettlementFee, Txn = transaction }; - } - - private IQueryable BuildTransactionSearchQuery(EstateManagementContext context, - DateTime queryDate) { - return from txn in context.Transactions join merchant in context.Merchants on txn.MerchantId equals merchant.MerchantId join @operator in context.Operators on txn.OperatorId equals @operator.OperatorId join product in context.ContractProducts on txn.ContractProductId equals product.ContractProductId where txn.TransactionDate == queryDate select new DatabaseProjections.TransactionSearchProjection { Transaction = txn, Merchant = merchant, Operator = @operator, Product = product }; - } + public async Task GetMerchantsTransactionKpis(MerchantQueries.GetTransactionKpisQuery request, + CancellationToken cancellationToken) + { + using ResolvedDbContext? resolvedContext = this.Resolver.Resolve(EstateManagementDatabaseName, request.EstateId.ToString()); + await using EstateManagementContext context = resolvedContext.Context; + var merchants = await context.Merchants.Select(m => new { m.Name, m.LastSaleDate, m.LastSaleDateTime }).ToListAsync(); + Int32 merchantsWithSaleInLastHour = (from m in context.Merchants where m.LastSaleDate == DateTime.Now.Date && m.LastSaleDateTime >= DateTime.Now.AddHours(-1) select m.MerchantReportingId).Count(); - private IQueryable BuildTodaySalesQuery(EstateManagementContext context) { - return from t in context.TodayTransactions where t.IsAuthorised && t.TransactionType == "Sale" && t.TransactionDate == DateTime.Now.Date && t.TransactionTime <= DateTime.Now.TimeOfDay select t; - } + Int32 merchantsWithNoSaleToday = (from m in context.Merchants where m.LastSaleDate >= DateTime.Now.Date.AddDays(-7) && m.LastSaleDate <= DateTime.Now.Date.AddDays(-1) select m.MerchantReportingId).Count(); - private IQueryable BuildComparisonSalesQuery(EstateManagementContext context, - DateTime comparisonDate) { - return from t in context.TransactionHistory where t.IsAuthorised && t.TransactionType == "Sale" && t.TransactionDate == comparisonDate && t.TransactionTime <= DateTime.Now.TimeOfDay select t; - } + Int32 merchantsWithNoSaleInLast7Days = (from m in context.Merchants where m.LastSaleDate <= DateTime.Now.Date.AddDays(-7) select m.MerchantReportingId).Count(); - private IQueryable BuildTodaySettlementQuery(EstateManagementContext context, - DateTime settlementDate) { - IQueryable settlementData = from s in context.Settlements join f in context.MerchantSettlementFees on s.SettlementId equals f.SettlementId join t in context.TodayTransactions on f.TransactionId equals t.TransactionId where s.SettlementDate == settlementDate select new DatabaseProjections.TodaySettlementTransactionProjection { Fee = f, Txn = t }; - return settlementData; - } + MerchantKpi response = new() { MerchantsWithSaleInLastHour = merchantsWithSaleInLastHour, MerchantsWithNoSaleToday = merchantsWithNoSaleToday, MerchantsWithNoSaleInLast7Days = merchantsWithNoSaleInLast7Days }; - private IQueryable BuildComparisonSettlementQuery(EstateManagementContext context, - DateTime settlementDate) { - IQueryable settlementData = from s in context.Settlements join f in context.MerchantSettlementFees on s.SettlementId equals f.SettlementId join t in context.TransactionHistory on f.TransactionId equals t.TransactionId where s.SettlementDate == settlementDate.Date select new DatabaseProjections.ComparisonSettlementTransactionProjection { Fee = f, Txn = t }; - return settlementData; + return response; } - - public async Task> GetUnsettledFees(Guid estateId, - DateTime startDate, - DateTime endDate, - List merchantIds, - List operatorIds, - List productIds, - GroupByOption? groupByOption, - CancellationToken cancellationToken) { - using ResolvedDbContext? resolvedContext = this.Resolver.Resolve(EstateManagementDatabaseName, estateId.ToString()); + public async Task> GetOperators(OperatorQueries.GetOperatorsQuery request, + CancellationToken cancellationToken) + { + using ResolvedDbContext? resolvedContext = this.Resolver.Resolve(EstateManagementDatabaseName, request.EstateId.ToString()); await using EstateManagementContext context = resolvedContext.Context; - IQueryable query = this.BuildUnsettledFeesQuery(context, startDate, endDate).ApplyMerchantFilter(context, merchantIds).ApplyOperatorFilter(context, operatorIds).ApplyProductFilter(context, productIds); + List operators = await (from o in context.Operators + select new Operator + { + Name = o.Name, + EstateReportingId = context.Estates.Single(e => e.EstateId == o.EstateId).EstateReportingId, + OperatorId = o.OperatorId, + OperatorReportingId = o.OperatorReportingId, + RequireCustomMerchantNumber = o.RequireCustomMerchantNumber, + RequireCustomTerminalNumber = o.RequireCustomTerminalNumber + }).ToListAsync(cancellationToken); - // Perform grouping - IQueryable groupedQuery = groupByOption switch { - GroupByOption.Merchant => query.ApplyMerchantGrouping(context), - GroupByOption.Operator => query.ApplyOperatorGrouping(context), - GroupByOption.Product => query.ApplyProductGrouping(context) - }; - return await groupedQuery.ToListAsync(cancellationToken); + return operators; } - public async Task> TransactionSearch(Guid estateId, - TransactionSearchRequest searchRequest, - PagingRequest pagingRequest, - SortingRequest sortingRequest, - CancellationToken cancellationToken) { - // Base query before any filtering is added - using ResolvedDbContext? resolvedContext = this.Resolver.Resolve(EstateManagementDatabaseName, estateId.ToString()); + public async Task GetOperator(OperatorQueries.GetOperatorQuery request, + CancellationToken cancellationToken) + { + using ResolvedDbContext? resolvedContext = this.Resolver.Resolve(EstateManagementDatabaseName, request.EstateId.ToString()); await using EstateManagementContext context = resolvedContext.Context; - IQueryable mainQuery = this.BuildTransactionSearchQuery(context, searchRequest.QueryDate); - - mainQuery = mainQuery.ApplyFilters(searchRequest); - mainQuery = mainQuery.ApplySorting(sortingRequest); - mainQuery = mainQuery.ApplyPagination(pagingRequest); - - // Now build the results - List queryResults = await mainQuery.ToListAsync(cancellationToken); - - List results = new(); - - queryResults.ForEach(qr => { - results.Add(new TransactionResult { - MerchantReportingId = qr.Merchant.MerchantReportingId, - ResponseCode = qr.Transaction.ResponseCode, - IsAuthorised = qr.Transaction.IsAuthorised, - MerchantName = qr.Merchant.Name, - OperatorName = qr.Operator.Name, - OperatorReportingId = qr.Operator.OperatorReportingId, - Product = qr.Product.ProductName, - ProductReportingId = qr.Product.ContractProductReportingId, - ResponseMessage = qr.Transaction.ResponseMessage, - TransactionDateTime = qr.Transaction.TransactionDateTime, - TransactionId = qr.Transaction.TransactionId, - TransactionReportingId = qr.Transaction.TransactionReportingId, - TransactionSource = qr.Transaction.TransactionSource.ToString(), - TransactionAmount = qr.Transaction.TransactionAmount - }); - }); + Operator @operator = await (from o in context.Operators + where o.OperatorId == request.OperatorId + select new Operator { + Name = o.Name, + EstateReportingId = context.Estates.Single(e => e.EstateId == o.EstateId).EstateReportingId, + OperatorId = o.OperatorId, + OperatorReportingId = o.OperatorReportingId, + RequireCustomMerchantNumber = o.RequireCustomMerchantNumber, + RequireCustomTerminalNumber = o.RequireCustomTerminalNumber + }).SingleOrDefaultAsync(cancellationToken); - return results; + return @operator; } - public async Task GetMerchantPerformance(Guid estateId, - DateTime comparisonDate, - List merchantReportingIds, - CancellationToken cancellationToken) { - using ResolvedDbContext? resolvedContext = this.Resolver.Resolve(EstateManagementDatabaseName, estateId.ToString()); + public async Task> GetMerchants(MerchantQueries.GetMerchantsQuery request, + CancellationToken cancellationToken) { + using ResolvedDbContext? resolvedContext = this.Resolver.Resolve(EstateManagementDatabaseName, request.EstateId.ToString()); await using EstateManagementContext context = resolvedContext.Context; + + + var merchantWithAddresses = context.Merchants + .Where(m => m.EstateId == request.EstateId) + .GroupJoin( + context.MerchantAddresses, + m => m.MerchantId, + a => a.MerchantId, + (m, addresses) => new + { + Merchant = m, + Address = addresses.First() + }) + .AsQueryable(); + + // Now apply the other filters from request.QueryOptions + if (String.IsNullOrEmpty(request.QueryOptions.Name) == false) { + merchantWithAddresses = merchantWithAddresses.Where(m => m.Merchant.Name.Contains(request.QueryOptions.Name)).AsQueryable(); + } - // First we need to get a value of todays sales - IQueryable todaysSalesQuery = this.BuildTodaySalesQuery(context); - IQueryable comparisonSalesQuery = this.BuildComparisonSalesQuery(context, comparisonDate); - - todaysSalesQuery = todaysSalesQuery.ApplyMerchantFilter(merchantReportingIds); - comparisonSalesQuery = comparisonSalesQuery.ApplyMerchantFilter(merchantReportingIds); - - // Build the response - TodaysSales response = new() { ComparisonSalesCount = await comparisonSalesQuery.CountAsync(cancellationToken), ComparisonSalesValue = await comparisonSalesQuery.SumAsync(t => t.TransactionAmount, cancellationToken), TodaysSalesCount = await todaysSalesQuery.CountAsync(cancellationToken), TodaysSalesValue = await todaysSalesQuery.SumAsync(t => t.TransactionAmount, cancellationToken) }; - response.ComparisonAverageSalesValue = this.SafeDivide(response.ComparisonSalesValue, response.ComparisonSalesCount); - response.TodaysAverageSalesValue = this.SafeDivide(response.TodaysSalesValue, response.TodaysSalesCount); - - return response; - } + if (String.IsNullOrEmpty(request.QueryOptions.Reference) == false) + { + merchantWithAddresses = merchantWithAddresses.Where(m => m.Merchant.Reference == request.QueryOptions.Reference).AsQueryable(); + } - public async Task GetProductPerformance(Guid estateId, - DateTime comparisonDate, - List productReportingIds, - CancellationToken cancellationToken) { - using ResolvedDbContext? resolvedContext = this.Resolver.Resolve(EstateManagementDatabaseName, estateId.ToString()); - await using EstateManagementContext context = resolvedContext.Context; + if (request.QueryOptions.SettlementSchedule > 0) + { + merchantWithAddresses = merchantWithAddresses.Where(m => m.Merchant.SettlementSchedule == request.QueryOptions.SettlementSchedule).AsQueryable(); + } - // First we need to get a value of todays sales - IQueryable todaysSalesQuery = this.BuildTodaySalesQuery(context); - IQueryable comparisonSalesQuery = this.BuildComparisonSalesQuery(context, comparisonDate); + if (String.IsNullOrEmpty(request.QueryOptions.Region) == false) + { + merchantWithAddresses = merchantWithAddresses.Where(m => m.Address.Region.Contains(request.QueryOptions.Region)).AsQueryable(); + } - todaysSalesQuery = todaysSalesQuery.ApplyProductFilter(productReportingIds); - comparisonSalesQuery = comparisonSalesQuery.ApplyProductFilter(productReportingIds); + if (String.IsNullOrEmpty(request.QueryOptions.PostCode) == false) + { + merchantWithAddresses = merchantWithAddresses.Where(m => m.Address.PostalCode == request.QueryOptions.PostCode).AsQueryable(); + } - TodaysSales response = new() { ComparisonSalesCount = await comparisonSalesQuery.CountAsync(cancellationToken), ComparisonSalesValue = await comparisonSalesQuery.SumAsync(t => t.TransactionAmount, cancellationToken), TodaysSalesCount = await todaysSalesQuery.CountAsync(cancellationToken), TodaysSalesValue = await todaysSalesQuery.SumAsync(t => t.TransactionAmount, cancellationToken) }; - response.ComparisonAverageSalesValue = this.SafeDivide(response.ComparisonSalesValue, response.ComparisonSalesCount); - response.TodaysAverageSalesValue = this.SafeDivide(response.TodaysSalesValue, response.TodaysSalesCount); + // Ok now enumerate the results + var queryResults = await merchantWithAddresses.ToListAsync(cancellationToken); + List merchants = new(); + foreach (var queryResult in queryResults) { + merchants.Add(new Merchant { + Balance = 0, + CreatedDateTime = queryResult.Merchant.CreatedDateTime, + Name = queryResult.Merchant.Name, + Region = queryResult.Address.Region, + Reference = queryResult.Merchant.Reference, + PostCode = queryResult.Address.PostalCode, + SettlementSchedule = queryResult.Merchant.SettlementSchedule, + MerchantId = queryResult.Merchant.MerchantId, + AddressId = queryResult.Address.AddressId, + AddressLine1 = queryResult.Address.AddressLine1, + AddressLine2 = queryResult.Address.AddressLine2, + Town = queryResult.Address.Town, + Country = queryResult.Address.Country + }); + } - return response; + return merchants; } - public async Task GetOperatorPerformance(Guid estateId, - DateTime comparisonDate, - List operatorReportingIds, - CancellationToken cancellationToken) { - using ResolvedDbContext? resolvedContext = this.Resolver.Resolve(EstateManagementDatabaseName, estateId.ToString()); + public async Task GetMerchant(MerchantQueries.GetMerchantQuery request, + CancellationToken cancellationToken) { + using ResolvedDbContext? resolvedContext = this.Resolver.Resolve(EstateManagementDatabaseName, request.EstateId.ToString()); await using EstateManagementContext context = resolvedContext.Context; - // First we need to get a value of todays sales - IQueryable todaysSalesQuery = this.BuildTodaySalesQuery(context); - IQueryable comparisonSalesQuery = this.BuildComparisonSalesQuery(context, comparisonDate); - todaysSalesQuery = todaysSalesQuery.ApplyOperatorFilter(operatorReportingIds); - comparisonSalesQuery = comparisonSalesQuery.ApplyOperatorFilter(operatorReportingIds); + var merchant = await context.Merchants.Select(m => new { + MerchantReportingId = m.MerchantReportingId, + Name = m.Name, + LastSaleDateTime = m.LastSaleDateTime, + LastSale = m.LastSaleDate, + CreatedDateTime = m.CreatedDateTime, + LastStatement = m.LastStatementGenerated, + MerchantId = m.MerchantId, + Reference = m.Reference, + m.SettlementSchedule, + AddressInfo = context.MerchantAddresses.Where(ma => ma.MerchantId == m.MerchantId).OrderByDescending(ma => ma.CreatedDateTime).Select(ma => new { + ma.AddressId, + ma.AddressLine1, + ma.AddressLine2, + ma.Country, + ma.PostalCode, + ma.Region, + ma.Town + // Add more properties as needed + }).FirstOrDefault(), // Get the first matching MerchantAddress or null + ContactInfo = context.MerchantContacts.Where(mc => mc.MerchantId == m.MerchantId).OrderByDescending(mc => mc.CreatedDateTime).Select(mc => new { + mc.ContactId, + mc.Name, + mc.EmailAddress, + mc.PhoneNumber + }).FirstOrDefault(), // Get the first matching MerchantContact or null + EstateReportingId = context.Estates.Single(e => e.EstateId == m.EstateId).EstateReportingId + }).Where(m => m.MerchantId == request.MerchantId).SingleOrDefaultAsync(cancellationToken); - TodaysSales response = new() { ComparisonSalesCount = await comparisonSalesQuery.CountAsync(cancellationToken), ComparisonSalesValue = await comparisonSalesQuery.SumAsync(t => t.TransactionAmount, cancellationToken), TodaysSalesCount = await todaysSalesQuery.CountAsync(cancellationToken), TodaysSalesValue = await todaysSalesQuery.SumAsync(t => t.TransactionAmount, cancellationToken) }; - response.ComparisonAverageSalesValue = this.SafeDivide(response.ComparisonSalesValue, response.ComparisonSalesCount); - response.TodaysAverageSalesValue = this.SafeDivide(response.TodaysSalesValue, response.TodaysSalesCount); + if (merchant == null) + return null; - return response; + // Ok now enumerate the results + Merchant result = new Merchant + { + Balance = 0, + CreatedDateTime = merchant.CreatedDateTime, + Name = merchant.Name, + Reference = merchant.Reference, + MerchantId = merchant.MerchantId, + MerchantReportingId = merchant.MerchantReportingId, + SettlementSchedule = merchant.SettlementSchedule, + AddressId = merchant.AddressInfo.AddressId, + AddressLine1 = merchant.AddressInfo.AddressLine1, + AddressLine2 = merchant.AddressInfo.AddressLine2, + Town = merchant.AddressInfo.Town, + Region = merchant.AddressInfo.Region, + PostCode = merchant.AddressInfo.PostalCode, + Country = merchant.AddressInfo.Country, + ContactId = merchant.ContactInfo.ContactId, + ContactName = merchant.ContactInfo.Name, + ContactEmail = merchant.ContactInfo.EmailAddress, + ContactPhone = merchant.ContactInfo.PhoneNumber + }; + + return result; } - public async Task> GetTopBottomData(Guid estateId, - TopBottom direction, - Int32 resultCount, - Dimension dimension, - CancellationToken cancellationToken) { - using ResolvedDbContext? resolvedContext = this.Resolver.Resolve(EstateManagementDatabaseName, estateId.ToString()); + public async Task> GetMerchantOperators(MerchantQueries.GetMerchantOperatorsQuery request, + CancellationToken cancellationToken) { + using ResolvedDbContext? resolvedContext = this.Resolver.Resolve(EstateManagementDatabaseName, request.EstateId.ToString()); await using EstateManagementContext context = resolvedContext.Context; - IQueryable mainQuery = this.BuildTodaySalesQuery(context); - - IQueryable topBottomData = dimension switch { - Dimension.Merchant => mainQuery.ApplyMerchantGrouping(context), - Dimension.Operator => mainQuery.ApplyOperatorGrouping(context), - Dimension.Product => mainQuery.ApplyProductGrouping(context) - }; - - topBottomData = direction switch { - TopBottom.Top => topBottomData.OrderByDescending(g => g.SalesValue), - _ => topBottomData.OrderBy(g => g.SalesValue) - }; - - List queryResult = await topBottomData.Take(resultCount).ToListAsync(cancellationToken); + List merchantOperators = await context.MerchantOperators.Where(mo => mo.MerchantId == request.MerchantId && mo.IsDeleted == false) + .ToListAsync(cancellationToken); + + List result = new(); + foreach (TransactionProcessor.Database.Entities.MerchantOperator merchantOperator in merchantOperators) { + result.Add(new MerchantOperator { + OperatorId = merchantOperator.OperatorId, + IsDeleted = merchantOperator.IsDeleted, + MerchantId = merchantOperator.MerchantId, + MerchantNumber = merchantOperator.MerchantNumber, + OperatorName = merchantOperator.Name, + TerminalNumber = merchantOperator.TerminalNumber + }); + } - List results = new(); - queryResult.ForEach(qr => results.Add(new Models.TopBottomData { DimensionName = qr.DimensionName, SalesValue = qr.SalesValue })); - return results; + return result; } - public async Task GetTodaysSettlement(Guid estateId, - Int32 merchantReportingId, - Int32 operatorReportingId, - DateTime comparisonDate, - CancellationToken cancellationToken) { - using ResolvedDbContext? resolvedContext = this.Resolver.Resolve(EstateManagementDatabaseName, estateId.ToString()); + public async Task> GetMerchantContracts(MerchantQueries.GetMerchantContractsQuery request, + CancellationToken cancellationToken) { + using ResolvedDbContext? resolvedContext = this.Resolver.Resolve(EstateManagementDatabaseName, request.EstateId.ToString()); await using EstateManagementContext context = resolvedContext.Context; - IQueryable todaySettlementData = this.BuildTodaySettlementQuery(context, DateTime.Now); - IQueryable comparisonSettlementData = this.BuildComparisonSettlementQuery(context, comparisonDate); - - todaySettlementData = todaySettlementData.ApplyMerchantFilter(merchantReportingId).ApplyOperatorFilter(operatorReportingId); - comparisonSettlementData = comparisonSettlementData.ApplyMerchantFilter(merchantReportingId).ApplyOperatorFilter(operatorReportingId); + var merchantContracts = await context.MerchantContracts.Where(mo => mo.MerchantId == request.MerchantId && mo.IsDeleted == false) + .Select(mc => new { + mc.ContractId, + mc.IsDeleted, + mc.MerchantId, + ContractInfo = context.Contracts.Where(c => c.ContractId == mc.ContractId).Select(ma => new { + ma.Description, + OperatorName = context.Operators.Where(o => o.OperatorId == ma.OperatorId).Select(p => p.Name).Single(), + Products = context.ContractProducts.Where(cp => cp.ContractId == mc.ContractId).Select(cp => new { + cp.DisplayText, + cp.ProductName, + cp.ContractProductId, + cp.ProductType, + cp.Value + }).ToList() + // Add more properties as needed + }).SingleOrDefault() + }) + .ToListAsync(cancellationToken); + + List result = new(); + foreach (var merchantContract in merchantContracts) + { + var c = new MerchantContract + { + ContractId = merchantContract.ContractId, + ContractName = merchantContract.ContractInfo.Description, + IsDeleted = merchantContract.IsDeleted, + MerchantId = merchantContract.MerchantId, + OperatorName = merchantContract.ContractInfo.OperatorName, + ContractProducts = new List() + }; - DatabaseProjections.SettlementGroupProjection todaySettlement = await this.GetSettlementSummary(todaySettlementData, cancellationToken); - DatabaseProjections.SettlementGroupProjection comparisonSettlement = await this.GetSettlementSummary(comparisonSettlementData, cancellationToken); + foreach (var product in merchantContract.ContractInfo.Products) { + c.ContractProducts.Add(new MerchantContractProduct { + ContractId = merchantContract.ContractId, + DisplayText = product.DisplayText, + MerchantId = merchantContract.MerchantId, + ProductName = product.ProductName, + ProductId = product.ContractProductId, + ProductType = product.ProductType, + Value = product.Value + }); + } - TodaysSettlement response = new() { - ComparisonSettlementCount = comparisonSettlement.SettledCount, - ComparisonSettlementValue = comparisonSettlement.SettledValue, - ComparisonPendingSettlementCount = comparisonSettlement.UnSettledCount, - ComparisonPendingSettlementValue = comparisonSettlement.UnSettledValue, - TodaysSettlementCount = todaySettlement.SettledCount, - TodaysSettlementValue = todaySettlement.SettledValue, - TodaysPendingSettlementCount = todaySettlement.UnSettledCount, - TodaysPendingSettlementValue = todaySettlement.UnSettledValue - }; + result.Add(c); + } - return response; + return result; } - private async Task GetSettlementSummary(IQueryable query, - CancellationToken cancellationToken) { - // Get the settleed fees summary - SettlementGroupProjection summary = await BuildSettlementSummaryQuery(query).SingleOrDefaultAsync(cancellationToken); + public async Task> GetMerchantDevices(MerchantQueries.GetMerchantDevicesQuery request, + CancellationToken cancellationToken) { + using ResolvedDbContext? resolvedContext = this.Resolver.Resolve(EstateManagementDatabaseName, request.EstateId.ToString()); + await using EstateManagementContext context = resolvedContext.Context; - return new DatabaseProjections.SettlementGroupProjection { SettledCount = summary.SettledCount, - SettledValue = summary.SettledValue, - UnSettledCount = summary.UnSettledCount, - UnSettledValue = summary.UnSettledValue }; - } + List merchantDevices = await context.MerchantDevices.Where(mo => mo.MerchantId == request.MerchantId).ToListAsync(cancellationToken); - private static IQueryable BuildSettlementSummaryQuery( - IQueryable query) - { - return query - .GroupBy(_ => 1) - .Select(g => new SettlementGroupProjection + List result = new(); + foreach (TransactionProcessor.Database.Entities.MerchantDevice merchantDevice in merchantDevices) + { + result.Add(new MerchantDevice { - SettledCount = g.Count(x => x.Fee.IsSettled), - SettledValue = g.Where(x => x.Fee.IsSettled).Sum(x => x.Fee.CalculatedValue), - UnSettledCount = g.Count(x => !x.Fee.IsSettled), - UnSettledValue = g.Where(x => !x.Fee.IsSettled).Sum(x => x.Fee.CalculatedValue) + DeviceId = merchantDevice.DeviceId, + DeviceIdentifier = merchantDevice.DeviceIdentifier, + IsDeleted = false, + MerchantId = merchantDevice.MerchantId }); - } + } - private static IQueryable BuildSettlementSummaryQuery( - IQueryable query) - { - return query - .GroupBy(_ => 1) - .Select(g => new SettlementGroupProjection - { - SettledCount = g.Count(x => x.Fee.IsSettled), - SettledValue = g.Where(x => x.Fee.IsSettled).Sum(x => x.Fee.CalculatedValue), - UnSettledCount = g.Count(x => !x.Fee.IsSettled), - UnSettledValue = g.Where(x => !x.Fee.IsSettled).Sum(x => x.Fee.CalculatedValue) - }); + return result; } - private async Task GetSettlementSummary( - IQueryable query, - CancellationToken cancellationToken) { - - // Get the settleed fees summary - SettlementGroupProjection summary = await BuildSettlementSummaryQuery(query).SingleOrDefaultAsync(cancellationToken); - - return new DatabaseProjections.SettlementGroupProjection { - SettledCount = summary.SettledCount, - SettledValue = summary.SettledValue, - UnSettledCount = summary.UnSettledCount, - UnSettledValue = summary.UnSettledValue - }; + private IQueryable BuildTodaySalesQuery(EstateManagementContext context) { + return from t in context.TodayTransactions where t.IsAuthorised && t.TransactionType == "Sale" && t.TransactionDate == DateTime.Now.Date && t.TransactionTime <= DateTime.Now.TimeOfDay select t; } - public async Task> GetOperators(Guid estateId, - CancellationToken cancellationToken) { - using ResolvedDbContext? resolvedContext = this.Resolver.Resolve(EstateManagementDatabaseName, estateId.ToString()); - await using EstateManagementContext context = resolvedContext.Context; - - List operators = await (from o in context.Operators select new Operator { Name = o.Name, EstateReportingId = context.Estates.Single(e => e.EstateId == o.EstateId).EstateReportingId, OperatorId = o.OperatorId, OperatorReportingId = o.OperatorReportingId }).ToListAsync(cancellationToken); - - return operators; + private IQueryable BuildComparisonSalesQuery(EstateManagementContext context, + DateTime comparisonDate) { + return from t in context.TransactionHistory where t.IsAuthorised && t.TransactionType == "Sale" && t.TransactionDate == comparisonDate && t.TransactionTime <= DateTime.Now.TimeOfDay select t; } - #endregion + - #region Others - private const String ConnectionStringIdentifier = "EstateReportingReadModel"; + + + #endregion -} \ No newline at end of file +} diff --git a/EstateReportingAPI.BusinessLogic/RequestHandlers/CalendarRequestHandler.cs b/EstateReportingAPI.BusinessLogic/RequestHandlers/CalendarRequestHandler.cs index 50fcc79..2ffa65a 100644 --- a/EstateReportingAPI.BusinessLogic/RequestHandlers/CalendarRequestHandler.cs +++ b/EstateReportingAPI.BusinessLogic/RequestHandlers/CalendarRequestHandler.cs @@ -1,62 +1,10 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using EstateReportingAPI.BusinessLogic.Queries; +using EstateReportingAPI.BusinessLogic.Queries; using EstateReportingAPI.Models; using MediatR; using SimpleResults; -namespace EstateReportingAPI.BusinessLogic.RequestHandlers -{ - public class TransactionRequestHandler : IRequestHandler>, - IRequestHandler>, - IRequestHandler>>, - IRequestHandler>>, - IRequestHandler>> { - private readonly IReportingManager Manager; - - public TransactionRequestHandler(IReportingManager manager) { - this.Manager = manager; - } - - public async Task> Handle(TransactionQueries.TodaysFailedSales request, - CancellationToken cancellationToken) { - var result = await this.Manager.GetTodaysFailedSales(request.estateId, request.comparisonDate, request.responseCode, cancellationToken); - return Result.Success(result); - } - - public async Task> Handle(TransactionQueries.TodaysSalesQuery request, - CancellationToken cancellationToken) { - var result = await this.Manager.GetTodaysSales(request.estateId, request.merchantReportingId, request.operatorReportingId, request.comparisonDate, cancellationToken); - return Result.Success(result); - } - - public async Task>> Handle(TransactionQueries.TodaysSalesCountByHour request, - CancellationToken cancellationToken) { - var result = await this.Manager.GetTodaysSalesCountByHour(request.estateId, request.merchantReportingId, request.operatorReportingId, request.comparisonDate, cancellationToken); - return Result.Success(result); - } - - public async Task>> Handle(TransactionQueries.TodaysSalesValueByHour request, - CancellationToken cancellationToken) { - var result = await this.Manager.GetTodaysSalesValueByHour(request.estateId, request.merchantReportingId, request.operatorReportingId, request.comparisonDate, cancellationToken); - return Result.Success(result); - } - - public async Task>> Handle(TransactionQueries.TransactionSearchQuery request, - CancellationToken cancellationToken) { - var result = await this.Manager.TransactionSearch(request.estateId, request.request, request.pagingRequest, request.sortingRequest, cancellationToken); - return Result.Success(result); - } - - - } - - - - public class CalendarRequestHandler : IRequestHandler>>, +namespace EstateReportingAPI.BusinessLogic.RequestHandlers; +public class CalendarRequestHandler : IRequestHandler>>, IRequestHandler>>, IRequestHandler>> { private readonly IReportingManager Manager; @@ -65,7 +13,7 @@ public CalendarRequestHandler(IReportingManager manager) { } public async Task>> Handle(CalendarQueries.GetAllDatesQuery request, CancellationToken cancellationToken) { - List result = await this.Manager.GetCalendarDates(request.EstateId, cancellationToken); + List result = await this.Manager.GetCalendarDates(request, cancellationToken); if (result.Any() == false) { return Result.NotFound("No calendar dates found"); @@ -77,7 +25,7 @@ public async Task>> Handle(CalendarQueries.GetAllDatesQuer public async Task>> Handle(CalendarQueries.GetComparisonDatesQuery request, CancellationToken cancellationToken) { - List result = await this.Manager.GetCalendarComparisonDates(request.EstateId, cancellationToken); + List result = await this.Manager.GetCalendarComparisonDates(request, cancellationToken); if (result.Any() == false) { return Result.NotFound("No calendar comparison dates found"); @@ -88,7 +36,7 @@ public async Task>> Handle(CalendarQueries.GetComparisonDa public async Task>> Handle(CalendarQueries.GetYearsQuery request, CancellationToken cancellationToken) { - List result = await this.Manager.GetCalendarYears(request.EstateId, cancellationToken); + List result = await this.Manager.GetCalendarYears(request, cancellationToken); if (result.Any() == false) { return Result.NotFound("No calendar years found"); @@ -96,180 +44,4 @@ public async Task>> Handle(CalendarQueries.GetYearsQuery requ return Result.Success(result); } - } - - public class MerchantRequestHandler :IRequestHandler>>, - IRequestHandler>>, - IRequestHandler>, - IRequestHandler>, - IRequestHandler>>, - IRequestHandler>> - { - private readonly IReportingManager Manager; - public MerchantRequestHandler(IReportingManager manager) - { - this.Manager = manager; - } - public async Task>> Handle(MerchantQueries.GetMerchantsQuery request, - CancellationToken cancellationToken) { - List result = await this.Manager.GetMerchants(request.EstateId, cancellationToken); - if (result.Any() == false) - { - return Result.NotFound("No merchants found"); - } - - return Result.Success(result); - } - - public async Task>> Handle(MerchantQueries.GetByLastSaleQuery request, - CancellationToken cancellationToken) { - var result = await this.Manager.GetMerchantsByLastSale(request.EstateId, request.StartDateTime, request.EndDateTime, cancellationToken); - return Result.Success(result); - } - - public async Task> Handle(MerchantQueries.GetMerchantPerformanceQuery request, - CancellationToken cancellationToken) { - var result = await this.Manager.GetMerchantPerformance(request.EstateId, request.comparisonDate, request.merchantReportingIds, cancellationToken); - return Result.Success(result); - } - - public async Task> Handle(MerchantQueries.GetTransactionKpisQuery request, - CancellationToken cancellationToken) { - var result = await this.Manager.GetMerchantsTransactionKpis(request.EstateId, cancellationToken); - return Result.Success(result); - } - - public async Task>> Handle(MerchantQueries.GetBottomMerchantsBySalesValueQuery request, - CancellationToken cancellationToken) { - var result = await this.Manager.GetTopBottomData(request.EstateId, TopBottom.Bottom, request.numberOfMerchants, Dimension.Merchant, cancellationToken); - return Result.Success(result); - } - - public async Task>> Handle(MerchantQueries.GetTopMerchantsBySalesValueQuery request, - CancellationToken cancellationToken) { - var result = await this.Manager.GetTopBottomData(request.EstateId, TopBottom.Top, request.numberOfMerchants, Dimension.Merchant, cancellationToken); - return Result.Success(result); - } - } - - public class OperatorRequestHandler : IRequestHandler>>, - IRequestHandler>, - IRequestHandler>>, - IRequestHandler>> - { - private readonly IReportingManager Manager; - public OperatorRequestHandler(IReportingManager manager) - { - this.Manager = manager; - } - - public async Task>> Handle(OperatorQueries.GetOperatorsQuery request, - CancellationToken cancellationToken) - { - List result = await this.Manager.GetOperators(request.EstateId, cancellationToken); - if (result.Any() == false) - { - return Result.NotFound("No operators found"); - } - - return Result.Success(result); - } - - public async Task> Handle(OperatorQueries.GetOperatorPerformanceQuery request, - CancellationToken cancellationToken) - { - var result = await this.Manager.GetOperatorPerformance(request.EstateId, request.comparisonDate, request.operatorReportingIds, cancellationToken); - return Result.Success(result); - } - - public async Task>> Handle(OperatorQueries.GetTopOperatorsBySalesValueQuery request, - CancellationToken cancellationToken) { - var result = await this.Manager.GetTopBottomData(request.EstateId, TopBottom.Top, request.numberOfOperators, Dimension.Operator, cancellationToken); - return Result.Success(result); - } - - public async Task>> Handle(OperatorQueries.GetBottomOperatorsBySalesValueQuery request, - CancellationToken cancellationToken) { - var result = await this.Manager.GetTopBottomData(request.EstateId, TopBottom.Bottom, request.numberOfOperators, Dimension.Operator, cancellationToken); - return Result.Success(result); - } - } - - public class ResponseCodeRequestHandler : IRequestHandler>> { - private readonly IReportingManager Manager; - - public ResponseCodeRequestHandler(IReportingManager manager) { - this.Manager = manager; - } - - public async Task>> Handle(ResponseCodeQueries.GetResponseCodesQuery request, - CancellationToken cancellationToken) { - List result = await this.Manager.GetResponseCodes(request.EstateId, cancellationToken); - if (result.Any() == false) { - return Result.NotFound("No response codes found"); - } - - return Result.Success(result); - } - } - - public class SettlementRequestHandler : IRequestHandler>, - IRequestHandler>, - IRequestHandler>> { - private readonly IReportingManager Manager; - public SettlementRequestHandler(IReportingManager manager) - { - this.Manager = manager; - } - - public async Task> Handle(SettlementQueries.GetTodaysSettlementQuery request, - CancellationToken cancellationToken) { - Models.TodaysSettlement model = await this.Manager.GetTodaysSettlement(request.EstateId, request.MerchantReportingId, request.OperatorReportingId, request.ComparisonDate, cancellationToken); - - return Result.Success(model); - } - - public async Task> Handle(SettlementQueries.GetLastSettlementQuery request, - CancellationToken cancellationToken) - { - LastSettlement model = await this.Manager.GetLastSettlement(request.EstateId, cancellationToken); - - return Result.Success(model); - } - - public async Task>> Handle(SettlementQueries.GetUnsettledFeesQuery request, - CancellationToken cancellationToken) { - List model = await this.Manager.GetUnsettledFees(request.EstateId, request.StartDate, request.EndDate, request.MerchantIdFilter, request.OperatorIdFilter, request.ProductIdFilter, request.GroupByOption, cancellationToken); - return Result.Success(model); - } - } - - public class ProductRequestHandler : IRequestHandler>, - IRequestHandler>>, - IRequestHandler>> - { - private readonly IReportingManager Manager; - - public ProductRequestHandler(IReportingManager manager) { - this.Manager = manager; - } - public async Task> Handle(ProductQueries.GetProductPerformanceQuery request, - CancellationToken cancellationToken) { - var result = await this.Manager.GetProductPerformance(request.EstateId, request.comparisonDate, request.productReportingIds, cancellationToken); - - return Result.Success(result); - } - - public async Task>> Handle(ProductQueries.GetTopProductsBySalesValueQuery request, - CancellationToken cancellationToken) { - var result = await this.Manager.GetTopBottomData(request.EstateId, TopBottom.Top, request.numberOfProducts, Dimension.Product, cancellationToken); - return Result.Success(result); - } - - public async Task>> Handle(ProductQueries.GetBottomProductsBySalesValueQuery request, - CancellationToken cancellationToken) { - var result = await this.Manager.GetTopBottomData(request.EstateId, TopBottom.Bottom, request.numberOfProducts, Dimension.Product, cancellationToken); - return Result.Success(result); - } - } -} + } \ No newline at end of file diff --git a/EstateReportingAPI.BusinessLogic/RequestHandlers/ContractRequestHandler.cs b/EstateReportingAPI.BusinessLogic/RequestHandlers/ContractRequestHandler.cs new file mode 100644 index 0000000..a56373e --- /dev/null +++ b/EstateReportingAPI.BusinessLogic/RequestHandlers/ContractRequestHandler.cs @@ -0,0 +1,38 @@ +using EstateReportingAPI.BusinessLogic.Queries; +using EstateReportingAPI.Models; +using MediatR; +using SimpleResults; + +namespace EstateReportingAPI.BusinessLogic.RequestHandlers; + +public class ContractRequestHandler : IRequestHandler>>, + IRequestHandler>>, + IRequestHandler> +{ + + private readonly IReportingManager Manager; + + public ContractRequestHandler(IReportingManager manager) + { + this.Manager = manager; + } + public async Task>> Handle(ContractQueries.GetRecentContractsQuery request, + CancellationToken cancellationToken) { + var result = await this.Manager.GetRecentContracts(request, cancellationToken); + return Result.Success(result); + } + + public async Task>> Handle(ContractQueries.GetContractsQuery request, + CancellationToken cancellationToken) + { + var result = await this.Manager.GetContracts(request, cancellationToken); + return Result.Success(result); + } + + public async Task> Handle(ContractQueries.GetContractQuery request, + CancellationToken cancellationToken) + { + var result = await this.Manager.GetContract(request, cancellationToken); + return Result.Success(result); + } +} \ No newline at end of file diff --git a/EstateReportingAPI.BusinessLogic/RequestHandlers/EstateRequestHandler.cs b/EstateReportingAPI.BusinessLogic/RequestHandlers/EstateRequestHandler.cs new file mode 100644 index 0000000..e07c589 --- /dev/null +++ b/EstateReportingAPI.BusinessLogic/RequestHandlers/EstateRequestHandler.cs @@ -0,0 +1,30 @@ +using EstateReportingAPI.BusinessLogic.Queries; +using EstateReportingAPI.Models; +using MediatR; +using SimpleResults; + +namespace EstateReportingAPI.BusinessLogic.RequestHandlers; + +public class EstateRequestHandler : IRequestHandler>, + IRequestHandler>> +{ + + private readonly IReportingManager Manager; + + public EstateRequestHandler(IReportingManager manager) + { + this.Manager = manager; + } + public async Task> Handle(EstateQueries.GetEstateQuery request, + CancellationToken cancellationToken) + { + var result = await this.Manager.GetEstate(request, cancellationToken); + return Result.Success(result); + } + + public async Task>> Handle(EstateQueries.GetEstateOperatorsQuery request, + CancellationToken cancellationToken) { + var result = await this.Manager.GetEstateOperators(request, cancellationToken); + return Result.Success(result); + } +} \ No newline at end of file diff --git a/EstateReportingAPI.BusinessLogic/RequestHandlers/MerchantRequestHandler.cs b/EstateReportingAPI.BusinessLogic/RequestHandlers/MerchantRequestHandler.cs new file mode 100644 index 0000000..e642e6c --- /dev/null +++ b/EstateReportingAPI.BusinessLogic/RequestHandlers/MerchantRequestHandler.cs @@ -0,0 +1,67 @@ +using EstateReportingAPI.BusinessLogic.Queries; +using EstateReportingAPI.Models; +using MediatR; +using SimpleResults; + +namespace EstateReportingAPI.BusinessLogic.RequestHandlers; + +public class MerchantRequestHandler : IRequestHandler>>, + IRequestHandler>, + IRequestHandler>>, + IRequestHandler>, + IRequestHandler>>, + IRequestHandler>>, + IRequestHandler>> +{ + private readonly IReportingManager Manager; + public MerchantRequestHandler(IReportingManager manager) + { + this.Manager = manager; + } + + public async Task>> Handle(MerchantQueries.GetRecentMerchantsQuery request, + CancellationToken cancellationToken) { + var result = await this.Manager.GetRecentMerchants(request, cancellationToken); + return Result.Success(result); + } + public async Task> Handle(MerchantQueries.GetTransactionKpisQuery request, + CancellationToken cancellationToken) + { + var result = await this.Manager.GetMerchantsTransactionKpis(request, cancellationToken); + return Result.Success(result); + } + + public async Task>> Handle(MerchantQueries.GetMerchantsQuery request, + CancellationToken cancellationToken) { + var result = await this.Manager.GetMerchants(request, cancellationToken); + return Result.Success(result); + } + + public async Task> Handle(MerchantQueries.GetMerchantQuery request, + CancellationToken cancellationToken) { + var result = await this.Manager.GetMerchant(request, cancellationToken); + + if (result == null) + return Result.NotFound(); + + return Result.Success(result); + } + + public async Task>> Handle(MerchantQueries.GetMerchantContractsQuery request, + CancellationToken cancellationToken) { + var result = await this.Manager.GetMerchantContracts(request, cancellationToken); + return Result.Success(result); + } + + public async Task>> Handle(MerchantQueries.GetMerchantOperatorsQuery request, + CancellationToken cancellationToken) { + var result = await this.Manager.GetMerchantOperators(request, cancellationToken); + return Result.Success(result); + } + + public async Task>> Handle(MerchantQueries.GetMerchantDevicesQuery request, + CancellationToken cancellationToken) { + var result = await this.Manager.GetMerchantDevices(request, cancellationToken); + return Result.Success(result); + } +} \ No newline at end of file diff --git a/EstateReportingAPI.BusinessLogic/RequestHandlers/OperatorRequestHandler.cs b/EstateReportingAPI.BusinessLogic/RequestHandlers/OperatorRequestHandler.cs new file mode 100644 index 0000000..e6c7c0f --- /dev/null +++ b/EstateReportingAPI.BusinessLogic/RequestHandlers/OperatorRequestHandler.cs @@ -0,0 +1,29 @@ +using EstateReportingAPI.BusinessLogic.Queries; +using EstateReportingAPI.Models; +using MediatR; +using SimpleResults; + +namespace EstateReportingAPI.BusinessLogic.RequestHandlers; + +public class OperatorRequestHandler : IRequestHandler>>, + IRequestHandler> +{ + + private readonly IReportingManager Manager; + + public OperatorRequestHandler(IReportingManager manager) { + this.Manager = manager; + } + + public async Task>> Handle(OperatorQueries.GetOperatorsQuery request, + CancellationToken cancellationToken) { + List result = await this.Manager.GetOperators(request, cancellationToken); + return Result.Success(result); + } + + public async Task> Handle(OperatorQueries.GetOperatorQuery request, + CancellationToken cancellationToken) { + Operator result = await this.Manager.GetOperator(request, cancellationToken); + return Result.Success(result); + } +} \ No newline at end of file diff --git a/EstateReportingAPI.BusinessLogic/RequestHandlers/TransactionRequestHandler.cs b/EstateReportingAPI.BusinessLogic/RequestHandlers/TransactionRequestHandler.cs new file mode 100644 index 0000000..445c627 --- /dev/null +++ b/EstateReportingAPI.BusinessLogic/RequestHandlers/TransactionRequestHandler.cs @@ -0,0 +1,27 @@ +using EstateReportingAPI.BusinessLogic.Queries; +using EstateReportingAPI.Models; +using MediatR; +using SimpleResults; + +namespace EstateReportingAPI.BusinessLogic.RequestHandlers; + +public class TransactionRequestHandler : IRequestHandler>, + IRequestHandler>{ + private readonly IReportingManager Manager; + + public TransactionRequestHandler(IReportingManager manager) { + this.Manager = manager; + } + + public async Task> Handle(TransactionQueries.TodaysFailedSales request, + CancellationToken cancellationToken) { + var result = await this.Manager.GetTodaysFailedSales(request, cancellationToken); + return Result.Success(result); + } + + public async Task> Handle(TransactionQueries.TodaysSalesQuery request, + CancellationToken cancellationToken) { + var result = await this.Manager.GetTodaysSales(request, cancellationToken); + return Result.Success(result); + } +} \ No newline at end of file diff --git a/EstateReportingAPI.Client/EstateReportingApiClient.cs b/EstateReportingAPI.Client/EstateReportingApiClient.cs deleted file mode 100644 index 38a6872..0000000 --- a/EstateReportingAPI.Client/EstateReportingApiClient.cs +++ /dev/null @@ -1,629 +0,0 @@ -using Shared.Results; -using SimpleResults; - -namespace EstateReportingAPI.Client{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Net.Http; - using System.Net.Http.Headers; - using System.Threading; - using System.Threading.Tasks; - using ClientProxyBase; - using DataTransferObjects; - using DataTrasferObjects; - using Newtonsoft.Json; - - public class EstateReportingApiClient : ClientProxyBase, IEstateReportingApiClient{ - #region Fields - - private readonly Func BaseAddressResolver; - - #endregion - - #region Constructors - - public EstateReportingApiClient(Func baseAddressResolver, - HttpClient httpClient) : base(httpClient){ - this.BaseAddressResolver = baseAddressResolver; - - // Add the API version header - this.HttpClient.DefaultRequestHeaders.Add("api-version", "1.0"); - } - - #endregion - - #region Methods - private const String EstateIdHeaderName = "EstateId"; - public async Task>> GetCalendarDates(String accessToken, Guid estateId, Int32 year, CancellationToken cancellationToken){ - String requestUri = this.BuildRequestUrl($"/api/dimensions/calendar/{year}/dates"); - - try{ - List<(String headerName, String headerValue)> additionalHeaders = [ - (EstateIdHeaderName, estateId.ToString()) - ]; - - Result> result = await this.SendHttpGetRequest>(requestUri, accessToken, additionalHeaders, cancellationToken); - - if (result.IsFailed) - return ResultHelpers.CreateFailure(result); - - return result; - } - catch(Exception ex){ - // 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); - - return Result.Failure(exception.Message); - } - } - - public async Task>> GetCalendarYears(String accessToken, Guid estateId, CancellationToken cancellationToken){ - - String requestUri = this.BuildRequestUrl("/api/dimensions/calendar/years"); - - try{ - List<(String headerName, String headerValue)> additionalHeaders = [ - (EstateIdHeaderName, estateId.ToString()) - ]; - Result> result = await this.SendHttpGetRequest>(requestUri, accessToken, additionalHeaders, cancellationToken); - - if (result.IsFailed) - return ResultHelpers.CreateFailure(result); - - return result; - } - catch(Exception ex){ - // An exception has occurred, add some additional information to the message - Exception exception = new Exception($"Error getting calendar years for estate {estateId}.", ex); - - return Result.Failure(exception.Message); - } - } - - public async Task>> GetComparisonDates(String accessToken, Guid estateId, CancellationToken cancellationToken){ - String requestUri = this.BuildRequestUrl("/api/dimensions/calendar/comparisondates"); - - try{ - List<(String headerName, String headerValue)> additionalHeaders = [ - (EstateIdHeaderName, estateId.ToString()) - ]; - - Result> result = await this.SendHttpGetRequest>(requestUri, accessToken, additionalHeaders, cancellationToken); - - if (result.IsFailed) - return ResultHelpers.CreateFailure(result); - - return result; - } - catch(Exception ex){ - // An exception has occurred, add some additional information to the message - Exception exception = new Exception($"Error getting comparison dates for estate {estateId}.", ex); - - return Result.Failure(exception.Message); - } - } - - public async Task> GetLastSettlement(String accessToken, Guid estateId, CancellationToken cancellationToken){ - String requestUri = this.BuildRequestUrl("/api/facts/settlements/lastsettlement"); - - try{ - List<(String headerName, String headerValue)> additionalHeaders = [ - (EstateIdHeaderName, estateId.ToString()) - ]; - Result result = await this.SendHttpGetRequest(requestUri, accessToken, additionalHeaders, cancellationToken); - - if (result.IsFailed) - return ResultHelpers.CreateFailure(result); - - return result; - } - catch(Exception ex){ - // An exception has occurred, add some additional information to the message - Exception exception = new Exception($"Error getting last settlement for estate {estateId}.", ex); - - return Result.Failure(exception.Message); - } - } - - public async Task>> GetResponseCodes(String accessToken, Guid estateId, CancellationToken cancellationToken){ - String requestUri = this.BuildRequestUrl("/api/dimensions/responsecodes"); - - try - { - List<(String headerName, String headerValue)> additionalHeaders = [ - (EstateIdHeaderName, estateId.ToString()) - ]; - Result> result = await this.SendHttpGetRequest>(requestUri, accessToken, additionalHeaders, cancellationToken); - - if (result.IsFailed) - return ResultHelpers.CreateFailure(result); - - return result; - } - catch (Exception ex) - { - // An exception has occurred, add some additional information to the message - Exception exception = new Exception($"Error getting response codes for estate {estateId}.", ex); - - return Result.Failure(exception.Message); - } - } - - public async Task> GetMerchantPerformance(String accessToken, Guid estateId, DateTime comparisonDate, List merchantReportingIds, CancellationToken cancellationToken){ - - // Serialize the integer array into a comma-separated string - string serializedArray = string.Join(",", merchantReportingIds); - - String requestUri = this.BuildRequestUrl($"/api/facts/transactions/merchants/performance?comparisonDate={comparisonDate.Date:yyyy-MM-dd}&merchantReportingIds={serializedArray}"); - - try - { - List<(String headerName, String headerValue)> additionalHeaders = [ - (EstateIdHeaderName, estateId.ToString()) - ]; - Result result = await this.SendHttpGetRequest(requestUri, accessToken, additionalHeaders, cancellationToken); - - if (result.IsFailed) - return ResultHelpers.CreateFailure(result); - - return result; - } - catch (Exception ex) - { - // An exception has occurred, add some additional information to the message - Exception exception = new Exception($"Error getting merchant performance for estate {estateId}.", ex); - - return Result.Failure(exception.Message); - } - } - - public async Task> GetProductPerformance(String accessToken, Guid estateId, DateTime comparisonDate, List productReportingIds, CancellationToken cancellationToken){ - // Serialize the integer array into a comma-separated string - string serializedArray = string.Join(",", productReportingIds); - - String requestUri = this.BuildRequestUrl($"/api/facts/transactions/products/performance?comparisonDate={comparisonDate.Date:yyyy-MM-dd}&productReportingIds={serializedArray}"); - - try - { - List<(String headerName, String headerValue)> additionalHeaders = [ - (EstateIdHeaderName, estateId.ToString()) - ]; - Result result = await this.SendHttpGetRequest(requestUri, accessToken, additionalHeaders, cancellationToken); - - if (result.IsFailed) - return ResultHelpers.CreateFailure(result); - - return result; - } - catch (Exception ex) - { - // An exception has occurred, add some additional information to the message - Exception exception = new Exception($"Error getting product performance for estate {estateId}.", ex); - - return Result.Failure(exception.Message); - } - } - - public async Task>> GetMerchantsByLastSaleDate(String accessToken, Guid estateId, DateTime startDate, DateTime endDate, CancellationToken cancellationToken){ - - String requestUri = this.BuildRequestUrl($"/api/facts/transactions/merchants/lastsale?startDate={startDate:yyyy-MM-dd HH:mm:ss}&enddate={endDate:yyyy-MM-dd HH:mm:ss}"); - - try - { - List<(String headerName, String headerValue)> additionalHeaders = [ - (EstateIdHeaderName, estateId.ToString()) - ]; - Result>? result = await this.SendHttpGetRequest>(requestUri, accessToken, additionalHeaders, cancellationToken); - - if (result.IsFailed) - return ResultHelpers.CreateFailure(result); - - return result; - } - catch (Exception ex) - { - // 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); - - return Result.Failure(exception.Message); - } - } - - public async Task> GetOperatorPerformance(String accessToken, Guid estateId, DateTime comparisonDate, List operatorReportingIds, CancellationToken cancellationToken){ - // Serialize the integer array into a comma-separated string - string serializedArray = string.Join(",", operatorReportingIds); - - String requestUri = this.BuildRequestUrl($"/api/facts/transactions/operators/performance?comparisonDate={comparisonDate.Date:yyyy-MM-dd}&operatorReportingIds={serializedArray}"); - - try - { - List<(String headerName, String headerValue)> additionalHeaders = [ - (EstateIdHeaderName, estateId.ToString()) - ]; - Result? result = await this.SendHttpGetRequest(requestUri, accessToken, additionalHeaders, cancellationToken); - - if (result.IsFailed) - return ResultHelpers.CreateFailure(result); - - return result; - } - catch (Exception ex) - { - // An exception has occurred, add some additional information to the message - Exception exception = new Exception($"Error getting operator performance for estate {estateId}.", ex); - - return Result.Failure(exception.Message); - } - } - - public async Task>> TransactionSearch(String accessToken, Guid estateId, TransactionSearchRequest searchRequest, Int32? page, Int32? pageSize, SortField? sortField, SortDirection? sortDirection, CancellationToken cancellationToken){ - QueryStringBuilder builder = new QueryStringBuilder(); - if (page.HasValue){ - builder.AddParameter("page", page.Value); - } - if (pageSize.HasValue) - { - builder.AddParameter("pageSize", pageSize.Value); - } - if (sortField.HasValue) - { - builder.AddParameter("sortField", (Int32)sortField.Value); - } - if (sortDirection.HasValue) - { - builder.AddParameter("sortDirection", (Int32)sortDirection.Value); - } - - String requestUri = this.BuildRequestUrl($"/api/facts/transactions/search?{builder.BuildQueryString()}"); - - try - { - List<(String headerName, String headerValue)> additionalHeaders = [ - (EstateIdHeaderName, estateId.ToString()) - ]; - - Result>? result = await this.SendHttpPostRequest>(requestUri, searchRequest, accessToken, additionalHeaders, cancellationToken); - - if (result.IsFailed) - return ResultHelpers.CreateFailure(result); - - return result; - } - catch (Exception ex) - { - // An exception has occurred, add some additional information to the message - Exception exception = new Exception($"Error getting operator performance for estate {estateId}.", ex); - - return Result.Failure(exception.Message); - } - } - - public async Task>> GetUnsettledFees(String accessToken, Guid estateId, DateTime startDate, DateTime endDate, List merchantIds, List operatorIds, List productIds, GroupByOption groupBy, CancellationToken cancellationToken){ - QueryStringBuilder builder = new QueryStringBuilder(); - builder.AddParameter("startDate", $"{startDate:yyyy-MM-dd}"); - builder.AddParameter("endDate", $"{endDate:yyyy-MM-dd}"); - if (merchantIds != null) - { - string serializedMerchantIdsArray = string.Join(",", merchantIds); - builder.AddParameter("merchantIds", serializedMerchantIdsArray); - } - if (operatorIds != null) - { - string serializedOperatorIdsArray = string.Join(",", operatorIds); - builder.AddParameter("operatorIds", serializedOperatorIdsArray); - } - if (productIds != null) - { - string serializedProductIdsArray = string.Join(",", productIds); - builder.AddParameter("productIds", serializedProductIdsArray); - } - - builder.AddParameter("groupByOption", (Int32)groupBy, true); - - - String requestUri = this.BuildRequestUrl($"/api/facts/settlements/unsettledfees?{builder.BuildQueryString()}"); - - try - { - List<(String headerName, String headerValue)> additionalHeaders = [ - (EstateIdHeaderName, estateId.ToString()) - ]; - Result>? result = await this.SendHttpGetRequest>(requestUri, accessToken, additionalHeaders, cancellationToken); - - if (result.IsFailed) - return ResultHelpers.CreateFailure(result); - - return result; - } - catch (Exception ex) - { - // An exception has occurred, add some additional information to the message - Exception exception = new Exception($"Error getting unsettled fees for estate {estateId}.", ex); - - return Result.Failure(exception.Message); - } - - } - - public async Task> GetMerchantKpi(String accessToken, Guid estateId, CancellationToken cancellationToken){ - String requestUri = this.BuildRequestUrl("/api/facts/transactions/merchantkpis"); - - try{ - List<(String headerName, String headerValue)> additionalHeaders = [ - (EstateIdHeaderName, estateId.ToString()) - ]; - Result? result = await this.SendHttpGetRequest(requestUri, accessToken, additionalHeaders, cancellationToken); - - if (result.IsFailed) - return ResultHelpers.CreateFailure(result); - - return result; - } - catch(Exception ex){ - // An exception has occurred, add some additional information to the message - Exception exception = new Exception($"Error getting merchant kpis for estate {estateId}.", ex); - - return Result.Failure(exception.Message); - } - } - - public async Task>> GetMerchants(String accessToken, Guid estateId, CancellationToken cancellationToken){ - - String requestUri = this.BuildRequestUrl("/api/dimensions/merchants"); - - try{ - List<(String headerName, String headerValue)> additionalHeaders = [ - (EstateIdHeaderName, estateId.ToString()) - ]; - Result>? result = await this.SendHttpGetRequest>(requestUri, accessToken, additionalHeaders, cancellationToken); - - if (result.IsFailed) - return ResultHelpers.CreateFailure(result); - - return result; - } - catch(Exception ex){ - // An exception has occurred, add some additional information to the message - Exception exception = new Exception($"Error getting merchants for estate {estateId}.", ex); - - return Result.Failure(exception.Message); - } - } - - public async Task>> GetOperators(String accessToken, Guid estateId, CancellationToken cancellationToken){ - - String requestUri = this.BuildRequestUrl("/api/dimensions/operators"); - - try{ - List<(String headerName, String headerValue)> additionalHeaders = [ - (EstateIdHeaderName, estateId.ToString()) - ]; - Result>? result = await this.SendHttpGetRequest>(requestUri, accessToken, additionalHeaders, cancellationToken); - - if (result.IsFailed) - return ResultHelpers.CreateFailure(result); - - return result; - } - catch(Exception ex){ - // An exception has occurred, add some additional information to the message - Exception exception = new Exception($"Error getting operators for estate {estateId}.", ex); - - return Result.Failure(exception.Message); - } - } - - public async Task> GetTodaysFailedSales(String accessToken, Guid estateId, Int32 merchantReportingId, Int32 operatorReportingId, String responseCode, DateTime comparisonDate, CancellationToken cancellationToken){ - QueryStringBuilder builder = new QueryStringBuilder(); - builder.AddParameter("comparisonDate", $"{comparisonDate.Date:yyyy-MM-dd}"); - builder.AddParameter("merchantReportingId", merchantReportingId); - builder.AddParameter("operatorReportingId", operatorReportingId); - builder.AddParameter("responseCode", responseCode); - - String requestUri = this.BuildRequestUrl($"/api/facts/transactions/todaysfailedsales?{builder.BuildQueryString()}"); - - try { - List<(String headerName, String headerValue)> additionalHeaders = [ - (EstateIdHeaderName, estateId.ToString()) - ]; - Result? result = await this.SendHttpGetRequest(requestUri, accessToken, additionalHeaders, cancellationToken); - - if (result.IsFailed) - return ResultHelpers.CreateFailure(result); - - return result; - } - catch (Exception ex) { - // 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); - - return Result.Failure(exception.Message); - } - } - - public async Task> GetTodaysSales(String accessToken, Guid estateId, Int32 merchantReportingId, Int32 operatorReportingId, DateTime comparisonDate, CancellationToken cancellationToken){ - QueryStringBuilder builder = new QueryStringBuilder(); - builder.AddParameter("comparisonDate", $"{comparisonDate.Date:yyyy-MM-dd}"); - builder.AddParameter("merchantReportingId", merchantReportingId); - builder.AddParameter("operatorReportingId", operatorReportingId); - - String requestUri = this.BuildRequestUrl($"/api/facts/transactions/todayssales?{builder.BuildQueryString()}"); - - try{ - List<(String headerName, String headerValue)> additionalHeaders = [ - (EstateIdHeaderName, estateId.ToString()) - ]; - Result? result = await this.SendHttpGetRequest(requestUri, accessToken, additionalHeaders, cancellationToken); - - if (result.IsFailed) - return ResultHelpers.CreateFailure(result); - - return result; - } - catch(Exception ex){ - // An exception has occurred, add some additional information to the message - Exception exception = new Exception($"Error getting todays sales for estate {estateId}.", ex); - - return Result.Failure(exception.Message); - } - } - - public async Task>> GetTodaysSalesCountByHour(String accessToken, Guid estateId, Int32 merchantReportingId, Int32 operatorReportingId, DateTime comparisonDate, CancellationToken cancellationToken){ - QueryStringBuilder builder = new QueryStringBuilder(); - builder.AddParameter("comparisonDate", $"{comparisonDate.Date:yyyy-MM-dd}"); - builder.AddParameter("merchantReportingId", merchantReportingId); - builder.AddParameter("operatorReportingId", operatorReportingId); - - String requestUri = this.BuildRequestUrl($"/api/facts/transactions/todayssales/countbyhour?{builder.BuildQueryString()}"); - - try{ - List<(String headerName, String headerValue)> additionalHeaders = [ - (EstateIdHeaderName, estateId.ToString()) - ]; - Result>? result = await this.SendHttpGetRequest>(requestUri, accessToken, additionalHeaders, cancellationToken); - - if (result.IsFailed) - return ResultHelpers.CreateFailure(result); - - return result; - } - catch(Exception ex){ - // 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); - - return Result.Failure(exception.Message); - } - } - - public async Task>> GetTodaysSalesValueByHour(String accessToken, Guid estateId, Int32 merchantReportingId, Int32 operatorReportingId, DateTime comparisonDate, CancellationToken cancellationToken){ - QueryStringBuilder builder = new QueryStringBuilder(); - builder.AddParameter("comparisonDate", $"{comparisonDate.Date:yyyy-MM-dd}"); - builder.AddParameter("merchantReportingId", merchantReportingId); - builder.AddParameter("operatorReportingId", operatorReportingId); - - String requestUri = this.BuildRequestUrl($"/api/facts/transactions/todayssales/valuebyhour?{builder.BuildQueryString()}"); - - try{ - List<(String headerName, String headerValue)> additionalHeaders = [ - (EstateIdHeaderName, estateId.ToString()) - ]; - Result>? result = await this.SendHttpGetRequest>(requestUri, accessToken, additionalHeaders, cancellationToken); - - if (result.IsFailed) - return ResultHelpers.CreateFailure(result); - - return result; - } - catch(Exception ex){ - // 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); - - return Result.Failure(exception.Message); - } - } - - public async Task> GetTodaysSettlement(String accessToken, Guid estateId, Int32 merchantReportingId, Int32 operatorReportingId, DateTime comparisonDate, CancellationToken cancellationToken){ - QueryStringBuilder builder = new QueryStringBuilder(); - builder.AddParameter("comparisonDate", $"{comparisonDate.Date:yyyy-MM-dd}"); - builder.AddParameter("merchantReportingId", merchantReportingId); - builder.AddParameter("operatorReportingId", operatorReportingId); - - String requestUri = this.BuildRequestUrl($"/api/facts/settlements/todayssettlement?{builder.BuildQueryString()}"); - - try{ - List<(String headerName, String headerValue)> additionalHeaders = [ - (EstateIdHeaderName, estateId.ToString()) - ]; - Result? result = await this.SendHttpGetRequest(requestUri, accessToken, additionalHeaders, cancellationToken); - - if (result.IsFailed) - return ResultHelpers.CreateFailure(result); - - return result; - } - catch(Exception ex){ - // An exception has occurred, add some additional information to the message - Exception exception = new Exception($"Error getting todays settlement for estate {estateId}.", ex); - - return Result.Failure(exception.Message); - } - } - - public async Task>> GetTopBottomMerchantData(String accessToken, Guid estateId, TopBottom topBottom, Int32 resultCount, CancellationToken cancellationToken){ - String requestUri = this.BuildRequestUrl($"/api/facts/transactions/merchants/topbottombyvalue?topOrBottom={(Int32)topBottom}&count={resultCount}"); - - try{ - List<(String headerName, String headerValue)> additionalHeaders = [ - (EstateIdHeaderName, estateId.ToString()) - ]; - Result>? result = await this.SendHttpGetRequest>(requestUri, accessToken, additionalHeaders, cancellationToken); - - if (result.IsFailed) - return ResultHelpers.CreateFailure(result); - - return result; - } - catch(Exception ex){ - // 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); - - return Result.Failure(exception.Message); - } - } - - public async Task>> GetTopBottomOperatorData(String accessToken, Guid estateId, TopBottom topBottom, Int32 resultCount, CancellationToken cancellationToken){ - String requestUri = this.BuildRequestUrl($"/api/facts/transactions/operators/topbottombyvalue?topOrBottom={(Int32)topBottom}&count={resultCount}"); - - try{ - List<(String headerName, String headerValue)> additionalHeaders = [ - (EstateIdHeaderName, estateId.ToString()) - ]; - Result>? result = await this.SendHttpGetRequest>(requestUri, accessToken, additionalHeaders, cancellationToken); - - if (result.IsFailed) - return ResultHelpers.CreateFailure(result); - - return result; - } - catch(Exception ex){ - // 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); - - return Result.Failure(exception.Message); - } - } - - public async Task>> GetTopBottomProductData(String accessToken, Guid estateId, TopBottom topBottom, Int32 resultCount, CancellationToken cancellationToken){ - String requestUri = this.BuildRequestUrl($"/api/facts/transactions/products/topbottombyvalue?topOrBottom={(Int32)topBottom}&count={resultCount}"); - - try{ - List<(String headerName, String headerValue)> additionalHeaders = [ - (EstateIdHeaderName, estateId.ToString()) - ]; - Result>? result = await this.SendHttpGetRequest>(requestUri, accessToken, additionalHeaders, cancellationToken); - - if (result.IsFailed) - return ResultHelpers.CreateFailure(result); - - return result; - } - catch(Exception ex){ - // 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); - - return Result.Failure(exception.Message); - } - } - - private String BuildRequestUrl(String route){ - String baseAddress = this.BaseAddressResolver("EstateReportingApi"); - - String requestUri = $"{baseAddress}{route}"; - - return requestUri; - } - - #endregion - } -} \ No newline at end of file diff --git a/EstateReportingAPI.Client/IEstateReportingApiClient.cs b/EstateReportingAPI.Client/IEstateReportingApiClient.cs deleted file mode 100644 index 9d10533..0000000 --- a/EstateReportingAPI.Client/IEstateReportingApiClient.cs +++ /dev/null @@ -1,45 +0,0 @@ -namespace EstateReportingAPI.Client{ - using System; - using System.Collections.Generic; - using System.Threading; - using System.Threading.Tasks; - using DataTransferObjects; - using DataTrasferObjects; - using SimpleResults; - - public interface IEstateReportingApiClient{ - #region Methods - - Task>> GetCalendarDates(String accessToken, Guid estateId, Int32 year, CancellationToken cancellationToken); - Task>> GetCalendarYears(String accessToken, Guid estateId, CancellationToken cancellationToken); - Task>> GetComparisonDates(String accessToken, Guid estateId, CancellationToken cancellationToken); - Task> GetLastSettlement(String accessToken, Guid estateId, CancellationToken cancellationToken); - Task> GetMerchantKpi(String accessToken, Guid estateId, CancellationToken cancellationToken); - Task> GetMerchantPerformance(String accessToken, Guid estateId, DateTime comparisonDate, List merchantReportingIds, CancellationToken cancellationToken); - Task>> GetMerchants(String accessToken, Guid estateId, CancellationToken cancellationToken); - - Task>> GetMerchantsByLastSaleDate(String accessToken, Guid estateId, DateTime startDate, DateTime endDate, CancellationToken cancellationToken); - - Task> GetOperatorPerformance(String accessToken, Guid estateId, DateTime comparisonDate, List operatorReportingIds, CancellationToken cancellationToken); - Task>> GetOperators(String accessToken, Guid estateId, CancellationToken cancellationToken); - - Task> GetProductPerformance(String accessToken, Guid estateId, DateTime comparisonDate, List productReportingIds, CancellationToken cancellationToken); - - Task>> GetResponseCodes(String accessToken, Guid estateId, CancellationToken cancellationToken); - - Task> GetTodaysFailedSales(String accessToken, Guid estateId, Int32 merchantReportingId, Int32 operatorReportingId, String responseCode, DateTime comparisonDate, CancellationToken cancellationToken); - Task> GetTodaysSales(String accessToken, Guid estateId, Int32 merchantReportingId, Int32 operatorReportingId, DateTime comparisonDate, CancellationToken cancellationToken); - Task>> GetTodaysSalesCountByHour(String accessToken, Guid estateId, Int32 merchantReportingId, Int32 operatorReportingId, DateTime comparisonDate, CancellationToken cancellationToken); - Task>> GetTodaysSalesValueByHour(String accessToken, Guid estateId, Int32 merchantReportingId, Int32 operatorReportingId, DateTime comparisonDate, CancellationToken cancellationToken); - Task> GetTodaysSettlement(String accessToken, Guid estateId, Int32 merchantReportingId, Int32 operatorReportingId, DateTime comparisonDate, CancellationToken cancellationToken); - Task>> GetTopBottomMerchantData(String accessToken, Guid estateId, TopBottom topBottom, Int32 resultCount, CancellationToken cancellationToken); - - Task>> GetTopBottomOperatorData(String accessToken, Guid estateId, TopBottom topBottom, Int32 resultCount, CancellationToken cancellationToken); - Task>> GetTopBottomProductData(String accessToken, Guid estateId, TopBottom topBottom, Int32 resultCount, CancellationToken cancellationToken); - - Task>> TransactionSearch(String accessToken, Guid estateId, TransactionSearchRequest searchRequest, Int32? page, Int32? pageSize, SortField? sortField, SortDirection? sortDirection, CancellationToken cancellationToken); - Task>> GetUnsettledFees(String accessToken, Guid estateId, DateTime startDate, DateTime endDate, List merchantIds, List operatorIds, List productIds, GroupByOption groupBy, CancellationToken cancellationToken); - - #endregion - } -} \ No newline at end of file diff --git a/EstateReportingAPI.Client/QueryStringBuilder.cs b/EstateReportingAPI.Client/QueryStringBuilder.cs deleted file mode 100644 index 83220e9..0000000 --- a/EstateReportingAPI.Client/QueryStringBuilder.cs +++ /dev/null @@ -1,76 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -public class QueryStringBuilder -{ - private Dictionary parameters = new Dictionary(); - - public QueryStringBuilder AddParameter(string key, object value, Boolean alwaysInclude=false) - { - this.parameters.Add(key, (value, alwaysInclude)); - return this; - } - - static Dictionary FilterDictionary(Dictionary inputDictionary) - { - Dictionary result = new Dictionary(); - - foreach (KeyValuePair entry in inputDictionary) - { - if (entry.Value.value != null && !IsDefaultValue(entry.Value.value, entry.Value.alwaysInclude)) - { - result.Add(entry.Key, entry.Value.value); - } - } - - return result; - } - - static bool IsDefaultValue(T value, Boolean alwaysInclude){ - if (alwaysInclude) - return false; - - Object? defaultValue = GetDefault(value.GetType()); - - if (defaultValue == null && value.GetType() == typeof(String)) - { - defaultValue = String.Empty; - } - return defaultValue.Equals(value); - } - - public static object GetDefault(Type t) - { - Func f = GetDefault; - return f.Method.GetGenericMethodDefinition().MakeGenericMethod(t).Invoke(null, null); - } - - private static T GetDefault() - { - return default(T); - } - - public string BuildQueryString(){ - Dictionary filtered = FilterDictionary(this.parameters); - - if (filtered.Count == 0) - { - return string.Empty; - } - - StringBuilder queryString = new StringBuilder(); - - foreach (KeyValuePair kvp in filtered) - { - if (queryString.Length > 0) - { - queryString.Append("&"); - } - - queryString.Append($"{Uri.EscapeDataString(kvp.Key)}={Uri.EscapeDataString(kvp.Value.ToString())}"); - } - - return queryString.ToString(); - } -} \ No newline at end of file diff --git a/EstateReportingAPI.DataTrasferObjects/Contract.cs b/EstateReportingAPI.DataTrasferObjects/Contract.cs new file mode 100644 index 0000000..d9b5682 --- /dev/null +++ b/EstateReportingAPI.DataTrasferObjects/Contract.cs @@ -0,0 +1,126 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; + +namespace EstateReportingAPI.DataTransferObjects; + +public class Contract +{ + #region Properties + [JsonProperty("estate_id")] + public Guid EstateId { get; set; } + [JsonProperty("estate_reporting_id")] + public Int32 EstateReportingId { get; set; } + [JsonProperty("contract_id")] + public Guid ContractId { get; set; } + [JsonProperty("contract_reporting_id")] + public Int32 ContractReportingId { get; set; } + [JsonProperty("description")] + public String Description { get; set; } + [JsonProperty("operator_name")] + public String OperatorName { get; set; } + [JsonProperty("operator_id")] + public Guid OperatorId { get; set; } + [JsonProperty("operator_reporting_id")] + public Int32 OperatorReportingId { get; set; } + [JsonProperty("products")] + public List Products { get; set; } + #endregion +} + +public class ContractProduct +{ + [JsonProperty("contract_id")] + public Guid ContractId { get; set; } + [JsonProperty("product_id")] + public Guid ProductId { get; set; } + [JsonProperty("product_name")] + public String ProductName { get; set; } + [JsonProperty("display_text")] + public String DisplayText { get; set; } + [JsonProperty("product_type")] + public Int32 ProductType { get; set; } + [JsonProperty("value")] + public Decimal? Value { get; set; } + [JsonProperty("transaction_fees")] + public List TransactionFees { get; set; } +} + +public class ContractProductTransactionFee { + [JsonProperty("transaction_fee_id")] + public Guid TransactionFeeId { get; set; } + [JsonProperty("description")] + public string? Description { get; set; } + [JsonProperty("calculation_type")] + public Int32 CalculationType { get; set; } + [JsonProperty("fee_type")] + public Int32 FeeType { get; set; } + [JsonProperty("value")] + public Decimal Value { get; set; } +} + + +public class Estate +{ + [JsonProperty("estate_id")] + public Guid EstateId { get; set; } + [JsonProperty("estate_name")] + public string? EstateName { get; set; } + [JsonProperty("reference")] + public string? Reference { get; set; } + [JsonProperty("operators")] + public List? Operators { get; set; } + [JsonProperty("merchants")] + public List? Merchants { get; set; } + [JsonProperty("contracts")] + public List? Contracts { get; set; } + [JsonProperty("users")] + public List? Users { get; set; } +} + +public class EstateUser +{ + [JsonProperty("user_id")] + public Guid UserId { get; set; } + [JsonProperty("email_address")] + public string? EmailAddress { get; set; } + [JsonProperty("created_date_time")] + public DateTime CreatedDateTime { get; set; } +} + +public class EstateOperator +{ + [JsonProperty("operator_id")] + public Guid OperatorId { get; set; } + [JsonProperty("name")] + public string? Name { get; set; } + [JsonProperty("require_custom_merchant_number")] + public bool RequireCustomMerchantNumber { get; set; } + [JsonProperty("require_custom_terminal_number")] + public bool RequireCustomTerminalNumber { get; set; } + [JsonProperty("created_date_time")] + public DateTime CreatedDateTime { get; set; } +} + +public class EstateContract +{ + [JsonProperty("operator_id")] + public Guid OperatorId { get; set; } + [JsonProperty("contract_id")] + public Guid ContractId { get; set; } + [JsonProperty("name")] + public string? Name { get; set; } + [JsonProperty("operator_name")] + public string? OperatorName { get; set; } +} + + +public class EstateMerchant +{ + [JsonProperty("merchant_id")] + public Guid MerchantId { get; set; } + [JsonProperty("name")] + public string? Name { get; set; } + [JsonProperty("reference")] + public string? Reference { get; set; } +} \ No newline at end of file diff --git a/EstateReportingAPI.DataTrasferObjects/Merchant.cs b/EstateReportingAPI.DataTrasferObjects/Merchant.cs index e776aad..eb3d192 100644 --- a/EstateReportingAPI.DataTrasferObjects/Merchant.cs +++ b/EstateReportingAPI.DataTrasferObjects/Merchant.cs @@ -1,35 +1,113 @@ -namespace EstateReportingAPI.DataTrasferObjects{ +namespace EstateReportingAPI.DataTransferObjects{ using Newtonsoft.Json; using System; + using System.Collections.Generic; public class Merchant{ #region Properties - [JsonProperty("created_date_time")] - public DateTime CreatedDateTime{ get; set; } - [JsonProperty("estate_reporting_id")] - public Int32 EstateReportingId{ get; set; } - [JsonProperty("last_sale")] - public DateTime LastSale{ get; set; } - [JsonProperty("last_sale_date_time")] - public DateTime LastSaleDateTime{ get; set; } - [JsonProperty("last_statement")] - public DateTime LastStatement{ get; set; } [JsonProperty("merchant_id")] public Guid MerchantId{ get; set; } [JsonProperty("merchant_reporting_id")] public Int32 MerchantReportingId{ get; set; } [JsonProperty("name")] public String Name{ get; set; } - [JsonProperty("post_code")] - public String PostCode{ get; set; } [JsonProperty("reference")] - public String Reference{ get; set; } - [JsonProperty("region")] - public String Region{ get; set; } + public String Reference { get; set; } + [JsonProperty("balance")] + public Decimal Balance { get; set; } + [JsonProperty("settlement_schedule")] + public Int32 SettlementSchedule { get; set; } + [JsonProperty("created_date_time")] + public DateTime CreatedDateTime { get; set; } + + [JsonProperty("address_id")] + public Guid AddressId { get; set; } + [JsonProperty("address_line1")] + public String AddressLine1 { get; set; } + [JsonProperty("address_line2")] + public String AddressLine2 { get; set; } [JsonProperty("town")] - public String Town{ get; set; } + public String Town { get; set; } + [JsonProperty("region")] + public String Region { get; set; } + [JsonProperty("post_code")] + public String PostCode{ get; set; } + [JsonProperty("country")] + public String Country { get; set; } + + [JsonProperty("contact_id")] + public Guid ContactId { get; set; } + [JsonProperty("contact_name")] + public String ContactName { get; set; } + [JsonProperty("contact_email")] + public String ContactEmail { get; set; } + [JsonProperty("contact_phone")] + public String ContactPhone { get; set; } + #endregion } + + public class MerchantOperator + { + [JsonProperty("merchant_id")] + public Guid MerchantId { get; set; } + [JsonProperty("operator_id")] + public Guid OperatorId { get; set; } + [JsonProperty("operator_name")] + public String OperatorName { get; set; } + [JsonProperty("merchant_number")] + public String MerchantNumber { get; set; } + [JsonProperty("terminal_number")] + public String TerminalNumber { get; set; } + [JsonProperty("is_deleted")] + public Boolean IsDeleted { get; set; } + } + + public class MerchantContract + { + [JsonProperty("merchant_id")] + public Guid MerchantId { get; set; } + [JsonProperty("contract_id")] + public Guid ContractId { get; set; } + [JsonProperty("contract_name")] + public String ContractName { get; set; } + [JsonProperty("operator_name")] + public String OperatorName { get; set; } + [JsonProperty("is_deleted")] + public Boolean IsDeleted { get; set; } + [JsonProperty("contract_products")] + public List ContractProducts { get; set; } + } + + public class MerchantContractProduct + { + [JsonProperty("merchant_id")] + public Guid MerchantId { get; set; } + [JsonProperty("contract_id")] + public Guid ContractId { get; set; } + [JsonProperty("product_id")] + public Guid ProductId { get; set; } + [JsonProperty("product_name")] + public String ProductName { get; set; } + [JsonProperty("display_text")] + public String DisplayText { get; set; } + [JsonProperty("product_type")] + public Int32 ProductType { get; set; } + [JsonProperty("value")] + public Decimal? Value { get; set; } + } + + public class MerchantDevice + { + [JsonProperty("merchant_id")] + public Guid MerchantId { get; set; } + [JsonProperty("device_id")] + public Guid DeviceId { get; set; } + [JsonProperty("device_identifier")] + public String DeviceIdentifier { get; set; } + [JsonProperty("is_deleted")] + public Boolean IsDeleted { get; set; } + } } \ No newline at end of file diff --git a/EstateReportingAPI.DataTrasferObjects/Operator.cs b/EstateReportingAPI.DataTrasferObjects/Operator.cs index db794b1..f01e350 100644 --- a/EstateReportingAPI.DataTrasferObjects/Operator.cs +++ b/EstateReportingAPI.DataTrasferObjects/Operator.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; -namespace EstateReportingAPI.DataTrasferObjects{ +namespace EstateReportingAPI.DataTransferObjects{ using System; public class Operator{ @@ -12,5 +12,9 @@ public class Operator{ public String Name { get; set; } [JsonProperty("operator_reporting_id")] public Int32 OperatorReportingId { get; set; } + [JsonProperty("require_custom_merchant_number")] + public Boolean RequireCustomMerchantNumber { get; set; } + [JsonProperty("require_custom_terminal_number")] + public Boolean RequireCustomTerminalNumber { get; set; } } } \ No newline at end of file diff --git a/EstateReportingAPI.IntegrationTests/CalendarEndpointTests.cs b/EstateReportingAPI.IntegrationTests/CalendarEndpointTests.cs new file mode 100644 index 0000000..07d347a --- /dev/null +++ b/EstateReportingAPI.IntegrationTests/CalendarEndpointTests.cs @@ -0,0 +1,833 @@ +using EstateReportingAPI.DataTransferObjects; +using EstateReportingAPI.DataTrasferObjects; +using EstateReportingAPI.Models; +using Microsoft.AspNetCore.Mvc; +using Shouldly; +using SimpleResults; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Net.Http; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using TransactionProcessor.Database.Contexts; +using TransactionProcessor.Database.Entities; +using Xunit; +using Xunit.Abstractions; +using Contract = EstateReportingAPI.DataTransferObjects.Contract; +using Estate = EstateReportingAPI.DataTransferObjects.Estate; +using EstateOperator = EstateReportingAPI.DataTransferObjects.EstateOperator; +using Merchant = EstateReportingAPI.DataTransferObjects.Merchant; +using MerchantContract = EstateReportingAPI.DataTransferObjects.MerchantContract; +using MerchantDevice = EstateReportingAPI.DataTransferObjects.MerchantDevice; +using MerchantKpi = EstateReportingAPI.DataTransferObjects.MerchantKpi; +using MerchantOperator = EstateReportingAPI.DataTransferObjects.MerchantOperator; +using Operator = EstateReportingAPI.DataTransferObjects.Operator; +using TodaysSales = EstateReportingAPI.DataTransferObjects.TodaysSales; + +namespace EstateReportingAPI.IntegrationTests { + public class CalendarEndpointTests : ControllerTestsBase { + private String BaseRoute = "api/calendars"; + + [Fact] + public async Task CalendarEndpoint_GetComparisonDates_DatesReturned() { + List datesInPreviousYear = helper.GetDatesForYear(DateTime.Now.Year - 1); + await helper.AddCalendarDates(datesInPreviousYear); + List datesInYear = helper.GetDatesForYear(DateTime.Now.Year); + await helper.AddCalendarDates(datesInYear); + + Result> result = await this.CreateAndSendHttpRequestMessage>($"{this.BaseRoute}/comparisondates", CancellationToken.None); + + result.IsSuccess.ShouldBeTrue(); + + List dates = result.Data; + List expectedDates = datesInYear.Where(d => d <= DateTime.Now.Date.AddDays(-1)).ToList(); + dates.ShouldNotBeNull(); + foreach (DateTime date in expectedDates) { + dates.Select(d => d.Date).Contains(date.Date).ShouldBeTrue(); + } + + dates.Select(d => d.Description).Contains("Yesterday"); + dates.Select(d => d.Description).Contains("Last Week"); + dates.Select(d => d.Description).Contains("Last Month"); + } + + protected override async Task ClearStandingData() { + + } + + protected override async Task SetupStandingData() { + + } + } + + public class ContractEndPointTests : ControllerTestsBase { + private String BaseRoute = "api/contracts"; + + [Fact] + public async Task ContractEndpoint_GetRecentContracts_ContractsReturned() { + await helper.AddEstate("Test Estate", "Ref1"); + + await this.helper.AddOperator("Test Estate", "Safaricom"); + await this.helper.AddOperator("Test Estate", "Voucher"); + await this.helper.AddOperator("Test Estate", "PataPawa PostPay"); + await this.helper.AddOperator("Test Estate", "PataPawa PrePay"); + + // 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) }; + 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) }; + 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) }; + 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) }; + await helper.AddContractWithProducts("Test Estate", "PataPawa PrePay Contract", "PataPawa PrePay", prePayProductList); + + Result> result = await this.CreateAndSendHttpRequestMessage>($"{this.BaseRoute}/recent", CancellationToken.None); + + result.IsSuccess.ShouldBeTrue(); + + List contracts = result.Data; + contracts.Count.ShouldBe(3); + contracts.SingleOrDefault(c => c.Description == "Safaricom Contract").ShouldNotBeNull(); + contracts.SingleOrDefault(c => c.Description == "PataPawa PostPay Contract").ShouldNotBeNull(); + contracts.SingleOrDefault(c => c.Description == "PataPawa PrePay Contract").ShouldNotBeNull(); + + } + + [Fact] + public async Task ContractEndpoint_GetContracts_ContractsReturned() { + await helper.AddEstate("Test Estate", "Ref1"); + + await this.helper.AddOperator("Test Estate", "Safaricom"); + await this.helper.AddOperator("Test Estate", "Voucher"); + await this.helper.AddOperator("Test Estate", "PataPawa PostPay"); + await this.helper.AddOperator("Test Estate", "PataPawa PrePay"); + + // 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) }; + 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) }; + 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) }; + 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) }; + await helper.AddContractWithProducts("Test Estate", "PataPawa PrePay Contract", "PataPawa PrePay", prePayProductList); + + Result> result = await this.CreateAndSendHttpRequestMessage>($"{this.BaseRoute}", CancellationToken.None); + + result.IsSuccess.ShouldBeTrue(); + + List contracts = result.Data; + contracts.Count.ShouldBe(4); + contracts.SingleOrDefault(c => c.Description == "Safaricom Contract").ShouldNotBeNull(); + contracts.SingleOrDefault(c => c.Description == "Healthcare Centre 1 Contract").ShouldNotBeNull(); + contracts.SingleOrDefault(c => c.Description == "PataPawa PostPay Contract").ShouldNotBeNull(); + contracts.SingleOrDefault(c => c.Description == "PataPawa PrePay Contract").ShouldNotBeNull(); + + } + + [Fact] + public async Task ContractEndpoint_GetContract_ContractReturned() { + await helper.AddEstate("Test Estate", "Ref1"); + + await this.helper.AddOperator("Test Estate", "Safaricom"); + await this.helper.AddOperator("Test Estate", "Voucher"); + await this.helper.AddOperator("Test Estate", "PataPawa PostPay"); + await this.helper.AddOperator("Test Estate", "PataPawa PrePay"); + + // 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) }; + 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) }; + 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) }; + 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) }; + var ppprepayContractId = await helper.AddContractWithProducts("Test Estate", "PataPawa PrePay Contract", "PataPawa PrePay", prePayProductList); + + Result result = await this.CreateAndSendHttpRequestMessage($"{this.BaseRoute}/{ppprepayContractId}", CancellationToken.None); + + result.IsSuccess.ShouldBeTrue(); + + Contract contract = result.Data; + contract.ShouldNotBeNull(); + contract.Description.ShouldBe("PataPawa PrePay Contract"); + + } + + protected override async Task ClearStandingData() { + + } + + protected override async Task SetupStandingData() { + + } + } + + public class EstateEndpointTests : ControllerTestsBase { + private String BaseRoute = "api/estates"; + + [Fact] + public async Task EstateEndpoint_GetEstates_EstateReturned() { + await helper.AddEstate("Test Estate", "Ref1"); + + Result result = await this.CreateAndSendHttpRequestMessage($"{this.BaseRoute}", CancellationToken.None); + + result.IsSuccess.ShouldBeTrue(); + + Estate estate = result.Data; + estate.ShouldNotBeNull(); + estate.EstateName.ShouldBe("Test Estate"); + estate.Reference.ShouldBe("Ref1"); + } + + [Fact] + public async Task EstateEndpoint_GetEstateOperator_EstateOperatorsReturned() { + await helper.AddEstate("Test Estate", "Ref1"); + await this.helper.AddOperator("Test Estate", "Safaricom"); + await this.helper.AddOperator("Test Estate", "Voucher"); + + await this.helper.AddEstateOperators("Test Estate", ["Safaricom", "Voucher"]); + + Result> result = await this.CreateAndSendHttpRequestMessage>($"{this.BaseRoute}/operators", CancellationToken.None); + + result.IsSuccess.ShouldBeTrue(); + + List estateOperators = result.Data; + estateOperators.Count.ShouldBe(2); + estateOperators.SingleOrDefault(e => e.Name == "Safaricom").ShouldNotBeNull(); + estateOperators.SingleOrDefault(e => e.Name == "Voucher").ShouldNotBeNull(); + } + + protected override async Task ClearStandingData() { + + } + + protected override async Task SetupStandingData() { + + } + } + + public class OperatorEndpointTests : ControllerTestsBase { + private String BaseRoute = "api/operators"; + + [Fact] + public async Task OperatorEndpoint_GetOperators_OperatorsReturned() { + await helper.AddEstate("Test Estate", "Ref1"); + await this.helper.AddOperator("Test Estate", "Safaricom"); + await this.helper.AddOperator("Test Estate", "Voucher"); + + Result> result = await this.CreateAndSendHttpRequestMessage>($"{this.BaseRoute}", CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + List operators = result.Data; + operators.Count.ShouldBe(2); + operators.SingleOrDefault(o => o.Name == "Safaricom").ShouldNotBeNull(); + operators.SingleOrDefault(o => o.Name == "Voucher").ShouldNotBeNull(); + } + + [Fact] + public async Task OperatorEndpoint_GetOperator_OperatorReturned() { + await helper.AddEstate("Test Estate", "Ref1"); + var operatorId = await this.helper.AddOperator("Test Estate", "Safaricom"); + Result result = await this.CreateAndSendHttpRequestMessage($"{this.BaseRoute}/{operatorId}", CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + Operator operatorData = result.Data; + operatorData.ShouldNotBeNull(); + operatorData.Name.ShouldBe("Safaricom"); + } + + protected override async Task ClearStandingData() { + + } + + protected override async Task SetupStandingData() { + + } + } + + public class MerchantEndpointTests : ControllerTestsBase { + private String BaseRoute = "api/merchants"; + + [Fact] + public async Task MerchantEndpoint_GetMerchants_MerchantsReturned() { + await helper.AddEstate("Test Estate", "Ref1"); + for (int i = 0; i < 10; i++) { + await helper.AddMerchant("Test Estate", $"Test Merchant {i}", DateTime.Now, DateTime.Now, + ("Address Line 1", $"Test Town {i}", $"TE57 {i}NG", $"Region {i}"), + ($"Contact {i}", @"{i}@2.com", $"{i}23456")); + } + + Result> result = await this.CreateAndSendHttpRequestMessage>($"{this.BaseRoute}", CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + var merchants = result.Data; + + merchants.ShouldNotBeNull(); + merchants.Count.ShouldBe(10); + + for (int i = 0; i < 10; i++) { + Merchant? expected = merchants.SingleOrDefault(m => m.Name == $"Test Merchant {i}"); + expected.ShouldNotBeNull(); + } + } + + [Fact] + public async Task MerchantEndpoint_GetMerchant_MerchantReturned() + { + await helper.AddEstate("Test Estate", "Ref1"); + var merchantId = await helper.AddMerchant("Test Estate", $"Test Merchant 1", DateTime.Now, DateTime.Now, + ("Address Line 1", $"Test Town", $"TE57 1NG", $"Region"), + ("Contact 1", "1@2.com", "123456")); + + Result result = await this.CreateAndSendHttpRequestMessage($"{this.BaseRoute}/{merchantId}", CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + var merchant = result.Data; + + merchant.ShouldNotBeNull(); + merchant.Name.ShouldBe("Test Merchant 1"); + } + + [Fact] + public async Task MerchantEndpoint_GetRecentMerchants_MerchantsReturned() + { + await helper.AddEstate("Test Estate", "Ref1"); + for (int i = 0; i < 10; i++) + { + await helper.AddMerchant("Test Estate", $"Test Merchant {i}", DateTime.Now.AddDays(i*-1), DateTime.Now, + ("Address Line 1", $"Test Town {i}", $"TE57 {i}NG", $"Region {i}"), + ($"Contact {i}", @"{i}@2.com", $"{i}23456")); + } + + Result> result = await this.CreateAndSendHttpRequestMessage>($"{this.BaseRoute}/recent", CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + var merchants = result.Data; + + merchants.ShouldNotBeNull(); + merchants.Count.ShouldBe(3); + merchants.SingleOrDefault(m => m.Name == "Test Merchant 0").ShouldNotBeNull(); + merchants.SingleOrDefault(m => m.Name == "Test Merchant 1").ShouldNotBeNull(); + merchants.SingleOrDefault(m => m.Name == "Test Merchant 2").ShouldNotBeNull(); + } + + + [Fact] + public async Task MerchantEndpoint_GetMerchantOperators_MerchantOperatorsReturned() + { + await helper.AddEstate("Test Estate", "Ref1"); + + await this.helper.AddOperator("Test Estate", "Safaricom"); + await this.helper.AddOperator("Test Estate", "Voucher"); + + var merchantId = await helper.AddMerchant("Test Estate", $"Test Merchant 1", DateTime.Now, DateTime.Now, + ("Address Line 1", $"Test Town", $"TE57 1NG", $"Region"), + ("Contact 1", "1@2.com", "123456"), operators: ["Safaricom", "Voucher"]); + + Result> result = await this.CreateAndSendHttpRequestMessage>($"{this.BaseRoute}/{merchantId}/operators", CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + var merchantOperators = result.Data; + + merchantOperators.ShouldNotBeNull(); + merchantOperators.Count.ShouldBe(2); + merchantOperators.SingleOrDefault(m => m.OperatorName == "Safaricom").ShouldNotBeNull(); + merchantOperators.SingleOrDefault(m => m.OperatorName == "Voucher").ShouldNotBeNull(); + } + + [Fact] + public async Task MerchantEndpoint_GetMerchantContracts_MerchantContractsReturned() + { + await helper.AddEstate("Test Estate", "Ref1"); + + await this.helper.AddOperator("Test Estate", "Safaricom"); + await this.helper.AddOperator("Test Estate", "Voucher"); + + 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) }; + await helper.AddContractWithProducts("Test Estate", "Healthcare Centre 1 Contract", "Voucher", voucherProductList); + + var merchantId = await helper.AddMerchant("Test Estate", $"Test Merchant 1", DateTime.Now, DateTime.Now, + ("Address Line 1", $"Test Town", $"TE57 1NG", $"Region"), + ("Contact 1", "1@2.com", "123456"), operators: ["Safaricom", "Voucher"], + ["Safaricom Contract", "Healthcare Centre 1 Contract"]); + + Result> result = await this.CreateAndSendHttpRequestMessage>($"{this.BaseRoute}/{merchantId}/contracts", CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + var merchantContracts = result.Data; + + merchantContracts.ShouldNotBeNull(); + merchantContracts.Count.ShouldBe(2); + merchantContracts.SingleOrDefault(m => m.ContractName == "Safaricom Contract").ShouldNotBeNull(); + merchantContracts.SingleOrDefault(m => m.ContractName == "Healthcare Centre 1 Contract").ShouldNotBeNull(); + } + + [Fact] + public async Task MerchantEndpoint_GetMerchantDevices_MerchantDevicesReturned() + { + await helper.AddEstate("Test Estate", "Ref1"); + + var merchantId = await helper.AddMerchant("Test Estate", $"Test Merchant 1", DateTime.Now, DateTime.Now, + ("Address Line 1", $"Test Town", $"TE57 1NG", $"Region"), + ("Contact 1", "1@2.com", "123456"), devices: ["123456"]); + + Result> result = await this.CreateAndSendHttpRequestMessage>($"{this.BaseRoute}/{merchantId}/devices", CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + var merchantDevices = result.Data; + + merchantDevices.ShouldNotBeNull(); + merchantDevices.Count.ShouldBe(1); + merchantDevices.SingleOrDefault(m => m.DeviceIdentifier == "123456").ShouldNotBeNull(); + } + + [Fact] + public async Task MerchantEndpoint_GetMerchantKpis_MerchantKpisReturned() + { + await helper.AddEstate("Test Estate", "Ref1"); + + await helper.AddMerchant("Test Estate", $"Test Merchant 1", DateTime.Now, DateTime.Now, + ("Address Line 1", $"Test Town", $"TE57 1NG", $"Region"), + ("Contact 1", "1@2.com", "123456"), devices: ["123456"]); + await helper.AddMerchant("Test Estate", $"Test Merchant 2", DateTime.Now, DateTime.Now.AddMinutes(-10), + ("Address Line 1", $"Test Town", $"TE57 1NG", $"Region"), + ("Contact 1", "1@2.com", "123456"), devices: ["123456"]); + await helper.AddMerchant("Test Estate", $"Test Merchant 3", DateTime.Now, DateTime.Now.AddHours(-2), + ("Address Line 1", $"Test Town", $"TE57 1NG", $"Region"), + ("Contact 1", "1@2.com", "123456"), devices: ["123456"]); + await helper.AddMerchant("Test Estate", $"Test Merchant 4", DateTime.Now, DateTime.Now.AddHours(-3), + ("Address Line 1", $"Test Town", $"TE57 1NG", $"Region"), + ("Contact 1", "1@2.com", "123456"), devices: ["123456"]); + await helper.AddMerchant("Test Estate", $"Test Merchant 5", DateTime.Now, DateTime.Now.AddDays(-2), + ("Address Line 1", $"Test Town", $"TE57 1NG", $"Region"), + ("Contact 1", "1@2.com", "123456"), devices: ["123456"]); + await helper.AddMerchant("Test Estate", $"Test Merchant 6", DateTime.Now, DateTime.Now.AddDays(-1), + ("Address Line 1", $"Test Town", $"TE57 1NG", $"Region"), + ("Contact 1", "1@2.com", "123456"), devices: ["123456"]); + await helper.AddMerchant("Test Estate", $"Test Merchant 7", DateTime.Now, DateTime.Now.AddDays(-3), + ("Address Line 1", $"Test Town", $"TE57 1NG", $"Region"), + ("Contact 1", "1@2.com", "123456"), devices: ["123456"]); + await helper.AddMerchant("Test Estate", $"Test Merchant 8", DateTime.Now, DateTime.Now.AddDays(-10), + ("Address Line 1", $"Test Town", $"TE57 1NG", $"Region"), + ("Contact 1", "1@2.com", "123456"), devices: ["123456"]); + + Result result = await this.CreateAndSendHttpRequestMessage($"{this.BaseRoute}/kpis", CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + var merchantKpis = result.Data; + + merchantKpis.MerchantsWithSaleInLastHour.ShouldBe(2); + merchantKpis.MerchantsWithNoSaleToday.ShouldBe(3); + merchantKpis.MerchantsWithNoSaleInLast7Days.ShouldBe(1); + } + + + protected override async Task ClearStandingData() { + + } + + protected override async Task SetupStandingData() { + + } + } + + public class TransactionsEndpointTests : ControllerTestsBase { + private String BaseRoute = "api/transactions"; + + public TransactionsEndpointTests(ITestOutputHelper testOutputHelper) { + this.TestOutputHelper = testOutputHelper; + } + + + protected override async Task ClearStandingData() { + throw new NotImplementedException(); + } + + 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 + await this.helper.AddOperator("Test Estate", "Safaricom"); + await this.helper.AddOperator("Test Estate", "Voucher"); + await this.helper.AddOperator("Test Estate", "PataPawa PostPay"); + 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, DateTime.MinValue, default, default); + await helper.AddMerchant("Test Estate", "Test Merchant 2", DateTime.MinValue, DateTime.MinValue, default, default); + await helper.AddMerchant("Test Estate", "Test Merchant 3", DateTime.MinValue, DateTime.MinValue, default, default); + await helper.AddMerchant("Test Estate", "Test Merchant 4", DateTime.MinValue, DateTime.MinValue, default, default); + sw.Stop(); + this.TestOutputHelper.WriteLine($"Setup Merchants {sw.ElapsedMilliseconds}ms"); + sw.Restart(); + + // 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) + }; + 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) + }; + 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) + }; + 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) + }; + 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"); + await helper.AddResponseCode(1001, "Unknown Estate"); + await helper.AddResponseCode(1002, "Unknown Merchant"); + await helper.AddResponseCode(1003, "No Devices Configured"); + + 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.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() + ); + + + sw.Stop(); + this.TestOutputHelper.WriteLine($"Data Caching {sw.ElapsedMilliseconds}ms"); + sw.Restart(); + } + + [Fact] + public async Task FactTransactionsControllerController_TodaysSales_SalesReturned() + { + List? todaysTransactions = new List(); + List comparisonDateTransactions = new List(); + + 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 } }; + + 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); + + // 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 this.helper.AddTransactionsX(comparisonDateTransactions); + + await helper.RunTodaysTransactionsSummaryProcessing(comparisonDate.Date); + await helper.RunHistoricTransactionsSummaryProcessing(comparisonDate.Date); + await helper.RunTodaysTransactionsSummaryProcessing(todaysDateTime.Date); + + Result result = await this.CreateAndSendHttpRequestMessage($"{this.BaseRoute}/todayssales?comparisonDate={comparisonDate:yyyy-MM-dd}", 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)); + } + + + [Fact] + public async Task FactTransactionsControllerController_TodaysSales_OperatorFilter_SalesReturned() + { + List? todaysTransactions = new List(); + List comparisonDateTransactions = new List(); + + 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 } }; + + 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); + + // 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 this.helper.AddTransactionsX(comparisonDateTransactions); + + await helper.RunTodaysTransactionsSummaryProcessing(comparisonDate.Date); + await helper.RunHistoricTransactionsSummaryProcessing(comparisonDate.Date); + await helper.RunTodaysTransactionsSummaryProcessing(todaysDateTime.Date); + + Result result = await this.CreateAndSendHttpRequestMessage($"{this.BaseRoute}/todayssales?comparisonDate={comparisonDate:yyyy-MM-dd}&operatorReportingId=1", CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + var todaysSales = result.Data; + + 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)); + + todaysSales.TodaysSalesCount.ShouldBe(todaysTransactions.Count(c => c.OperatorId == operatorId)); + todaysSales.ComparisonSalesValue.ShouldBe(comparisonDateTransactions.Where(c => c.OperatorId == operatorId).Sum(c => c.TransactionAmount)); + } + + + [Fact] + public async Task FactTransactionsControllerController_TodaysSales_MerchantFilter_SalesReturned() + { + List? todaysTransactions = new List(); + List comparisonDateTransactions = new List(); + + 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 } }; + + 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); + + // 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 this.helper.AddTransactionsX(comparisonDateTransactions); + + await helper.RunTodaysTransactionsSummaryProcessing(comparisonDate.Date); + await helper.RunHistoricTransactionsSummaryProcessing(comparisonDate.Date); + await helper.RunTodaysTransactionsSummaryProcessing(todaysDateTime.Date); + + Result result = await this.CreateAndSendHttpRequestMessage($"{this.BaseRoute}/todayssales?comparisonDate={comparisonDate:yyyy-MM-dd}&merchantReportingId=1", CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + var todaysSales = result.Data; + + 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)); + + todaysSales.TodaysSalesCount.ShouldBe(todaysTransactions.Count(c => c.MerchantId == merchantId)); + todaysSales.ComparisonSalesValue.ShouldBe(comparisonDateTransactions.Where(c => c.MerchantId == merchantId).Sum(c => c.TransactionAmount)); + } + + [Fact] + public async Task FactTransactionsControllerController_TodaysFailedSales_SalesReturned() + { + + //EstateManagementContext context = new EstateManagementContext(GetLocalConnectionString($"TransactionProcessorReadModel-{TestId.ToString()}")); + var todaysTransactions = new List(); + var comparisonDateTransactions = new List(); + //DatabaseHelper helper1 = new DatabaseHelper(context); + // TODO: make counts dynamic + DateTime todaysDateTime = DateTime.Now; + //todaysDateTime = todaysDateTime.AddHours(12).AddMinutes(30); + + Dictionary transactionCounts = new() { { "Test Merchant 1", 3 }, { "Test Merchant 2", 6 }, { "Test Merchant 3", 2 }, { "Test Merchant 4", 0 } }; + + DateTime comparisonDate = todaysDateTime.AddDays(-1); + + 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); + } + } + } + } + + await 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.AddHours(-1), merchant.MerchantId, contract.operatorId, contract.contractId, product.productId, "1009", product.productValue); + comparisonDateTransactions.Add(transaction); + } + } + } + } + + await helper.AddTransactionsX(comparisonDateTransactions); + + + await helper.RunTodaysTransactionsSummaryProcessing(comparisonDate.Date); + await helper.RunHistoricTransactionsSummaryProcessing(comparisonDate.Date); + await helper.RunTodaysTransactionsSummaryProcessing(todaysDateTime.Date); + + Result result = await this.CreateAndSendHttpRequestMessage($"{this.BaseRoute}/todaysfailedsales?comparisonDate={comparisonDate:yyyy-MM-dd}&responseCode=1009", 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)); + + todaysSales.TodaysSalesCount.ShouldBe(todaysTransactions.Count); + todaysSales.ComparisonSalesValue.ShouldBe(comparisonDateTransactions.Sum(c => c.TransactionAmount)); + } + } +} + diff --git a/EstateReportingAPI.IntegrationTests/ControllerTestsBase.cs b/EstateReportingAPI.IntegrationTests/ControllerTestsBase.cs index a1d14f4..26a35fd 100644 --- a/EstateReportingAPI.IntegrationTests/ControllerTestsBase.cs +++ b/EstateReportingAPI.IntegrationTests/ControllerTestsBase.cs @@ -4,6 +4,7 @@ using System.Net.Http; using System.Threading; using System.Threading.Tasks; +using Newtonsoft.Json; using Shared.IntegrationTesting.TestContainers; using SimpleResults; using TransactionProcessor.Database.Contexts; @@ -13,11 +14,6 @@ namespace EstateReportingAPI.IntegrationTests; using System.Net.Http.Headers; using System.Text; -using Azure; -using Client; -using Common; -using Ductus.FluentDocker.Services; -using Ductus.FluentDocker.Services.Extensions; using NLog; using Shared.IntegrationTesting; using Shared.Logger; @@ -50,11 +46,10 @@ public virtual async Task InitializeAsync() this.factory = new CustomWebApplicationFactory(dbConnString); this.Client = this.factory.CreateClient(); - this.ApiClient = new EstateReportingApiClient((s) => "http://localhost", this.Client); - + this.context = new EstateManagementContext(dbConnString); - this.helper = new DatabaseHelper(context); + this.helper = new DatabaseHelper(context, this.TestId); await this.helper.CreateStoredProcedures(CancellationToken.None); await this.SetupStandingData(); } @@ -72,8 +67,6 @@ public virtual async Task DisposeAsync() protected HttpClient Client; protected CustomWebApplicationFactory factory; - protected EstateReportingApiClient ApiClient; - protected Guid TestId; internal async Task> CreateAndSendHttpRequestMessage(String url, CancellationToken cancellationToken) @@ -87,7 +80,7 @@ public virtual async Task DisposeAsync() String content = await result.Content.ReadAsStringAsync(cancellationToken); content.ShouldNotBeNull(); - return null; + return Result.Success(JsonConvert.DeserializeObject(content)); } internal async Task> CreateAndSendHttpRequestMessage(String url, String payload, CancellationToken cancellationToken) @@ -104,7 +97,7 @@ public virtual async Task DisposeAsync() String content = await result.Content.ReadAsStringAsync(cancellationToken); content.ShouldNotBeNull(); - return null; + return Result.Success(JsonConvert.DeserializeObject(content)); } //public static IContainer DatabaseServerContainer; diff --git a/EstateReportingAPI.IntegrationTests/DatabaseHelper.cs b/EstateReportingAPI.IntegrationTests/DatabaseHelper.cs index 1d8f199..2678bff 100644 --- a/EstateReportingAPI.IntegrationTests/DatabaseHelper.cs +++ b/EstateReportingAPI.IntegrationTests/DatabaseHelper.cs @@ -19,9 +19,11 @@ namespace EstateReportingAPI.IntegrationTests; public class DatabaseHelper{ private readonly EstateManagementContext Context; + private readonly Guid EstateId; - public DatabaseHelper(EstateManagementContext context){ + public DatabaseHelper(EstateManagementContext context, Guid estateId) { this.Context = context; + this.EstateId = estateId; } public async Task DeleteAllMerchants(){ @@ -272,7 +274,7 @@ public async Task AddTransactionsX(List transactions) { public async Task AddEstate(String estateName, String reference){ Estate estate = new(){ CreatedDateTime = DateTime.Now, - EstateId = Guid.NewGuid(), + EstateId = this.EstateId, Name = estateName, Reference = reference }; @@ -282,6 +284,33 @@ public async Task AddEstate(String estateName, String reference){ return estate.EstateReportingId; } + public async Task AddEstateOperators(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) { + var @operator = await this.Context.Operators.SingleOrDefaultAsync(o => o.Name == operatorName); + if (@operator == null){ + throw new Exception($"No operator found with name {operatorName}"); + } + + EstateOperator estateOperator = new EstateOperator() + { + EstateId = estate.EstateId, + OperatorId = @operator.OperatorId + }; + + await this.Context.EstateOperators.AddAsync(estateOperator); + } + + await this.Context.SaveChangesAsync(CancellationToken.None); + } + public async Task AddOperators(String estateName, List operators) { Estate? estate = await this.Context.Estates.SingleOrDefaultAsync(e => e.Name == estateName); @@ -306,7 +335,7 @@ public async Task AddOperators(String estateName, List operators) await this.Context.SaveChangesAsync(CancellationToken.None); } - public async Task AddOperator(String estateName, String operatorName) + public async Task AddOperator(String estateName, String operatorName) { Estate? estate = await this.Context.Estates.SingleOrDefaultAsync(e => e.Name == estateName); @@ -327,48 +356,100 @@ public async Task AddOperator(String estateName, String operatorName) await this.Context.Operators.AddAsync(@operator); await this.Context.SaveChangesAsync(CancellationToken.None); - return @operator.OperatorReportingId; + return @operator.OperatorId; } - - 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 createDateTime, + DateTime lastSaleDateTime, + (String addressLine1, String town, String postCode, String region) address, + (String contactName, String contactEmail, String contactPhone) contact, + List operators = null, + List contracts = null, + List devices = null) { Estate? estate = await this.Context.Estates.SingleOrDefaultAsync(e => e.Name == estateName); - if (estate == null){ + if (estate == null) { throw new Exception($"No estate found with name {estateName}"); } - Merchant merchant = new Merchant{ - EstateId = estate.EstateId, - MerchantId = Guid.NewGuid(), - Name = merchantName, - LastSaleDate = lastSaleDateTime.Date, - LastSaleDateTime = lastSaleDateTime - }; + Merchant merchant = new Merchant { + EstateId = estate.EstateId, + MerchantId = Guid.NewGuid(), + Name = merchantName, + LastSaleDate = lastSaleDateTime.Date, + LastSaleDateTime = lastSaleDateTime + }; await this.Context.Merchants.AddAsync(merchant); await this.Context.SaveChangesAsync(CancellationToken.None); - if (addressList != null){ - var savedMerchant = await this.Context.Merchants.SingleOrDefaultAsync(m => m.MerchantId == merchant.MerchantId); + var savedMerchant = await this.Context.Merchants.SingleOrDefaultAsync(m => m.MerchantId == merchant.MerchantId); + + if (savedMerchant == null) { + throw new Exception($"Error saving merchant {merchant.Name}"); + } + + if (address != default) { + + await this.Context.MerchantAddresses.AddAsync(new MerchantAddress { + AddressId = Guid.NewGuid(), + AddressLine1 = address.addressLine1, + Region = address.region, + Town = address.town, + PostalCode = address.postCode, + MerchantId = savedMerchant.MerchantId + }); + } + + if (contact != default) { + await this.Context.MerchantContacts.AddAsync(new MerchantContact { + MerchantId = savedMerchant.MerchantId, + ContactId = Guid.NewGuid(), + EmailAddress = contact.contactEmail, + Name = contact.contactName, + PhoneNumber = contact.contactPhone + }); + } - if (savedMerchant == null){ - throw new Exception($"Error saving merchant {merchant.Name}"); + if (operators != null && operators.Any()) { + foreach (String @operator in operators) { + var @op = await this.Context.Operators.SingleOrDefaultAsync(o => o.Name == @operator); + if (@op == null) { + throw new Exception($"No operator found with name {@operator}"); + } + await this.Context.MerchantOperators.AddAsync(new MerchantOperator { + MerchantId = savedMerchant.MerchantId, + OperatorId = @op.OperatorId, + Name = @op.Name, + IsDeleted = false + }); + } + } + + if (contracts != null && contracts.Any()) { + foreach (String contract in contracts) { + var cr = await this.Context.Contracts.SingleOrDefaultAsync(c => c.Description == contract); + if (cr == null) { + throw new Exception($"No contract found with name {contract}"); + } + await this.Context.MerchantContracts.AddAsync(new MerchantContract { + MerchantId = savedMerchant.MerchantId, + ContractId = cr.ContractId + }); } + } - foreach ((String addressLine1, String town, String postCode, String region) address in addressList){ - await this.Context.MerchantAddresses.AddAsync(new MerchantAddress{ - AddressId = Guid.NewGuid(), - AddressLine1 = address.addressLine1, - Region = address.region, - Town = address.town, - PostalCode = address.postCode, - MerchantId = savedMerchant.MerchantId - }); + if (devices != null && devices.Any()) { + foreach (String device in devices) { + await this.Context.MerchantDevices.AddAsync(new MerchantDevice { CreatedDateTime = DateTime.Now, MerchantId = savedMerchant.MerchantId, DeviceIdentifier = device, DeviceId = Guid.NewGuid() }); } } - return merchant.MerchantReportingId; + await this.Context.SaveChangesAsync(CancellationToken.None); + + return merchant.MerchantId; } public async Task AddMerchants(String estateName, @@ -407,7 +488,7 @@ await this.Context.MerchantAddresses.AddAsync(new MerchantAddress { } } - public async Task AddContract(String estateName, String contractName, String operatorName){ + public async Task AddContract(String estateName, String contractName, String operatorName){ Estate? estate = await this.Context.Estates.SingleOrDefaultAsync(e => e.Name == estateName); if (estate == null){ @@ -431,15 +512,18 @@ public async Task AddContract(String estateName, String contractName, Str await this.Context.Contracts.AddAsync(contract); await this.Context.SaveChangesAsync(CancellationToken.None); - return contract.ContractReportingId; + return contract.ContractId; } - public async Task AddContractWithProducts(String estateName, String contractName, String operatorName, List<(String productName, Int32 productType, Decimal? value)> products){ - await this.AddContract(estateName, contractName, operatorName); + public async Task AddContractWithProducts(String estateName, String contractName, String operatorName, List<(String productName, Int32 productType, Decimal? value)> products) + { + var contractId = await this.AddContract(estateName, contractName, operatorName); foreach ((String productName, Int32 productType, Decimal? value) product in products){ await this.AddContractProduct(contractName, product.productName, product.productType, product.value); } + + return contractId; } public async Task AddContractProduct(String contractName, String productName, Int32 productType, Decimal? value){ diff --git a/EstateReportingAPI.IntegrationTests/DimensionControllerTests.cs b/EstateReportingAPI.IntegrationTests/DimensionControllerTests.cs index 3168b65..90492c6 100644 --- a/EstateReportingAPI.IntegrationTests/DimensionControllerTests.cs +++ b/EstateReportingAPI.IntegrationTests/DimensionControllerTests.cs @@ -11,11 +11,11 @@ namespace EstateReportingAPI.IntegrationTests; using EstateReportingAPI.DataTrasferObjects; using Shouldly; using Xunit; -using Merchant = DataTrasferObjects.Merchant; -using Operator = DataTrasferObjects.Operator; +using Merchant = DataTransferObjects.Merchant; +using Operator = DataTransferObjects.Operator; using ResponseCode = DataTrasferObjects.ResponseCode; -public class DimensionsControllerTests : ControllerTestsBase +/*public class DimensionsControllerTests : ControllerTestsBase { [Fact] public async Task DimensionsController_GetCalendarYears_NoDataInDatabase() @@ -284,10 +284,4 @@ public void Dispose() //Console.WriteLine($"Delete result is {result}"); //result.ShouldBeTrue(); } -} - -public enum ClientType -{ - Api, - Direct -} +}*/ \ No newline at end of file diff --git a/EstateReportingAPI.IntegrationTests/EstateReportingAPI.IntegrationTests.csproj b/EstateReportingAPI.IntegrationTests/EstateReportingAPI.IntegrationTests.csproj index a64ebec..fb50b6e 100644 --- a/EstateReportingAPI.IntegrationTests/EstateReportingAPI.IntegrationTests.csproj +++ b/EstateReportingAPI.IntegrationTests/EstateReportingAPI.IntegrationTests.csproj @@ -8,7 +8,7 @@ - + diff --git a/EstateReportingAPI.IntegrationTests/FactSettlementsControllerTests.cs b/EstateReportingAPI.IntegrationTests/FactSettlementsControllerTests.cs index cb0de31..4bbb84c 100644 --- a/EstateReportingAPI.IntegrationTests/FactSettlementsControllerTests.cs +++ b/EstateReportingAPI.IntegrationTests/FactSettlementsControllerTests.cs @@ -1,4 +1,4 @@ -using System; +/*using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; @@ -658,3 +658,4 @@ public async Task FactSettlementsController_UnsettledFees_ByProduct_ProductFilte } } +*/ \ No newline at end of file diff --git a/EstateReportingAPI.IntegrationTests/FactTransactionsControllerTests.cs b/EstateReportingAPI.IntegrationTests/FactTransactionsControllerTests.cs index e6cd1cd..973be5a 100644 --- a/EstateReportingAPI.IntegrationTests/FactTransactionsControllerTests.cs +++ b/EstateReportingAPI.IntegrationTests/FactTransactionsControllerTests.cs @@ -20,9 +20,10 @@ namespace EstateReportingAPI.IntegrationTests; using Newtonsoft.Json; using Shouldly; using Xunit; -using Merchant = DataTrasferObjects.Merchant; +using Merchant = DataTransferObjects.Merchant; using SortDirection = DataTransferObjects.SortDirection; +/* public class FactTransactionsControllerTestsBase : ControllerTestsBase { protected override async Task ClearStandingData() @@ -1869,4 +1870,4 @@ public async Task FactTransactionsController_TransactionSearch_SortingTest_Trans searchResult[1].TransactionAmount.ShouldBe(200); searchResult[2].TransactionAmount.ShouldBe(100); } - } \ No newline at end of file + }*/ \ No newline at end of file diff --git a/EstateReportingAPI.Models/Dimension.cs b/EstateReportingAPI.Models/Archive/Dimension.cs similarity index 100% rename from EstateReportingAPI.Models/Dimension.cs rename to EstateReportingAPI.Models/Archive/Dimension.cs diff --git a/EstateReportingAPI.Models/LastSettlement.cs b/EstateReportingAPI.Models/Archive/LastSettlement.cs similarity index 100% rename from EstateReportingAPI.Models/LastSettlement.cs rename to EstateReportingAPI.Models/Archive/LastSettlement.cs diff --git a/EstateReportingAPI.Models/ResponseCode.cs b/EstateReportingAPI.Models/Archive/ResponseCode.cs similarity index 100% rename from EstateReportingAPI.Models/ResponseCode.cs rename to EstateReportingAPI.Models/Archive/ResponseCode.cs diff --git a/EstateReportingAPI.Models/TodaysSalesCountByHour.cs b/EstateReportingAPI.Models/Archive/TodaysSalesCountByHour.cs similarity index 100% rename from EstateReportingAPI.Models/TodaysSalesCountByHour.cs rename to EstateReportingAPI.Models/Archive/TodaysSalesCountByHour.cs diff --git a/EstateReportingAPI.Models/TodaysSalesValueByHour.cs b/EstateReportingAPI.Models/Archive/TodaysSalesValueByHour.cs similarity index 100% rename from EstateReportingAPI.Models/TodaysSalesValueByHour.cs rename to EstateReportingAPI.Models/Archive/TodaysSalesValueByHour.cs diff --git a/EstateReportingAPI.Models/TodaysSettlement.cs b/EstateReportingAPI.Models/Archive/TodaysSettlement.cs similarity index 100% rename from EstateReportingAPI.Models/TodaysSettlement.cs rename to EstateReportingAPI.Models/Archive/TodaysSettlement.cs diff --git a/EstateReportingAPI.Models/TopBottom.cs b/EstateReportingAPI.Models/Archive/TopBottom.cs similarity index 100% rename from EstateReportingAPI.Models/TopBottom.cs rename to EstateReportingAPI.Models/Archive/TopBottom.cs diff --git a/EstateReportingAPI.Models/TopBottomData.cs b/EstateReportingAPI.Models/Archive/TopBottomData.cs similarity index 100% rename from EstateReportingAPI.Models/TopBottomData.cs rename to EstateReportingAPI.Models/Archive/TopBottomData.cs diff --git a/EstateReportingAPI.Models/TransactionSearchRequest.cs b/EstateReportingAPI.Models/Archive/TransactionSearchRequest.cs similarity index 100% rename from EstateReportingAPI.Models/TransactionSearchRequest.cs rename to EstateReportingAPI.Models/Archive/TransactionSearchRequest.cs diff --git a/EstateReportingAPI.Models/UnsettledFee.cs b/EstateReportingAPI.Models/Archive/UnsettledFee.cs similarity index 100% rename from EstateReportingAPI.Models/UnsettledFee.cs rename to EstateReportingAPI.Models/Archive/UnsettledFee.cs diff --git a/EstateReportingAPI.Models/ValueRange.cs b/EstateReportingAPI.Models/Archive/ValueRange.cs similarity index 100% rename from EstateReportingAPI.Models/ValueRange.cs rename to EstateReportingAPI.Models/Archive/ValueRange.cs diff --git a/EstateReportingAPI.Models/Contract.cs b/EstateReportingAPI.Models/Contract.cs new file mode 100644 index 0000000..76e3fde --- /dev/null +++ b/EstateReportingAPI.Models/Contract.cs @@ -0,0 +1,17 @@ +namespace EstateReportingAPI.Models; + +public class Contract +{ + #region Properties + public Guid EstateId { get; set; } + public Int32 EstateReportingId { get; set; } + public Guid ContractId { get; set; } + public Int32 ContractReportingId { get; set; } + public String Description { get; set; } + public String OperatorName { get; set; } + public Guid OperatorId { get; set; } + public Int32 OperatorReportingId { get; set; } + public List Products { get; set; } + + #endregion +} \ No newline at end of file diff --git a/EstateReportingAPI.Models/ContractProduct.cs b/EstateReportingAPI.Models/ContractProduct.cs new file mode 100644 index 0000000..3e35e4b --- /dev/null +++ b/EstateReportingAPI.Models/ContractProduct.cs @@ -0,0 +1,11 @@ +namespace EstateReportingAPI.Models; + +public class ContractProduct +{ public Guid ContractId { get; set; } + public Guid ProductId { get; set; } + public String ProductName { get; set; } + public String DisplayText { get; set; } + public Int32 ProductType { get; set; } + public Decimal? Value { get; set; } + public List TransactionFees { get; set; } +} \ No newline at end of file diff --git a/EstateReportingAPI.Models/ContractProductTransactionFee.cs b/EstateReportingAPI.Models/ContractProductTransactionFee.cs new file mode 100644 index 0000000..35f18ab --- /dev/null +++ b/EstateReportingAPI.Models/ContractProductTransactionFee.cs @@ -0,0 +1,10 @@ +namespace EstateReportingAPI.Models; + +public class ContractProductTransactionFee +{ + public Guid TransactionFeeId { get; set; } + public string? Description { get; set; } + public Int32 CalculationType { get; set; } + public Int32 FeeType { get; set; } + public Decimal Value { get; set; } +} \ No newline at end of file diff --git a/EstateReportingAPI.Models/Estate.cs b/EstateReportingAPI.Models/Estate.cs new file mode 100644 index 0000000..1cc8367 --- /dev/null +++ b/EstateReportingAPI.Models/Estate.cs @@ -0,0 +1,12 @@ +namespace EstateReportingAPI.Models; + +public class Estate +{ + public Guid EstateId { get; set; } + public string? EstateName { get; set; } + public string? Reference { get; set; } + public List? Operators { get; set; } + public List? Merchants { get; set; } + public List? Contracts { get; set; } + public List? Users { get; set; } +} \ No newline at end of file diff --git a/EstateReportingAPI.Models/EstateContract.cs b/EstateReportingAPI.Models/EstateContract.cs new file mode 100644 index 0000000..e51a7c0 --- /dev/null +++ b/EstateReportingAPI.Models/EstateContract.cs @@ -0,0 +1,7 @@ +namespace EstateReportingAPI.Models; + +public class EstateContract +{ + public Guid ContractId { get; set; } + public string? Name { get; set; } +} \ No newline at end of file diff --git a/EstateReportingAPI.Models/EstateMerchant.cs b/EstateReportingAPI.Models/EstateMerchant.cs new file mode 100644 index 0000000..fb30629 --- /dev/null +++ b/EstateReportingAPI.Models/EstateMerchant.cs @@ -0,0 +1,8 @@ +namespace EstateReportingAPI.Models; + +public class EstateMerchant +{ + public Guid MerchantId { get; set; } + public string? Name { get; set; } + public string? Reference { get; set; } +} \ No newline at end of file diff --git a/EstateReportingAPI.Models/EstateOperator.cs b/EstateReportingAPI.Models/EstateOperator.cs new file mode 100644 index 0000000..1ad7971 --- /dev/null +++ b/EstateReportingAPI.Models/EstateOperator.cs @@ -0,0 +1,7 @@ +namespace EstateReportingAPI.Models; + +public class EstateOperator +{ + public Guid OperatorId { get; set; } + public string? Name { get; set; } +} \ No newline at end of file diff --git a/EstateReportingAPI.Models/EstateUser.cs b/EstateReportingAPI.Models/EstateUser.cs new file mode 100644 index 0000000..f161cd4 --- /dev/null +++ b/EstateReportingAPI.Models/EstateUser.cs @@ -0,0 +1,8 @@ +namespace EstateReportingAPI.Models; + +public class EstateUser +{ + public Guid UserId { get; set; } + public string? EmailAddress { get; set; } + public DateTime CreatedDateTime { get; set; } +} \ No newline at end of file diff --git a/EstateReportingAPI.Models/Merchant.cs b/EstateReportingAPI.Models/Merchant.cs index 4993a64..9aec326 100644 --- a/EstateReportingAPI.Models/Merchant.cs +++ b/EstateReportingAPI.Models/Merchant.cs @@ -3,19 +3,26 @@ public class Merchant { #region Properties - - public DateTime CreatedDateTime { get; set; } - public Int32 EstateReportingId { get; set; } - public DateTime LastSale { get; set; } - public DateTime LastSaleDateTime { get; set; } - public DateTime LastStatement { get; set; } public Guid MerchantId { get; set; } public Int32 MerchantReportingId { get; set; } public String Name { get; set; } - public String PostCode { get; set; } public String Reference { get; set; } - public String Region { get; set; } + public Decimal Balance { get; set; } + public Int32 SettlementSchedule { get; set; } + public DateTime CreatedDateTime { get; set; } + + public Guid AddressId { get; set; } + public String AddressLine1 { get; set; } + public String AddressLine2 { get; set; } public String Town { get; set; } + public String Region { get; set; } + public String PostCode { get; set; } + public String Country { get; set; } + + public Guid ContactId { get; set; } + public String ContactName { get; set; } + public String ContactEmail { get; set; } + public String ContactPhone { get; set; } #endregion } \ No newline at end of file diff --git a/EstateReportingAPI.Models/MerchantContract.cs b/EstateReportingAPI.Models/MerchantContract.cs new file mode 100644 index 0000000..855c14a --- /dev/null +++ b/EstateReportingAPI.Models/MerchantContract.cs @@ -0,0 +1,11 @@ +namespace EstateReportingAPI.Models; + +public class MerchantContract +{ + public Guid MerchantId { get; set; } + public Guid ContractId { get; set; } + public String ContractName { get; set; } + public String OperatorName { get; set; } + public Boolean IsDeleted { get; set; } + public List ContractProducts { get; set; } +} \ No newline at end of file diff --git a/EstateReportingAPI.Models/MerchantContractProduct.cs b/EstateReportingAPI.Models/MerchantContractProduct.cs new file mode 100644 index 0000000..be24947 --- /dev/null +++ b/EstateReportingAPI.Models/MerchantContractProduct.cs @@ -0,0 +1,12 @@ +namespace EstateReportingAPI.Models; + +public class MerchantContractProduct +{ + public Guid MerchantId { get; set; } + public Guid ContractId { get; set; } + public Guid ProductId { get; set; } + public String ProductName { get; set; } + public String DisplayText { get; set; } + public Int32 ProductType { get; set; } + public Decimal? Value { get; set; } +} \ No newline at end of file diff --git a/EstateReportingAPI.Models/MerchantDevice.cs b/EstateReportingAPI.Models/MerchantDevice.cs new file mode 100644 index 0000000..aa80b4e --- /dev/null +++ b/EstateReportingAPI.Models/MerchantDevice.cs @@ -0,0 +1,9 @@ +namespace EstateReportingAPI.Models; + +public class MerchantDevice +{ + public Guid MerchantId { get; set; } + public Guid DeviceId { get; set; } + public String DeviceIdentifier { get; set; } + public Boolean IsDeleted { get; set; } +} \ No newline at end of file diff --git a/EstateReportingAPI.Models/MerchantOperator.cs b/EstateReportingAPI.Models/MerchantOperator.cs new file mode 100644 index 0000000..5e7da5b --- /dev/null +++ b/EstateReportingAPI.Models/MerchantOperator.cs @@ -0,0 +1,10 @@ +namespace EstateReportingAPI.Models; + +public class MerchantOperator { + public Guid MerchantId { get; set; } + public Guid OperatorId { get; set; } + public String OperatorName { get; set; } + public String MerchantNumber { get; set; } + public String TerminalNumber { get; set; } + public Boolean IsDeleted { get; set; } +} \ No newline at end of file diff --git a/EstateReportingAPI.Models/Operator.cs b/EstateReportingAPI.Models/Operator.cs index 2fa4bfc..77e7571 100644 --- a/EstateReportingAPI.Models/Operator.cs +++ b/EstateReportingAPI.Models/Operator.cs @@ -6,4 +6,6 @@ public class Operator public Guid OperatorId { get; set; } public String Name { get; set; } public Int32 OperatorReportingId { get; set; } + public Boolean RequireCustomMerchantNumber { get; set; } + public Boolean RequireCustomTerminalNumber { get; set; } } \ No newline at end of file diff --git a/EstateReportingAPI.Tests/EstateReportingAPI.Tests.csproj b/EstateReportingAPI.Tests/EstateReportingAPI.Tests.csproj index 98282cf..d05d8ec 100644 --- a/EstateReportingAPI.Tests/EstateReportingAPI.Tests.csproj +++ b/EstateReportingAPI.Tests/EstateReportingAPI.Tests.csproj @@ -20,7 +20,7 @@ - + all diff --git a/EstateReportingAPI.Tests/EstateReportingApiClientTests.cs b/EstateReportingAPI.Tests/EstateReportingApiClientTests.cs deleted file mode 100644 index a90b8f5..0000000 --- a/EstateReportingAPI.Tests/EstateReportingApiClientTests.cs +++ /dev/null @@ -1,585 +0,0 @@ -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 = 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() - { - 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 da1c3e2..31b702c 100644 --- a/EstateReportingAPI.Tests/General/BootstrapperTests.cs +++ b/EstateReportingAPI.Tests/General/BootstrapperTests.cs @@ -3,9 +3,6 @@ using System; using System.Collections.Generic; using System.Diagnostics; - using System.Net.Http; - using System.Threading; - using Client; using EstateReportingAPI; using Lamar; using Microsoft.AspNetCore.Hosting; diff --git a/EstateReportingAPI.Tests/General/QueryStringBuilderTests.cs b/EstateReportingAPI.Tests/General/QueryStringBuilderTests.cs index cc31276..d2e6c45 100644 --- a/EstateReportingAPI.Tests/General/QueryStringBuilderTests.cs +++ b/EstateReportingAPI.Tests/General/QueryStringBuilderTests.cs @@ -1,4 +1,5 @@ using System; +using Shared.Web; using Shouldly; using Xunit; diff --git a/EstateReportingAPI/Bootstrapper/MediatorRegistry.cs b/EstateReportingAPI/Bootstrapper/MediatorRegistry.cs index a12e0a0..6eaffd7 100644 --- a/EstateReportingAPI/Bootstrapper/MediatorRegistry.cs +++ b/EstateReportingAPI/Bootstrapper/MediatorRegistry.cs @@ -13,36 +13,29 @@ public class MediatorRegistry : ServiceRegistry { public MediatorRegistry() { this.AddTransient(); + this.AddSingleton>, EstateRequestHandler >(); + this.AddSingleton>>, EstateRequestHandler>(); + + this.AddSingleton>>, OperatorRequestHandler>(); + this.AddSingleton>, OperatorRequestHandler>(); + this.AddSingleton>, TransactionRequestHandler>(); this.AddSingleton>, TransactionRequestHandler>(); - this.AddSingleton>>, TransactionRequestHandler>(); - this.AddSingleton>>, TransactionRequestHandler>(); - this.AddSingleton>>, TransactionRequestHandler>(); - + this.AddSingleton>>, CalendarRequestHandler>(); this.AddSingleton>>, CalendarRequestHandler>(); this.AddSingleton>>, CalendarRequestHandler>(); - this.AddSingleton>>, MerchantRequestHandler>(); - this.AddSingleton>>, MerchantRequestHandler>(); - this.AddSingleton>>, MerchantRequestHandler>(); - this.AddSingleton>>, MerchantRequestHandler>(); - this.AddSingleton>, MerchantRequestHandler>(); + this.AddSingleton>>, MerchantRequestHandler>(); this.AddSingleton>, MerchantRequestHandler>(); + this.AddSingleton>>, MerchantRequestHandler>(); + this.AddSingleton>, MerchantRequestHandler>(); + this.AddSingleton>>, MerchantRequestHandler>(); + this.AddSingleton>>, MerchantRequestHandler>(); + this.AddSingleton>>, MerchantRequestHandler>(); - this.AddSingleton>>, OperatorRequestHandler>(); - this.AddSingleton>, OperatorRequestHandler>(); - this.AddSingleton>>, OperatorRequestHandler>(); - this.AddSingleton>>, OperatorRequestHandler>(); - - this.AddSingleton>>, ResponseCodeRequestHandler>(); - - this.AddSingleton>, SettlementRequestHandler>(); - this.AddSingleton>, SettlementRequestHandler>(); - this.AddSingleton>>, SettlementRequestHandler>(); - - this.AddSingleton>, ProductRequestHandler>(); - this.AddSingleton>>, ProductRequestHandler>(); - this.AddSingleton>>, ProductRequestHandler>(); + this.AddSingleton>>, ContractRequestHandler>(); + this.AddSingleton>>, ContractRequestHandler>(); + this.AddSingleton>, ContractRequestHandler>(); } -} \ No newline at end of file +} diff --git a/EstateReportingAPI/Endpoints/CalendarEndpoints.cs b/EstateReportingAPI/Endpoints/CalendarEndpoints.cs new file mode 100644 index 0000000..3e3fd4f --- /dev/null +++ b/EstateReportingAPI/Endpoints/CalendarEndpoints.cs @@ -0,0 +1,26 @@ +using EstateReportingAPI.DataTrasferObjects; +using EstateReportingAPI.Handlers; +using Shared.Extensions; +using Shared.General; + +namespace EstateReportingAPI.Endpoints; + +public static class CalendarEndpoints +{ + private const string BaseRoute = "api/calendars"; + + public static void MapCalendarEndpoints(this IEndpointRouteBuilder app) { + + + RouteGroupBuilder group = app.MapGroup(BaseRoute) + .WithTags("Calendars"); + + Boolean disableAuthorisation = ConfigurationReader.GetValueOrDefault("AppSettings", "DisableAuthorisation", false); + if (disableAuthorisation == false) { + group = group.RequireAuthorization(); + } + + group.MapGet("/comparisondates", CalenderHandler.GetCalendarComparisonDates) + .WithStandardProduces>(); + } +} \ No newline at end of file diff --git a/EstateReportingAPI/Endpoints/ContractEndpoints.cs b/EstateReportingAPI/Endpoints/ContractEndpoints.cs new file mode 100644 index 0000000..26210f8 --- /dev/null +++ b/EstateReportingAPI/Endpoints/ContractEndpoints.cs @@ -0,0 +1,29 @@ +using EstateReportingAPI.DataTransferObjects; +using EstateReportingAPI.Handlers; +using Shared.Extensions; +using Shared.General; + +namespace EstateReportingAPI.Endpoints; + +public static class ContractEndpoints +{ + private const string BaseRoute = "api/contracts"; + + public static void MapContractEndpoints(this IEndpointRouteBuilder app) + { + RouteGroupBuilder group = app.MapGroup(BaseRoute) + .WithTags("Contracts"); + + Boolean disableAuthorisation = ConfigurationReader.GetValueOrDefault("AppSettings", "DisableAuthorisation", false); + if (disableAuthorisation == false) { + group = group.RequireAuthorization(); + } + + group.MapGet("/recent", ContractHandler.GetRecentContracts) + .WithStandardProduces>(); + group.MapGet("/", ContractHandler.GetContracts) + .WithStandardProduces>(); + group.MapGet("/{contractId}", ContractHandler.GetContract) + .WithStandardProduces>(); + } +} \ No newline at end of file diff --git a/EstateReportingAPI/Endpoints/DimensionsEndpoints.cs b/EstateReportingAPI/Endpoints/DimensionsEndpoints.cs deleted file mode 100644 index 9e09b99..0000000 --- a/EstateReportingAPI/Endpoints/DimensionsEndpoints.cs +++ /dev/null @@ -1,28 +0,0 @@ -using EstateReportingAPI.DataTrasferObjects; -using EstateReportingAPI.Handlers; -using Shared.Extensions; - -namespace EstateReportingAPI.Endpoints; - -public static class DimensionsEndpoints -{ - private const string BaseRoute = "api/dimensions"; - - public static void MapDimensionsEndpoints(this IEndpointRouteBuilder app) - { - var group = app.MapGroup(BaseRoute) - .RequireAuthorization() - .WithTags("Dimensions"); - - group.MapGet("calendar/years", DimensionsHandlers.GetCalendarYears) - .WithStandardProduces>(); - - group.MapGet("calendar/{year}/dates", DimensionsHandlers.GetCalendarDates) - .WithStandardProduces>(); - group.MapGet("calendar/comparisondates", DimensionsHandlers.GetCalendarComparisonDates) - .WithStandardProduces>(); - group.MapGet("merchants", DimensionsHandlers.GetMerchants).WithStandardProduces>(); - group.MapGet("operators", DimensionsHandlers.GetOperators).WithStandardProduces>(); - group.MapGet("responsecodes", DimensionsHandlers.GetResponseCodes).WithStandardProduces>(); - } -} \ No newline at end of file diff --git a/EstateReportingAPI/Endpoints/EstateEndpoints.cs b/EstateReportingAPI/Endpoints/EstateEndpoints.cs new file mode 100644 index 0000000..ec91b24 --- /dev/null +++ b/EstateReportingAPI/Endpoints/EstateEndpoints.cs @@ -0,0 +1,27 @@ +using EstateReportingAPI.DataTransferObjects; +using EstateReportingAPI.Handlers; +using Shared.Extensions; +using Shared.General; + +namespace EstateReportingAPI.Endpoints; + +public static class EstateEndpoints +{ + private const string BaseRoute = "api/estates"; + + public static void MapEstateEndpoints(this IEndpointRouteBuilder app) + { + RouteGroupBuilder group = app.MapGroup(BaseRoute) + .WithTags("Estates"); + + Boolean disableAuthorisation = ConfigurationReader.GetValueOrDefault("AppSettings", "DisableAuthorisation", false); + if (disableAuthorisation == false) { + group = group.RequireAuthorization(); + } + + group.MapGet("/", EstateHandler.GetEstate) + .WithStandardProduces(); + group.MapGet("/operators", EstateHandler.GetOperators) + .WithStandardProduces>(); + } +} \ No newline at end of file diff --git a/EstateReportingAPI/Endpoints/FactSettlementsEndpoints.cs b/EstateReportingAPI/Endpoints/FactSettlementsEndpoints.cs deleted file mode 100644 index bbfd7dc..0000000 --- a/EstateReportingAPI/Endpoints/FactSettlementsEndpoints.cs +++ /dev/null @@ -1,24 +0,0 @@ -using EstateReportingAPI.DataTransferObjects; -using EstateReportingAPI.Handlers; -using Shared.Extensions; - -namespace EstateReportingAPI.Endpoints; - -public static class FactSettlementsEndpoints -{ - private const string BaseRoute = "api/facts/settlements"; - - public static void MapFactSettlementsEndpoints(this IEndpointRouteBuilder app) - { - RouteGroupBuilder group = app.MapGroup(BaseRoute) - .RequireAuthorization() - .WithTags("Fact Settlements"); - - group.MapGet("todayssettlement", FactSettlementsHandlers.TodaysSettlement) - .WithStandardProduces(); - group.MapGet("lastsettlement", FactSettlementsHandlers.LastSettlement) - .WithStandardProduces(); - group.MapGet("unsettledfees", FactSettlementsHandlers.GetUnsettledFees) - .WithStandardProduces>(); - } -} \ No newline at end of file diff --git a/EstateReportingAPI/Endpoints/FactTransactionsEndpoints.cs b/EstateReportingAPI/Endpoints/FactTransactionsEndpoints.cs deleted file mode 100644 index fa9e0e6..0000000 --- a/EstateReportingAPI/Endpoints/FactTransactionsEndpoints.cs +++ /dev/null @@ -1,45 +0,0 @@ -using EstateReportingAPI.DataTransferObjects; -using EstateReportingAPI.DataTrasferObjects; -using EstateReportingAPI.Handlers; -using Shared.Extensions; - -namespace EstateReportingAPI.Endpoints; - -public static class FactTransactionsEndpoints -{ - private const string BaseRoute = "api/facts/transactions"; - - public static void MapFactTransactionEndpoints(this IEndpointRouteBuilder app) - { - RouteGroupBuilder group = app.MapGroup(BaseRoute) - .RequireAuthorization() - .WithTags("Fact Transactions"); - - group.MapGet("todayssales", FactTransactionsHandler.TodaysSales) - .WithStandardProduces(); - group.MapGet("todayssales/countbyhour", FactTransactionsHandler.TodaysSalesCountByHour) - .WithStandardProduces>(); - group.MapGet("todayssales/valuebyhour", FactTransactionsHandler.TodaysSalesValueByHour) - .WithStandardProduces>(); - group.MapGet("merchants/lastsale", FactTransactionsHandler.GetMerchantsByLastSale) - .WithStandardProduces>(); - group.MapGet("merchantkpis", FactTransactionsHandler.GetMerchantsTransactionKpis) - .WithStandardProduces(); - group.MapGet("todaysfailedsales", FactTransactionsHandler.TodaysFailedSales) - .WithStandardProduces(); - group.MapGet("products/topbottombyvalue", FactTransactionsHandler.GetTopBottomProductsByValue) - .WithStandardProduces>(); - group.MapGet("merchants/topbottombyvalue", FactTransactionsHandler.GetTopBottomMerchantsByValue) - .WithStandardProduces>(); - group.MapGet("operators/topbottombyvalue", FactTransactionsHandler.GetTopBottomOperatorsByValue) - .WithStandardProduces>(); - group.MapGet("merchants/performance", FactTransactionsHandler.GetMerchantPerformance) - .WithStandardProduces(); - group.MapGet("products/performance", FactTransactionsHandler.GetProductPerformance) - .WithStandardProduces(); - group.MapGet("operators/performance", FactTransactionsHandler.GetOperatorPerformance) - .WithStandardProduces(); - group.MapPost("search", FactTransactionsHandler.TransactionSearch) - .WithStandardProduces>(); - } -} \ No newline at end of file diff --git a/EstateReportingAPI/Endpoints/MerchantEndpoints.cs b/EstateReportingAPI/Endpoints/MerchantEndpoints.cs new file mode 100644 index 0000000..99a1bb0 --- /dev/null +++ b/EstateReportingAPI/Endpoints/MerchantEndpoints.cs @@ -0,0 +1,38 @@ +using EstateReportingAPI.DataTransferObjects; +using EstateReportingAPI.DataTrasferObjects; +using EstateReportingAPI.Handlers; +using Shared.Extensions; +using Shared.General; + +namespace EstateReportingAPI.Endpoints; + +public static class MerchantEndpoints +{ + private const string BaseRoute = "api/merchants"; + + public static void MapMerchantEndpoints(this IEndpointRouteBuilder app) + { + RouteGroupBuilder group = app.MapGroup(BaseRoute) + .WithTags("Merchants"); + + Boolean disableAuthorisation = ConfigurationReader.GetValueOrDefault("AppSettings", "DisableAuthorisation", false); + if (disableAuthorisation == false) { + group = group.RequireAuthorization(); + } + + group.MapGet("/kpis", MerchantHandler.GetMerchantKpis) + .WithStandardProduces>(); + group.MapGet("/recent", MerchantHandler.GetRecentMerchants) + .WithStandardProduces>(); + group.MapGet("/", MerchantHandler.GetMerchants) + .WithStandardProduces>(); + group.MapGet("/{merchantId}", MerchantHandler.GetMerchant) + .WithStandardProduces(); + group.MapGet("/{merchantId}/operators", MerchantHandler.GetMerchantOperators) + .WithStandardProduces(); + group.MapGet("/{merchantId}/contracts", MerchantHandler.GetMerchantContracts) + .WithStandardProduces(); + group.MapGet("/{merchantId}/devices", MerchantHandler.GetMerchantDevices) + .WithStandardProduces(); + } +} \ No newline at end of file diff --git a/EstateReportingAPI/Endpoints/OperatorEndpoints.cs b/EstateReportingAPI/Endpoints/OperatorEndpoints.cs new file mode 100644 index 0000000..fb249c3 --- /dev/null +++ b/EstateReportingAPI/Endpoints/OperatorEndpoints.cs @@ -0,0 +1,28 @@ +using EstateReportingAPI.DataTransferObjects; +using EstateReportingAPI.Handlers; +using Shared.Extensions; +using Shared.General; + +namespace EstateReportingAPI.Endpoints; + +public static class OperatorEndpoints +{ + private const string BaseRoute = "api/operators"; + + public static void MapOperatorEndpoints(this IEndpointRouteBuilder app) + { + RouteGroupBuilder group = app.MapGroup(BaseRoute) + .WithTags("Operators"); + + Boolean disableAuthorisation = ConfigurationReader.GetValueOrDefault("AppSettings", "DisableAuthorisation", false); + if (disableAuthorisation == false) { + group = group.RequireAuthorization(); + } + + group.MapGet("/", OperatorHandler.GetOperators) + .WithStandardProduces>(); + + group.MapGet("/{operatorId}", OperatorHandler.GetOperator) + .WithStandardProduces(); + } +} \ No newline at end of file diff --git a/EstateReportingAPI/Endpoints/TransactionEndpoints.cs b/EstateReportingAPI/Endpoints/TransactionEndpoints.cs new file mode 100644 index 0000000..a8d995c --- /dev/null +++ b/EstateReportingAPI/Endpoints/TransactionEndpoints.cs @@ -0,0 +1,26 @@ +using EstateReportingAPI.DataTransferObjects; +using EstateReportingAPI.Handlers; +using Shared.Extensions; +using Shared.General; + +namespace EstateReportingAPI.Endpoints; + +public static class TransactionEndpoints +{ + private const string BaseRoute = "api/transactions"; + public static void MapTransactionEndpoints(this IEndpointRouteBuilder app) + { + RouteGroupBuilder group = app.MapGroup(BaseRoute) + .WithTags("Transactions"); + + Boolean disableAuthorisation = ConfigurationReader.GetValueOrDefault("AppSettings", "DisableAuthorisation", false); + if (disableAuthorisation == false) { + group = group.RequireAuthorization(); + } + + group.MapGet("todayssales", TransactionHandler.TodaysSales) + .WithStandardProduces(); + group.MapGet("todaysfailedsales", TransactionHandler.TodaysFailedSales) + .WithStandardProduces(); + } +} \ No newline at end of file diff --git a/EstateReportingAPI/EstateReportingAPI.csproj b/EstateReportingAPI/EstateReportingAPI.csproj index dc43842..12832ee 100644 --- a/EstateReportingAPI/EstateReportingAPI.csproj +++ b/EstateReportingAPI/EstateReportingAPI.csproj @@ -35,7 +35,7 @@ - + diff --git a/EstateReportingAPI/Handlers/CalenderHandler.cs b/EstateReportingAPI/Handlers/CalenderHandler.cs new file mode 100644 index 0000000..723ad05 --- /dev/null +++ b/EstateReportingAPI/Handlers/CalenderHandler.cs @@ -0,0 +1,29 @@ +using EstateReportingAPI.BusinessLogic.Queries; +using EstateReportingAPI.DataTrasferObjects; +using EstateReportingAPI.Models; +using MediatR; +using Microsoft.AspNetCore.Mvc; +using Shared.Results.Web; +using SimpleResults; + +namespace EstateReportingAPI.Handlers; + +public static class CalenderHandler { + public static async Task GetCalendarComparisonDates([FromHeader] Guid estateId, + IMediator mediator, + CancellationToken cancellationToken) { + CalendarQueries.GetComparisonDatesQuery query = new CalendarQueries.GetComparisonDatesQuery(estateId); + Result> result = await mediator.Send(query, cancellationToken); + + return ResponseFactory.FromResult(result, r => { + List response = new() { new() { Date = DateTime.Now.Date.AddDays(-1), Description = "Yesterday", OrderValue = 0 }, new() { Date = DateTime.Now.Date.AddDays(-7), Description = "Last Week", OrderValue = 1 }, new() { Date = DateTime.Now.Date.AddMonths(-1), Description = "Last Month", OrderValue = 2 } }; + + int orderValue = 3; + foreach (Calendar d in r) { + response.Add(new ComparisonDate { Date = d.Date, Description = d.Date.ToString("yyyy-MM-dd"), OrderValue = orderValue++ }); + } + + return response.OrderBy(d => d.OrderValue); + }); + } +} \ No newline at end of file diff --git a/EstateReportingAPI/Handlers/ContractHandler.cs b/EstateReportingAPI/Handlers/ContractHandler.cs new file mode 100644 index 0000000..d338a56 --- /dev/null +++ b/EstateReportingAPI/Handlers/ContractHandler.cs @@ -0,0 +1,101 @@ +using EstateReportingAPI.BusinessLogic.Queries; +using EstateReportingAPI.Models; +using MediatR; +using Microsoft.AspNetCore.Mvc; +using Shared.Results.Web; +using SimpleResults; + +namespace EstateReportingAPI.Handlers; + +public static class ContractHandler { + public static async Task GetRecentContracts([FromHeader] Guid estateId, + IMediator mediator, + CancellationToken cancellationToken) + { + ContractQueries.GetRecentContractsQuery query = new ContractQueries.GetRecentContractsQuery(estateId); + Result> result = await mediator.Send(query, cancellationToken); + return ResponseFactory.FromResult(result, r => r.Select(m => new DataTransferObjects.Contract + { + EstateReportingId = m.EstateReportingId, + ContractId = m.ContractId, + ContractReportingId = m.ContractReportingId, + Description = m.Description, + EstateId = m.EstateId, + OperatorId = m.OperatorId, + OperatorName = m.OperatorName, + OperatorReportingId = m.OperatorReportingId + }).ToList()); + } + + public static async Task GetContracts([FromHeader] Guid estateId, + IMediator mediator, + CancellationToken cancellationToken) + { + ContractQueries.GetContractsQuery query = new ContractQueries.GetContractsQuery(estateId); + Result> result = await mediator.Send(query, cancellationToken); + return ResponseFactory.FromResult(result, r => r.Select(m => new DataTransferObjects.Contract + { + EstateReportingId = m.EstateReportingId, + ContractId = m.ContractId, + ContractReportingId = m.ContractReportingId, + Description = m.Description, + EstateId = m.EstateId, + OperatorId = m.OperatorId, + OperatorName = m.OperatorName, + OperatorReportingId = m.OperatorReportingId, + Products = m.Products.Select(p => new DataTransferObjects.ContractProduct + { + ContractId = p.ContractId, + DisplayText = p.DisplayText, + ProductId = p.ProductId, + ProductName = p.ProductName, + ProductType = p.ProductType, + Value = p.Value, + TransactionFees = p.TransactionFees.Select(f => new DataTransferObjects.ContractProductTransactionFee { + Description = f.Description, + Value = f.Value, + CalculationType = f.CalculationType, + FeeType = f.FeeType, + TransactionFeeId = f.TransactionFeeId + }).ToList() + }).ToList() + }).ToList()); + } + + public static async Task GetContract([FromHeader] Guid estateId, + [FromRoute] Guid contractId, + IMediator mediator, + CancellationToken cancellationToken) + { + ContractQueries.GetContractQuery query = new ContractQueries.GetContractQuery(estateId, contractId); + Result result = await mediator.Send(query, cancellationToken); + return ResponseFactory.FromResult(result, r => new DataTransferObjects.Contract + { + EstateReportingId = r.EstateReportingId, + ContractId = r.ContractId, + ContractReportingId = r.ContractReportingId, + Description = r.Description, + EstateId = r.EstateId, + OperatorId = r.OperatorId, + OperatorName = r.OperatorName, + OperatorReportingId = r.OperatorReportingId, + Products = r.Products.Select(p => new DataTransferObjects.ContractProduct + { + ContractId = p.ContractId, + DisplayText = p.DisplayText, + ProductId = p.ProductId, + ProductName = p.ProductName, + ProductType = p.ProductType, + Value = p.Value, + TransactionFees = p.TransactionFees.Select(f => new DataTransferObjects.ContractProductTransactionFee + { + Description = f.Description, + Value = f.Value, + CalculationType = f.CalculationType, + FeeType = f.FeeType, + TransactionFeeId = f.TransactionFeeId + }).ToList() + }).ToList() + }); + } +} \ No newline at end of file diff --git a/EstateReportingAPI/Handlers/EstateHandler.cs b/EstateReportingAPI/Handlers/EstateHandler.cs new file mode 100644 index 0000000..ff8584e --- /dev/null +++ b/EstateReportingAPI/Handlers/EstateHandler.cs @@ -0,0 +1,83 @@ +using EstateReportingAPI.BusinessLogic.Queries; +using EstateReportingAPI.Models; +using MediatR; +using Microsoft.AspNetCore.Mvc; +using Shared.Results.Web; +using SimpleResults; + +namespace EstateReportingAPI.Handlers; + +public static class EstateHandler { + public static async Task GetEstate([FromHeader] Guid estateId, + IMediator mediator, + CancellationToken cancellationToken) + { + EstateQueries.GetEstateQuery query = new EstateQueries.GetEstateQuery(estateId); + Result result = await mediator.Send(query, cancellationToken); + + return ResponseFactory.FromResult(result, r => { + DataTransferObjects.Estate estate = new DataTransferObjects.Estate { + EstateId = r.EstateId, + EstateName = r.EstateName, + Reference = r.Reference, + Contracts = new List(), + Merchants = new List(), + Operators = new List(), + Users = new List() + }; + + foreach (var contract in r.Contracts) { + estate.Contracts.Add(new DataTransferObjects.EstateContract { + ContractId = contract.ContractId, + Name = contract.Name, + }); + } + + foreach (var merchant in r.Merchants) { + estate.Merchants.Add(new DataTransferObjects.EstateMerchant { + MerchantId = merchant.MerchantId, + Name = merchant.Name, + Reference = merchant.Reference + }); + } + + foreach (Models.EstateOperator estateOperator in r.Operators) { + estate.Operators.Add(new DataTransferObjects.EstateOperator() { + Name = estateOperator.Name, + OperatorId = estateOperator.OperatorId + }); + } + + foreach (Models.EstateUser estateUser in r.Users) { + estate.Users.Add(new DataTransferObjects.EstateUser() { + CreatedDateTime = estateUser.CreatedDateTime, + EmailAddress = estateUser.EmailAddress, + UserId = estateUser.UserId + }); + } + + return estate; + }); + } + + public static async Task GetOperators([FromHeader] Guid estateId, + IMediator mediator, + CancellationToken cancellationToken) + { + EstateQueries.GetEstateOperatorsQuery query = new EstateQueries.GetEstateOperatorsQuery(estateId); + Result> result = await mediator.Send(query, cancellationToken); + + return ResponseFactory.FromResult(result, r => { + List operators = new(); + + foreach (Models.EstateOperator estateOperator in r) { + operators.Add(new() { + Name = estateOperator.Name, + OperatorId = estateOperator.OperatorId + }); + } + + return operators; + }); + } +} \ No newline at end of file diff --git a/EstateReportingAPI/Handlers/MerchantHandler.cs b/EstateReportingAPI/Handlers/MerchantHandler.cs new file mode 100644 index 0000000..910359c --- /dev/null +++ b/EstateReportingAPI/Handlers/MerchantHandler.cs @@ -0,0 +1,180 @@ +using EstateReportingAPI.BusinessLogic.Queries; +using EstateReportingAPI.Models; +using MediatR; +using Microsoft.AspNetCore.Mvc; +using Shared.Results.Web; +using SimpleResults; + +namespace EstateReportingAPI.Handlers; + +public static class MerchantHandler +{ + public static async Task GetMerchantKpis([FromHeader] Guid estateId, + IMediator mediator, + CancellationToken cancellationToken) + { + MerchantQueries.GetTransactionKpisQuery query = new(estateId); + Result result = await mediator.Send(query, cancellationToken); + return ResponseFactory.FromResult(result, r => + new DataTransferObjects.MerchantKpi() + { + MerchantsWithNoSaleInLast7Days =r.MerchantsWithNoSaleInLast7Days, + MerchantsWithNoSaleToday = r.MerchantsWithNoSaleToday, + MerchantsWithSaleInLastHour = r.MerchantsWithSaleInLastHour + }); + } + + public static async Task GetRecentMerchants([FromHeader] Guid estateId, + IMediator mediator, + CancellationToken cancellationToken) { + MerchantQueries.GetRecentMerchantsQuery query = new(estateId); + Result> result = await mediator.Send(query, cancellationToken); + return ResponseFactory.FromResult(result, r => r.Select(m => new EstateReportingAPI.DataTransferObjects.Merchant + { + MerchantReportingId = m.MerchantReportingId, + MerchantId = m.MerchantId, + Name = m.Name, + Reference = m.Reference, + SettlementSchedule = m.SettlementSchedule, + CreatedDateTime = m.CreatedDateTime, + Balance = m.Balance, + AddressLine1 = m.AddressLine1, + AddressLine2 = m.AddressLine2, + Town = m.Town, + Region = m.Region, + PostCode = m.PostCode, + Country = m.Country, + ContactName = m.ContactName, + ContactEmail = m.ContactEmail, + ContactPhone = m.ContactPhone + + }).OrderByDescending(m => m.CreatedDateTime).ToList()); + } + + public static async Task GetMerchants([FromHeader] Guid estateId, + [FromQuery] String? name, + [FromQuery] String? reference, + [FromQuery] Int32? settlementSchedule, + [FromQuery] String? region, + [FromQuery] String? postCode, + IMediator mediator, + CancellationToken cancellationToken) + { + // Build the query options + MerchantQueries.MerchantQueryOptions queryOptions = new(name, reference, settlementSchedule, region, postCode); + MerchantQueries.GetMerchantsQuery query = new(estateId, queryOptions); + Result> result = await mediator.Send(query, cancellationToken); + + return ResponseFactory.FromResult(result, r => r.Select(m => new EstateReportingAPI.DataTransferObjects.Merchant + { + MerchantReportingId = m.MerchantReportingId, + MerchantId = m.MerchantId, + Name = m.Name, + Reference = m.Reference, + SettlementSchedule = m.SettlementSchedule, + CreatedDateTime = m.CreatedDateTime, + Balance = m.Balance, + AddressLine1 = m.AddressLine1, + AddressLine2 = m.AddressLine2, + Town = m.Town, + Region = m.Region, + PostCode = m.PostCode, + Country = m.Country, + ContactName = m.ContactName, + ContactEmail = m.ContactEmail, + ContactPhone = m.ContactPhone + }).OrderByDescending(m => m.CreatedDateTime).ToList()); + } + + public static async Task GetMerchant([FromHeader] Guid estateId, + [FromRoute] Guid merchantId, + IMediator mediator, + CancellationToken cancellationToken) + { + // Build the query options + MerchantQueries.GetMerchantQuery query = new(estateId, merchantId); + Result result = await mediator.Send(query, cancellationToken); + + return ResponseFactory.FromResult(result, r => new EstateReportingAPI.DataTransferObjects.Merchant + { + MerchantReportingId = r.MerchantReportingId, + MerchantId = r.MerchantId, + Name = r.Name, + Reference = r.Reference, + SettlementSchedule = r.SettlementSchedule, + CreatedDateTime = r.CreatedDateTime, + Balance = r.Balance, + AddressLine1 = r.AddressLine1, + AddressLine2 = r.AddressLine2, + Town = r.Town, + Region = r.Region, + PostCode = r.PostCode, + Country = r.Country, + ContactName = r.ContactName, + ContactEmail = r.ContactEmail, + ContactPhone = r.ContactPhone, + AddressId = r.AddressId, + ContactId = r.ContactId + }); + } + + public static async Task GetMerchantOperators([FromHeader] Guid estateId, + [FromRoute] Guid merchantId, + IMediator mediator, + CancellationToken cancellationToken) { + MerchantQueries.GetMerchantOperatorsQuery query = new(estateId, merchantId); + Result> result = await mediator.Send(query, cancellationToken); + + return ResponseFactory.FromResult(result, r => r.Select(o => new DataTransferObjects.MerchantOperator() { + IsDeleted = o.IsDeleted, + MerchantId = o.MerchantId, + MerchantNumber = o.MerchantNumber, + OperatorId = o.OperatorId, + OperatorName = o.OperatorName, + TerminalNumber = o.TerminalNumber + }).ToList()); + } + + public static async Task GetMerchantContracts([FromHeader] Guid estateId, + [FromRoute] Guid merchantId, + IMediator mediator, + CancellationToken cancellationToken) + { + MerchantQueries.GetMerchantContractsQuery query = new(estateId, merchantId); + Result> result = await mediator.Send(query, cancellationToken); + + return ResponseFactory.FromResult(result, r => r.Select(c => new DataTransferObjects.MerchantContract() + { + ContractId = c.ContractId, + ContractName = c.ContractName, + OperatorName = c.OperatorName, + IsDeleted = c.IsDeleted, + MerchantId = c.MerchantId, + ContractProducts = c.ContractProducts.Select(p => new DataTransferObjects.MerchantContractProduct() + { + ProductId = p.ProductId, + ProductName = p.ProductName, + ContractId = c.ContractId, + DisplayText = p.DisplayText, + MerchantId = c.MerchantId + }).ToList() + }).ToList()); + } + + public static async Task GetMerchantDevices([FromHeader] Guid estateId, + [FromRoute] Guid merchantId, + IMediator mediator, + CancellationToken cancellationToken) + { + MerchantQueries.GetMerchantDevicesQuery query = new(estateId, merchantId); + Result> result = await mediator.Send(query, cancellationToken); + + return ResponseFactory.FromResult(result, r => r.Select(c => new DataTransferObjects.MerchantDevice() + { + IsDeleted = c.IsDeleted, + MerchantId = c.MerchantId, + DeviceId = c.DeviceId, + DeviceIdentifier = c.DeviceIdentifier + }).ToList()); + } +} \ No newline at end of file diff --git a/EstateReportingAPI/Handlers/OperatorHandler.cs b/EstateReportingAPI/Handlers/OperatorHandler.cs new file mode 100644 index 0000000..84ce179 --- /dev/null +++ b/EstateReportingAPI/Handlers/OperatorHandler.cs @@ -0,0 +1,45 @@ +using EstateReportingAPI.BusinessLogic.Queries; +using EstateReportingAPI.Models; +using MediatR; +using Microsoft.AspNetCore.Mvc; +using Shared.Results.Web; +using SimpleResults; + +namespace EstateReportingAPI.Handlers; + +public static class OperatorHandler { + public static async Task GetOperators([FromHeader] Guid estateId, + IMediator mediator, + CancellationToken cancellationToken) + { + OperatorQueries.GetOperatorsQuery query = new(estateId); + Result> result = await mediator.Send(query, cancellationToken); + return ResponseFactory.FromResult(result, r => r.Select(m => new DataTransferObjects.Operator + { + OperatorId = m.OperatorId, + Name = m.Name, + OperatorReportingId = m.OperatorReportingId, + EstateReportingId = m.EstateReportingId, + RequireCustomMerchantNumber = m.RequireCustomMerchantNumber, + RequireCustomTerminalNumber = m.RequireCustomTerminalNumber + }).ToList()); + } + + public static async Task GetOperator([FromHeader] Guid estateId, + [FromRoute] Guid operatorId, + IMediator mediator, + CancellationToken cancellationToken) + { + OperatorQueries.GetOperatorQuery query = new(estateId, operatorId); + Result result = await mediator.Send(query, cancellationToken); + return ResponseFactory.FromResult(result, r => new DataTransferObjects.Operator + { + OperatorId = r.OperatorId, + Name = r.Name, + OperatorReportingId = r.OperatorReportingId, + EstateReportingId = r.EstateReportingId, + RequireCustomMerchantNumber = r.RequireCustomMerchantNumber, + RequireCustomTerminalNumber = r.RequireCustomTerminalNumber + }); + } +} \ No newline at end of file diff --git a/EstateReportingAPI/Handlers/TransactionHandler.cs b/EstateReportingAPI/Handlers/TransactionHandler.cs new file mode 100644 index 0000000..18d65ca --- /dev/null +++ b/EstateReportingAPI/Handlers/TransactionHandler.cs @@ -0,0 +1,50 @@ +using EstateReportingAPI.BusinessLogic.Queries; +using EstateReportingAPI.Models; +using MediatR; +using Microsoft.AspNetCore.Mvc; +using Shared.Results.Web; + +namespace EstateReportingAPI.Handlers; + +public static class TransactionHandler { + public static async Task TodaysSales([FromHeader] Guid estateId, + [FromQuery] int? merchantReportingId, + [FromQuery] int? operatorReportingId, + [FromQuery] DateTime comparisonDate, + IMediator mediator, + CancellationToken cancellationToken) + { + var query = new TransactionQueries.TodaysSalesQuery(estateId, merchantReportingId.GetValueOrDefault(), operatorReportingId.GetValueOrDefault(), comparisonDate); + var result = await mediator.Send(query, cancellationToken); + + return ResponseFactory.FromResult(result, r => new TodaysSales + { + ComparisonSalesCount = r.ComparisonSalesCount, + ComparisonSalesValue = r.ComparisonSalesValue, + TodaysSalesCount = r.TodaysSalesCount, + TodaysSalesValue = r.TodaysSalesValue, + ComparisonAverageSalesValue = r.ComparisonAverageSalesValue, + TodaysAverageSalesValue = r.TodaysAverageSalesValue, + }); + } + + public static async Task TodaysFailedSales([FromHeader] Guid estateId, + [FromQuery] DateTime comparisonDate, + [FromQuery] string responseCode, + IMediator mediator, + CancellationToken cancellationToken) + { + var query = new TransactionQueries.TodaysFailedSales(estateId, comparisonDate, responseCode); + var result = await mediator.Send(query, cancellationToken); + + return ResponseFactory.FromResult(result, r => new TodaysSales + { + ComparisonSalesCount = r.ComparisonSalesCount, + ComparisonSalesValue = r.ComparisonSalesValue, + TodaysSalesCount = r.TodaysSalesCount, + TodaysSalesValue = r.TodaysSalesValue, + ComparisonAverageSalesValue = r.ComparisonAverageSalesValue, + TodaysAverageSalesValue = r.TodaysAverageSalesValue, + }); + } +} \ No newline at end of file diff --git a/EstateReportingAPI/Handlers/DimensionsHandlers.cs b/EstateReportingAPI/Handlers/archivecode/DimensionsHandlers.cs similarity index 99% rename from EstateReportingAPI/Handlers/DimensionsHandlers.cs rename to EstateReportingAPI/Handlers/archivecode/DimensionsHandlers.cs index 6a740ed..7aca218 100644 --- a/EstateReportingAPI/Handlers/DimensionsHandlers.cs +++ b/EstateReportingAPI/Handlers/archivecode/DimensionsHandlers.cs @@ -5,7 +5,7 @@ using Shared.Results.Web; namespace EstateReportingAPI.Handlers; - +/* public static class DimensionsHandlers { public static async Task GetCalendarYears( @@ -148,4 +148,4 @@ public static async Task GetResponseCodes( .ToList() ); } -} \ No newline at end of file +}*/ \ No newline at end of file diff --git a/EstateReportingAPI/Handlers/FactSettlementsHandlers.cs b/EstateReportingAPI/Handlers/archivecode/FactSettlementsHandlers.cs similarity index 99% rename from EstateReportingAPI/Handlers/FactSettlementsHandlers.cs rename to EstateReportingAPI/Handlers/archivecode/FactSettlementsHandlers.cs index d15311e..151a115 100644 --- a/EstateReportingAPI/Handlers/FactSettlementsHandlers.cs +++ b/EstateReportingAPI/Handlers/archivecode/FactSettlementsHandlers.cs @@ -5,7 +5,7 @@ using Shared.Results.Web; namespace EstateReportingAPI.Handlers; - +/* public static class FactSettlementsHandlers { @@ -173,4 +173,4 @@ private static Models.GroupByOption ConvertGroupByOption(GroupByOption groupByOp GroupByOption.Product => Models.GroupByOption.Product, _ => Models.GroupByOption.Operator }; -} \ No newline at end of file +}*/ \ No newline at end of file diff --git a/EstateReportingAPI/Handlers/FactTransactionsHandler.cs b/EstateReportingAPI/Handlers/archivecode/FactTransactionsHandler.cs similarity index 99% rename from EstateReportingAPI/Handlers/FactTransactionsHandler.cs rename to EstateReportingAPI/Handlers/archivecode/FactTransactionsHandler.cs index bc837d1..123fc39 100644 --- a/EstateReportingAPI/Handlers/FactTransactionsHandler.cs +++ b/EstateReportingAPI/Handlers/archivecode/FactTransactionsHandler.cs @@ -7,9 +7,9 @@ using SimpleResults; using GroupByOption = EstateReportingAPI.DataTransferObjects.GroupByOption; using LastSettlement = EstateReportingAPI.DataTransferObjects.LastSettlement; -using Merchant = EstateReportingAPI.DataTrasferObjects.Merchant; +using Merchant = EstateReportingAPI.DataTransferObjects.Merchant; using MerchantKpi = EstateReportingAPI.DataTransferObjects.MerchantKpi; -using Operator = EstateReportingAPI.DataTrasferObjects.Operator; +using Operator = EstateReportingAPI.DataTransferObjects.Operator; using ResponseCode = EstateReportingAPI.DataTrasferObjects.ResponseCode; using SortDirection = EstateReportingAPI.DataTransferObjects.SortDirection; using SortField = EstateReportingAPI.DataTransferObjects.SortField; @@ -21,7 +21,7 @@ using TransactionResult = EstateReportingAPI.DataTransferObjects.TransactionResult; using TransactionSearchRequest = EstateReportingAPI.DataTransferObjects.TransactionSearchRequest; -namespace EstateReportingAPI.Handlers +/*namespace EstateReportingAPI.Handlers { public static class FactTransactionsHandler { public static async Task TodaysSales([FromHeader] Guid estateId, @@ -332,3 +332,4 @@ private static List ParseCsvIds(string? csv) } } +*/ \ No newline at end of file diff --git a/EstateReportingAPI/Startup.cs b/EstateReportingAPI/Startup.cs index ad8f387..74fcfdc 100644 --- a/EstateReportingAPI/Startup.cs +++ b/EstateReportingAPI/Startup.cs @@ -68,9 +68,16 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerF app.UseEndpoints(endpoints => { - endpoints.MapDimensionsEndpoints(); - endpoints.MapFactSettlementsEndpoints(); - endpoints.MapFactTransactionEndpoints(); + endpoints.MapCalendarEndpoints(); + endpoints.MapEstateEndpoints(); + endpoints.MapOperatorEndpoints(); + endpoints.MapMerchantEndpoints(); + endpoints.MapContractEndpoints(); + endpoints.MapTransactionEndpoints(); + + // Old Endpoints + //endpoints.MapFactSettlementsEndpoints(); + //endpoints.MapFactTransactionEndpoints(); //endpoints.MapControllers(); endpoints.MapHealthChecks("health", new HealthCheckOptions()