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