From 2d349b92b77bef215ea3069d7d1780c3140c0475 Mon Sep 17 00:00:00 2001 From: Stuart Ferguson Date: Wed, 5 Nov 2025 17:16:00 +0000 Subject: [PATCH] more to minimal api completed --- .../EstateReportingApiClient.cs | 6 +- .../Controllers/DimensionsController.cs | 183 -------- .../Controllers/FactSettlementController.cs | 143 ------ .../Controllers/FactTransactionsController.cs | 444 ------------------ .../Endpoints/DimensionsEndpoints.cs | 28 ++ .../Endpoints/FactSettlementsEndpoints.cs | 24 + .../Endpoints/FactTransactionsEndpoints.cs | 45 ++ .../Handlers/DimensionsHandlers.cs | 151 ++++++ .../Handlers/FactSettlementsHandlers.cs | 112 +++++ .../Handlers/FactTransactionsHandler.cs | 334 +++++++++++++ EstateReportingAPI/Startup.cs | 7 +- 11 files changed, 702 insertions(+), 775 deletions(-) delete mode 100644 EstateReportingAPI/Controllers/DimensionsController.cs delete mode 100644 EstateReportingAPI/Controllers/FactSettlementController.cs delete mode 100644 EstateReportingAPI/Controllers/FactTransactionsController.cs create mode 100644 EstateReportingAPI/Endpoints/DimensionsEndpoints.cs create mode 100644 EstateReportingAPI/Endpoints/FactSettlementsEndpoints.cs create mode 100644 EstateReportingAPI/Endpoints/FactTransactionsEndpoints.cs create mode 100644 EstateReportingAPI/Handlers/DimensionsHandlers.cs create mode 100644 EstateReportingAPI/Handlers/FactSettlementsHandlers.cs create mode 100644 EstateReportingAPI/Handlers/FactTransactionsHandler.cs diff --git a/EstateReportingAPI.Client/EstateReportingApiClient.cs b/EstateReportingAPI.Client/EstateReportingApiClient.cs index b456d12..5b793c1 100644 --- a/EstateReportingAPI.Client/EstateReportingApiClient.cs +++ b/EstateReportingAPI.Client/EstateReportingApiClient.cs @@ -285,10 +285,8 @@ public async Task>> TransactionSearch(String acce List<(String headerName, String headerValue)> additionalHeaders = [ ("EstateId", estateId.ToString()) ]; - - StringContent content = new StringContent(JsonConvert.SerializeObject(searchRequest), Encoding.UTF8, "application/json"); - - Result>? result = await this.SendGetRequest>(requestUri, accessToken, additionalHeaders,content, cancellationToken); + + Result>? result = await this.SendPostRequest>(requestUri, accessToken, searchRequest, cancellationToken, additionalHeaders); if (result.IsFailed) return ResultHelpers.CreateFailure(result); diff --git a/EstateReportingAPI/Controllers/DimensionsController.cs b/EstateReportingAPI/Controllers/DimensionsController.cs deleted file mode 100644 index 29149eb..0000000 --- a/EstateReportingAPI/Controllers/DimensionsController.cs +++ /dev/null @@ -1,183 +0,0 @@ -using EstateReportingAPI.BusinessLogic.Queries; -using EstateReportingAPI.Models; -using MediatR; -using Shared.Results; -using Shared.Results.Web; -using SimpleResults; - -namespace EstateReportingAPI.Controllers -{ - using DataTrasferObjects; - using Microsoft.AspNetCore.Authorization; - using Microsoft.AspNetCore.Mvc; - using System.Diagnostics.CodeAnalysis; - using BusinessLogic; - using IdentityModel; - - [ExcludeFromCodeCoverage] - [Route(DimensionsController.ControllerRoute)] - [ApiController] - [Authorize] - public class DimensionsController : ControllerBase{ - private readonly IMediator Mediator; - - public DimensionsController(IMediator mediator) { - this.Mediator = mediator; - } - - #region Others - - /// - /// The controller name - /// - private const String ControllerName = "dimensions"; - - /// - /// The controller route - /// - private const String ControllerRoute = "api/" + DimensionsController.ControllerName; - - #endregion - - [HttpGet] - [Route("calendar/years")] - public async Task GetCalendarYears([FromHeader] Guid estateId, - CancellationToken cancellationToken) { - - CalendarQueries.GetYearsQuery query = new(estateId); - Result> result = await this.Mediator.Send(query, cancellationToken); - - return ResponseFactory.FromResult(result, (r) => { - List response = new List(); - - r.ForEach(y => response.Add(new CalendarYear { Year = y })); - - return response; - }); - } - - - [HttpGet] - [Route("calendar/{year}/dates")] - public async Task GetCalendarDates([FromHeader] Guid estateId, [FromRoute] Int32 year, CancellationToken cancellationToken){ - CalendarQueries.GetAllDatesQuery query = new(estateId); - Result> result = await this.Mediator.Send(query, cancellationToken); - - return ResponseFactory.FromResult(result, (r) => { - List response = new List(); - - r.ForEach(d => response.Add(new CalendarDate - { - Year = d.Year, - Date = d.Date, - DayOfWeek = d.DayOfWeek, - DayOfWeekNumber = d.DayOfWeekNumber, - DayOfWeekShort = d.DayOfWeekShort, - MonthName = d.MonthNameLong, - MonthNameShort = d.MonthNameShort, - MonthNumber = d.MonthNumber, - WeekNumber = d.WeekNumber ?? 0, - WeekNumberString = d.WeekNumberString, - YearWeekNumber = d.YearWeekNumber, - })); - - return response; - }); - } - - [HttpGet] - [Route("calendar/comparisondates")] - public async Task GetCalendarComparisonDates([FromHeader] Guid estateId, CancellationToken cancellationToken){ - CalendarQueries.GetComparisonDatesQuery query = new(estateId); - Result> result = await this.Mediator.Send(query, cancellationToken); - - return ResponseFactory.FromResult(result, (r) => { - List response = [ - new ComparisonDate { Date = DateTime.Now.Date.AddDays(-1), Description = "Yesterday", OrderValue = 0 }, - - new ComparisonDate { Date = DateTime.Now.Date.AddDays(-7), Description = "Last Week", OrderValue = 1 }, - - new ComparisonDate { Date = DateTime.Now.Date.AddMonths(-1), Description = "Last Month", OrderValue = 2 } - - ]; - - Int32 orderValue = 3; - r.ForEach(d => - { - response.Add(new ComparisonDate - { - Date = d.Date, - Description = d.Date.ToString("yyyy-MM-dd"), - OrderValue = orderValue - }); - orderValue++; - }); - - return response.OrderBy(d => d.OrderValue); - }); - } - - [HttpGet] - [Route("merchants")] - public async Task GetMerchants([FromHeader] Guid estateId, CancellationToken cancellationToken) { - MerchantQueries.GetMerchantsQuery query = new(estateId); - Result> result = await this.Mediator.Send(query, cancellationToken); - - return ResponseFactory.FromResult(result, (r) => { - List response = new List(); - - r.ForEach(m => response.Add(new Merchant { - MerchantReportingId = m.MerchantReportingId, - MerchantId = m.MerchantId, - EstateReportingId = m.EstateReportingId, - Name = m.Name, - LastSaleDateTime = m.LastSaleDateTime, - CreatedDateTime = m.CreatedDateTime, - LastSale = m.LastSale, - LastStatement = m.LastStatement, - PostCode = m.PostCode, - Reference = m.Reference, - Region = m.Region, - Town = m.Town, - })); - - return response.OrderBy(m => m.Name); - }); - } - - [HttpGet] - [Route("operators")] - public async Task GetOperators([FromHeader] Guid estateId, CancellationToken cancellationToken) - { - OperatorQueries.GetOperatorsQuery query = new(estateId); - Result> result = await this.Mediator.Send(query, cancellationToken); - - return ResponseFactory.FromResult(result, (r) => { - List response = new List(); - - r.ForEach(o => response.Add(new Operator { EstateReportingId = o.EstateReportingId, Name = o.Name, OperatorId = o.OperatorId, OperatorReportingId = o.OperatorReportingId })); - - return response.OrderBy(o => o.Name); - }); - } - - [HttpGet] - [Route("responsecodes")] - public async Task GetResponseCodes([FromHeader] Guid estateId, CancellationToken cancellationToken) - { - - ResponseCodeQueries.GetResponseCodesQuery query = new(estateId); - var result = await this.Mediator.Send(query, cancellationToken); - - return ResponseFactory.FromResult(result, (r) => { - List response = new List(); - - r.ForEach(o => response.Add(new ResponseCode { Code = o.Code, Description = o.Description })); - - return response.OrderBy(r => r.Code); - }); - } - } - - -} diff --git a/EstateReportingAPI/Controllers/FactSettlementController.cs b/EstateReportingAPI/Controllers/FactSettlementController.cs deleted file mode 100644 index b7ff366..0000000 --- a/EstateReportingAPI/Controllers/FactSettlementController.cs +++ /dev/null @@ -1,143 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using System.Diagnostics.CodeAnalysis; -using EstateReportingAPI.BusinessLogic.Queries; -using Shared.Results.Web; - -namespace EstateReportingAPI.Controllers -{ - using BusinessLogic; - using MediatR; - using Microsoft.AspNetCore.Authorization; - using Microsoft.AspNetCore.Mvc.Formatters; - using Models; - using Shared.Results; - using SimpleResults; - using static Microsoft.EntityFrameworkCore.DbLoggerCategory; - using GroupByOption = DataTransferObjects.GroupByOption; - using LastSettlement = Models.LastSettlement; - using TodaysSettlement = DataTransferObjects.TodaysSettlement; - - [ExcludeFromCodeCoverage] - [Route(FactSettlementsController.ControllerRoute)] - [ApiController] - [Authorize] - public class FactSettlementsController : ControllerBase - { - #region Others - - /// - /// The controller name - /// - private const String ControllerName = "settlements"; - - /// - /// The controller route - /// - private const String ControllerRoute = "api/facts/" + FactSettlementsController.ControllerName; - - #endregion - - private readonly IMediator Mediator; - - public FactSettlementsController(IMediator mediator) { - this.Mediator = mediator; - } - - [HttpGet] - [Route("todayssettlement")] - public async Task TodaysSettlement([FromHeader] Guid estateId, [FromQuery] Int32 merchantReportingId, [FromQuery] Int32 operatorReportingId, [FromQuery] DateTime comparisonDate, CancellationToken cancellationToken){ - SettlementQueries.GetTodaysSettlementQuery query = new(estateId, merchantReportingId, operatorReportingId, comparisonDate); - Result result = await this.Mediator.Send(query, cancellationToken); - - return ResponseFactory.FromResult(result, (r) => new TodaysSettlement - { - ComparisonSettlementCount = r.ComparisonSettlementCount, - ComparisonSettlementValue = r.ComparisonSettlementValue, - ComparisonPendingSettlementCount = r.ComparisonPendingSettlementCount, - ComparisonPendingSettlementValue = r.ComparisonPendingSettlementValue, - - TodaysSettlementCount = r.TodaysSettlementCount, - TodaysSettlementValue = r.TodaysSettlementValue, - TodaysPendingSettlementCount = r.TodaysPendingSettlementCount, - TodaysPendingSettlementValue = r.TodaysPendingSettlementValue - }); - } - - [HttpGet] - [Route("lastsettlement")] - public async Task LastSettlement([FromHeader] Guid estateId, - CancellationToken cancellationToken) { - SettlementQueries.GetLastSettlementQuery query = new(estateId); - - Result result = await this.Mediator.Send(query, cancellationToken); - - return ResponseFactory.FromResult(result, (r) => new LastSettlement() - { - SalesCount = r.SalesCount, - FeesValue = r.FeesValue, - SalesValue = r.SalesValue, - SettlementDate = r.SettlementDate, - }); - } - - [HttpGet] - [Route("unsettledfees")] - public async Task GetUnsettledFees([FromHeader] Guid estateId, - [FromQuery] DateTime startDate, - [FromQuery] DateTime endDate, - [FromQuery] string? merchantIds, [FromQuery] string? operatorIds, - [FromQuery] string? productIds, - [FromQuery] GroupByOption? groupByOption, CancellationToken cancellationToken) - { - List merchantIdFilter = new List(); - if (String.IsNullOrEmpty(merchantIds) == false) { - List merchantListStrings = merchantIds.Split(',').ToList(); - foreach (String merchantListString in merchantListStrings) { - merchantIdFilter.Add(Int32.Parse(merchantListString)); - } - } - - List operatorIdFilter = new List(); - if (String.IsNullOrEmpty(operatorIds) == false) { - List operatorListStrings = operatorIds.Split(',').ToList(); - foreach (String operatorListString in operatorListStrings) { - operatorIdFilter.Add(Int32.Parse(operatorListString)); - } - } - - List productIdFilter = new List(); - if (String.IsNullOrEmpty(productIds) == false) { - List productListStrings = productIds.Split(',').ToList(); - foreach (String productListString in productListStrings) { - productIdFilter.Add(Int32.Parse(productListString)); - } - } - - Models.GroupByOption groupByOptionConverted = ConvertGroupByOption(groupByOption.GetValueOrDefault()); - SettlementQueries.GetUnsettledFeesQuery query = new(estateId, startDate, endDate,merchantIdFilter, operatorIdFilter, productIdFilter, groupByOptionConverted); - Result> result = await this.Mediator.Send(query, cancellationToken); - - return ResponseFactory.FromResult(result, (r) => { - List response = new(); - foreach (UnsettledFee unsettledFee in result.Data) - { - response.Add(new DataTransferObjects.UnsettledFee - { - DimensionName = unsettledFee.DimensionName, - FeesCount = unsettledFee.FeesCount, - FeesValue = unsettledFee.FeesValue - }); - } - return response; - }); - } - - private static Models.GroupByOption ConvertGroupByOption(GroupByOption groupByOption){ - return groupByOption switch{ - GroupByOption.Merchant => Models.GroupByOption.Merchant, - GroupByOption.Product => Models.GroupByOption.Product, - _ => Models.GroupByOption.Operator - }; - } - } -} diff --git a/EstateReportingAPI/Controllers/FactTransactionsController.cs b/EstateReportingAPI/Controllers/FactTransactionsController.cs deleted file mode 100644 index a3eb78d..0000000 --- a/EstateReportingAPI/Controllers/FactTransactionsController.cs +++ /dev/null @@ -1,444 +0,0 @@ -using EstateReportingAPI.BusinessLogic.Queries; -using JasperFx.Core; -using MediatR; -using Microsoft.AspNetCore.Mvc; -using Shared.Results; -using Shared.Results.Web; -using System.Diagnostics.CodeAnalysis; - -namespace EstateReportingAPI.Controllers{ - using BusinessLogic; - using DataTransferObjects; - using EstateReportingAPI.DataTrasferObjects; - using EstateReportingAPI.Models; - using Microsoft.AspNetCore.Authorization; - using Newtonsoft.Json; - using SimpleResults; - using MerchantKpi = DataTransferObjects.MerchantKpi; - using SortDirection = DataTransferObjects.SortDirection; - using SortField = DataTransferObjects.SortField; - using TodaysSales = DataTransferObjects.TodaysSales; - using TodaysSalesCountByHour = DataTransferObjects.TodaysSalesCountByHour; - using TodaysSalesValueByHour = DataTransferObjects.TodaysSalesValueByHour; - using TransactionResult = DataTransferObjects.TransactionResult; - using TransactionSearchRequest = DataTransferObjects.TransactionSearchRequest; - - [ExcludeFromCodeCoverage] - [Route(FactTransactionsController.ControllerRoute)] - [ApiController] - [Authorize] - public class FactTransactionsController : ControllerBase{ - #region Others - - /// - /// The controller name - /// - private const String ControllerName = "transactions"; - - /// - /// The controller route - /// - private const String ControllerRoute = "api/facts/" + FactTransactionsController.ControllerName; - - #endregion - - - private readonly IReportingManager ReportingManager; - private readonly IMediator Mediator; - - public FactTransactionsController(IReportingManager reportingManager, IMediator mediator) { - this.ReportingManager = reportingManager; - this.Mediator = mediator; - } - - private const String ConnectionStringIdentifier = "EstateReportingReadModel"; - - [HttpGet] - [Route("todayssales")] - public async Task TodaysSales([FromHeader] Guid estateId, [FromQuery] Int32 merchantReportingId, [FromQuery] Int32 operatorReportingId, [FromQuery] DateTime comparisonDate, CancellationToken cancellationToken) { - TransactionQueries.TodaysSalesQuery query = new(estateId, merchantReportingId, operatorReportingId, comparisonDate); - Result result = await this.Mediator.Send(query, cancellationToken); - - return ResponseFactory.FromResult(result, (r) => { - TodaysSales response = new TodaysSales - { - ComparisonSalesCount = result.Data.ComparisonSalesCount, - ComparisonSalesValue = result.Data.ComparisonSalesValue, - TodaysSalesCount = result.Data.TodaysSalesCount, - TodaysSalesValue = result.Data.TodaysSalesValue, - ComparisonAverageSalesValue = result.Data.ComparisonAverageSalesValue, - TodaysAverageSalesValue = result.Data.TodaysAverageSalesValue, - }; - - return response; - }); - } - - [HttpGet] - [Route("todayssales/countbyhour")] - public async Task TodaysSalesCountByHour([FromHeader] Guid estateId, - [FromQuery] Int32 merchantReportingId, - [FromQuery] Int32 operatorReportingId, - [FromQuery] DateTime comparisonDate, - CancellationToken cancellationToken) { - TransactionQueries.TodaysSalesCountByHour query = new TransactionQueries.TodaysSalesCountByHour(estateId, merchantReportingId, operatorReportingId, comparisonDate); - var result = await this.Mediator.Send(query, cancellationToken); - - return ResponseFactory.FromResult(result, (r) => { - List response = new List(); - - foreach (Models.TodaysSalesCountByHour todaysSalesCountByHour in r) { - response.Add(new TodaysSalesCountByHour { ComparisonSalesCount = todaysSalesCountByHour.ComparisonSalesCount, Hour = todaysSalesCountByHour.Hour, TodaysSalesCount = todaysSalesCountByHour.TodaysSalesCount, }); - } - - return response; - }); - } - - [HttpGet] - [Route("todayssales/valuebyhour")] - public async Task TodaysSalesValueByHour([FromHeader] Guid estateId, [FromQuery] Int32 merchantReportingId, [FromQuery] Int32 operatorReportingId, [FromQuery] DateTime comparisonDate, CancellationToken cancellationToken){ - - TransactionQueries.TodaysSalesValueByHour query = new TransactionQueries.TodaysSalesValueByHour(estateId, merchantReportingId, operatorReportingId, comparisonDate); - var result = await this.Mediator.Send(query, cancellationToken); - - return ResponseFactory.FromResult(result, (r) => { - List response = new List(); - - foreach (Models.TodaysSalesValueByHour todaysSalesValueByHour in r) - { - response.Add(new TodaysSalesValueByHour - { - ComparisonSalesValue = todaysSalesValueByHour.ComparisonSalesValue, - Hour = todaysSalesValueByHour.Hour, - TodaysSalesValue = todaysSalesValueByHour.TodaysSalesValue - }); - } - - return response; - }); - } - - [HttpGet] - [Route("merchants/lastsale")] - public async Task GetMerchantsByLastSale([FromHeader] Guid estateId, [FromQuery] DateTime startDate, [FromQuery] DateTime endDate, CancellationToken cancellationToken){ - MerchantQueries.GetByLastSaleQuery query = new(estateId, startDate, endDate); - Result> result = await this.Mediator.Send(query, cancellationToken); - - return ResponseFactory.FromResult(result, (r) => { - List response = new(); - - r.ForEach(m => response.Add(new DataTrasferObjects.Merchant - { - MerchantReportingId = m.MerchantReportingId, - MerchantId = m.MerchantId, - EstateReportingId = m.EstateReportingId, - Name = m.Name, - LastSaleDateTime = m.LastSaleDateTime, - CreatedDateTime = m.CreatedDateTime, - LastSale = m.LastSale, - LastStatement = m.LastStatement, - PostCode = m.PostCode, - Reference = m.Reference, - Region = m.Region, - Town = m.Town, - })); - - return response.OrderBy(m => m.Name); - }); - } - - [HttpGet] - [Route("merchantkpis")] - public async Task GetMerchantsTransactionKpis([FromHeader] Guid estateId, CancellationToken cancellationToken){ - - MerchantQueries.GetTransactionKpisQuery query = new MerchantQueries.GetTransactionKpisQuery(estateId); - var result = await this.Mediator.Send(query, cancellationToken); - - return ResponseFactory.FromResult(result, (r) => { - MerchantKpi response = new MerchantKpi - { - MerchantsWithNoSaleInLast7Days = result.Data.MerchantsWithNoSaleInLast7Days, - MerchantsWithNoSaleToday = result.Data.MerchantsWithNoSaleToday, - MerchantsWithSaleInLastHour = result.Data.MerchantsWithSaleInLastHour - }; - - return response; - }); - } - - [HttpGet] - [Route("todaysfailedsales")] - public async Task TodaysFailedSales([FromHeader] Guid estateId, [FromQuery] DateTime comparisonDate, [FromQuery] String responseCode, CancellationToken cancellationToken){ - TransactionQueries.TodaysFailedSales query = new(estateId, comparisonDate, responseCode); - var result = await this.Mediator.Send(query, cancellationToken); - - return ResponseFactory.FromResult(result, (r) => { - TodaysSales response = new TodaysSales - { - ComparisonSalesCount = result.Data.ComparisonSalesCount, - ComparisonSalesValue = result.Data.ComparisonSalesValue, - TodaysSalesCount = result.Data.TodaysSalesCount, - TodaysSalesValue = result.Data.TodaysSalesValue, - ComparisonAverageSalesValue = result.Data.ComparisonAverageSalesValue, - TodaysAverageSalesValue = result.Data.TodaysAverageSalesValue, - }; - - return response; - }); - } - - [HttpGet] - [Route("products/topbottombyvalue")] - public async Task GetTopBottomProductsByValue([FromHeader] Guid estateId, [FromQuery] TopBottom topOrBottom, [FromQuery] Int32 count, CancellationToken cancellationToken){ - - Models.TopBottom modelTopBottom = Enum.Parse(topOrBottom.ToString()); - - IRequest>> query = modelTopBottom switch - { - Models.TopBottom.Bottom => new ProductQueries.GetBottomProductsBySalesValueQuery(estateId, count), - _ => new ProductQueries.GetTopProductsBySalesValueQuery(estateId, count) - }; - Result> result = await this.Mediator.Send(query, cancellationToken); - - return ResponseFactory.FromResult(result, (r) => { - List response = new List(); - r.ForEach(t => { - response.Add(new TopBottomProductData - { - ProductName = t.DimensionName, - SalesValue = t.SalesValue - }); - }); - - return response; - }); - } - - [HttpGet] - [Route("merchants/topbottombyvalue")] - public async Task GetTopBottomMerchantsByValue([FromHeader] Guid estateId, [FromQuery] TopBottom topOrBottom, [FromQuery] Int32 count, CancellationToken cancellationToken){ - Models.TopBottom modelTopBottom = Enum.Parse(topOrBottom.ToString()); - - IRequest>> query = modelTopBottom switch { - Models.TopBottom.Bottom => new MerchantQueries.GetBottomMerchantsBySalesValueQuery(estateId, count), - _ => new MerchantQueries.GetTopMerchantsBySalesValueQuery(estateId, count) - }; - Result> result = await this.Mediator.Send(query, cancellationToken); - - return ResponseFactory.FromResult(result, (r) => { - List response = new List(); - r.ForEach(t => { - response.Add(new TopBottomMerchantData - { - MerchantName = t.DimensionName, - SalesValue = t.SalesValue - }); - }); - - return response; - }); - } - - [HttpGet] - [Route("operators/topbottombyvalue")] - public async Task GetTopBottomOperatorsByValue([FromHeader] Guid estateId, [FromQuery] TopBottom topOrBottom, [FromQuery] Int32 count, CancellationToken cancellationToken){ - Models.TopBottom modelTopBottom = Enum.Parse(topOrBottom.ToString()); - - IRequest>> query = modelTopBottom switch - { - Models.TopBottom.Bottom => new OperatorQueries.GetBottomOperatorsBySalesValueQuery(estateId, count), - _ => new OperatorQueries.GetTopOperatorsBySalesValueQuery(estateId, count) - }; - Result> result = await this.Mediator.Send(query, cancellationToken); - - return ResponseFactory.FromResult(result, (r) => { - List response = new List(); - r.ForEach(t => { - response.Add(new TopBottomOperatorData - { - OperatorName = t.DimensionName, - SalesValue = t.SalesValue - }); - }); - - return response; - }); - } - - [HttpGet] - [Route("merchants/performance")] - public async Task GetMerchantPerformance([FromHeader] Guid estateId, [FromQuery] DateTime comparisonDate, [FromQuery] string? merchantReportingIds, CancellationToken cancellationToken){ - - List merchantIdFilter = new List(); - if (String.IsNullOrEmpty(merchantReportingIds) == false){ - List merchantListStrings = merchantReportingIds.Split(',').ToList(); - foreach (String merchantListString in merchantListStrings){ - merchantIdFilter.Add(Int32.Parse(merchantListString)); - } - } - - MerchantQueries.GetMerchantPerformanceQuery query = new(estateId, comparisonDate, merchantIdFilter); - var result = await this.Mediator.Send(query, cancellationToken); - return ResponseFactory.FromResult(result, (r) => { - TodaysSales response = new TodaysSales - { - ComparisonSalesCount = result.Data.ComparisonSalesCount, - ComparisonSalesValue = result.Data.ComparisonSalesValue, - TodaysSalesCount = result.Data.TodaysSalesCount, - TodaysSalesValue = result.Data.TodaysSalesValue, - ComparisonAverageSalesValue = result.Data.ComparisonAverageSalesValue, - TodaysAverageSalesValue = result.Data.TodaysAverageSalesValue, - }; - - return response; - }); - } - - [HttpGet] - [Route("products/performance")] - public async Task GetProductPerformance([FromHeader] Guid estateId, [FromQuery] DateTime comparisonDate, [FromQuery] string? productReportingIds, CancellationToken cancellationToken) - { - - List productIdFilter = new List(); - if (String.IsNullOrEmpty(productReportingIds) == false) - { - List productListStrings = productReportingIds.Split(',').ToList(); - foreach (String productListString in productListStrings) - { - productIdFilter.Add(Int32.Parse(productListString)); - } - } - - ProductQueries.GetProductPerformanceQuery query = new(estateId, comparisonDate, productIdFilter); - var result = await this.Mediator.Send(query, cancellationToken); - return ResponseFactory.FromResult(result, (r) => { - TodaysSales response = new TodaysSales - { - ComparisonSalesCount = result.Data.ComparisonSalesCount, - ComparisonSalesValue = result.Data.ComparisonSalesValue, - TodaysSalesCount = result.Data.TodaysSalesCount, - TodaysSalesValue = result.Data.TodaysSalesValue, - ComparisonAverageSalesValue = result.Data.ComparisonAverageSalesValue, - TodaysAverageSalesValue = result.Data.TodaysAverageSalesValue, - }; - - return response; - }); - } - - [HttpGet] - [Route("operators/performance")] - public async Task GetOperatorPerformance([FromHeader] Guid estateId, [FromQuery] DateTime comparisonDate, [FromQuery] string? operatorReportingIds, CancellationToken cancellationToken) - { - - List operatorIdFilter = new List(); - if (String.IsNullOrEmpty(operatorReportingIds) == false) - { - List productListStrings = operatorReportingIds.Split(',').ToList(); - foreach (String productListString in productListStrings) - { - operatorIdFilter.Add(Int32.Parse(productListString)); - } - } - - OperatorQueries.GetOperatorPerformanceQuery query = new(estateId, comparisonDate, operatorIdFilter); - Result result = await this.Mediator.Send(query, cancellationToken); - return ResponseFactory.FromResult(result, (r) => { - TodaysSales response = new TodaysSales - { - ComparisonSalesCount = result.Data.ComparisonSalesCount, - ComparisonSalesValue = result.Data.ComparisonSalesValue, - TodaysSalesCount = result.Data.TodaysSalesCount, - TodaysSalesValue = result.Data.TodaysSalesValue, - ComparisonAverageSalesValue = result.Data.ComparisonAverageSalesValue, - TodaysAverageSalesValue = result.Data.TodaysAverageSalesValue, - }; - - return response; - }); - } - - [HttpGet] - [Route("search")] - public async Task TransactionSearch([FromHeader] Guid estateId, [FromBody] TransactionSearchRequest request, - [FromQuery] int? page, [FromQuery] int? pageSize, - [FromQuery] SortField? sortField, [FromQuery] SortDirection? sortDirection, - CancellationToken cancellationToken){ - - PagingRequest pagingRequest = CreatePagingRequest(page, pageSize); - Models.SortingRequest sortingRequest = CreateSortingRequest(sortField, sortDirection); - // TODO: Convert the request - Models.TransactionSearchRequest searchModel = new Models.TransactionSearchRequest{ - AuthCode = request.AuthCode, - Merchants = request.Merchants, - Operators = request.Operators, - QueryDate = request.QueryDate, - ResponseCode = request.ResponseCode, - TransactionNumber = request.TransactionNumber, - }; - - if (request.ValueRange != null){ - searchModel.ValueRange = new Models.ValueRange{ - EndValue = request.ValueRange.EndValue, - StartValue = request.ValueRange.StartValue, - }; - } - - TransactionQueries.TransactionSearchQuery query = new(estateId, searchModel, pagingRequest, sortingRequest); - var result = await this.Mediator.Send(query, cancellationToken); - - return ResponseFactory.FromResult(result, (r) => { - List response = new List(); - - r.ForEach(t => { - response.Add(new TransactionResult - { - MerchantReportingId = t.MerchantReportingId, - ResponseCode = t.ResponseCode, - Product = t.Product, - TransactionReportingId = t.TransactionReportingId, - TransactionSource = t.TransactionSource, - IsAuthorised = t.IsAuthorised, - MerchantName = t.MerchantName, - OperatorName = t.OperatorName, - OperatorReportingId = t.OperatorReportingId, - ProductReportingId = t.ProductReportingId, - ResponseMessage = t.ResponseMessage, - TransactionDateTime = t.TransactionDateTime, - TransactionId = t.TransactionId, - TransactionAmount = t.TransactionAmount - }); - }); - - return response; - }); - } - - private static PagingRequest CreatePagingRequest(int? page, int? pageSize) => new(page, pageSize); - - private static SortingRequest CreateSortingRequest(SortField? sortField, SortDirection? sortDirection) - { - Models.SortField modelSortField = Models.SortField.TransactionAmount; - Models.SortDirection modelSortDirection = Models.SortDirection.Ascending; - if (sortField != null) - { - modelSortField = Enum.Parse(sortField.ToString(), true); - } - if (sortDirection != null) - { - modelSortDirection = Enum.Parse(sortDirection.ToString(), true); - } - - return new Models.SortingRequest(modelSortField, modelSortDirection); - } - } - - public enum TopBottom{ - Top = 0, - - Bottom = 1 - } - - -} diff --git a/EstateReportingAPI/Endpoints/DimensionsEndpoints.cs b/EstateReportingAPI/Endpoints/DimensionsEndpoints.cs new file mode 100644 index 0000000..9e09b99 --- /dev/null +++ b/EstateReportingAPI/Endpoints/DimensionsEndpoints.cs @@ -0,0 +1,28 @@ +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/FactSettlementsEndpoints.cs b/EstateReportingAPI/Endpoints/FactSettlementsEndpoints.cs new file mode 100644 index 0000000..bbfd7dc --- /dev/null +++ b/EstateReportingAPI/Endpoints/FactSettlementsEndpoints.cs @@ -0,0 +1,24 @@ +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 new file mode 100644 index 0000000..fa9e0e6 --- /dev/null +++ b/EstateReportingAPI/Endpoints/FactTransactionsEndpoints.cs @@ -0,0 +1,45 @@ +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/Handlers/DimensionsHandlers.cs b/EstateReportingAPI/Handlers/DimensionsHandlers.cs new file mode 100644 index 0000000..6a740ed --- /dev/null +++ b/EstateReportingAPI/Handlers/DimensionsHandlers.cs @@ -0,0 +1,151 @@ +using EstateReportingAPI.BusinessLogic.Queries; +using EstateReportingAPI.DataTrasferObjects; +using MediatR; +using Microsoft.AspNetCore.Mvc; +using Shared.Results.Web; + +namespace EstateReportingAPI.Handlers; + +public static class DimensionsHandlers +{ + public static async Task GetCalendarYears( + [FromHeader] Guid estateId, + IMediator mediator, + CancellationToken cancellationToken) + { + var query = new CalendarQueries.GetYearsQuery(estateId); + var result = await mediator.Send(query, cancellationToken); + + return ResponseFactory.FromResult(result, r => + r.Select(y => new CalendarYear { Year = y }).ToList() + ); + } + + public static async Task GetCalendarDates( + [FromHeader] Guid estateId, + [FromRoute] int year, + IMediator mediator, + CancellationToken cancellationToken) + { + var query = new CalendarQueries.GetAllDatesQuery(estateId); + var result = await mediator.Send(query, cancellationToken); + + return ResponseFactory.FromResult(result, r => + r.Select(d => new CalendarDate + { + Year = d.Year, + Date = d.Date, + DayOfWeek = d.DayOfWeek, + DayOfWeekNumber = d.DayOfWeekNumber, + DayOfWeekShort = d.DayOfWeekShort, + MonthName = d.MonthNameLong, + MonthNameShort = d.MonthNameShort, + MonthNumber = d.MonthNumber, + WeekNumber = d.WeekNumber ?? 0, + WeekNumberString = d.WeekNumberString, + YearWeekNumber = d.YearWeekNumber + }).ToList() + ); + } + + public static async Task GetCalendarComparisonDates( + [FromHeader] Guid estateId, + IMediator mediator, + CancellationToken cancellationToken) + { + var query = new CalendarQueries.GetComparisonDatesQuery(estateId); + var result = await mediator.Send(query, cancellationToken); + + return ResponseFactory.FromResult(result, r => + { + var response = new List + { + 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 (var 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); + }); + } + + public static async Task GetMerchants( + [FromHeader] Guid estateId, + IMediator mediator, + CancellationToken cancellationToken) + { + var query = new MerchantQueries.GetMerchantsQuery(estateId); + var result = await mediator.Send(query, cancellationToken); + + return ResponseFactory.FromResult(result, r => + r.Select(m => new Merchant + { + MerchantReportingId = m.MerchantReportingId, + MerchantId = m.MerchantId, + EstateReportingId = m.EstateReportingId, + Name = m.Name, + LastSaleDateTime = m.LastSaleDateTime, + CreatedDateTime = m.CreatedDateTime, + LastSale = m.LastSale, + LastStatement = m.LastStatement, + PostCode = m.PostCode, + Reference = m.Reference, + Region = m.Region, + Town = m.Town + }) + .OrderBy(m => m.Name) + .ToList() + ); + } + + public static async Task GetOperators( + [FromHeader] Guid estateId, + IMediator mediator, + CancellationToken cancellationToken) + { + var query = new OperatorQueries.GetOperatorsQuery(estateId); + var result = await mediator.Send(query, cancellationToken); + + return ResponseFactory.FromResult(result, r => + r.Select(o => new Operator + { + EstateReportingId = o.EstateReportingId, + Name = o.Name, + OperatorId = o.OperatorId, + OperatorReportingId = o.OperatorReportingId + }) + .OrderBy(o => o.Name) + .ToList() + ); + } + + public static async Task GetResponseCodes( + [FromHeader] Guid estateId, + IMediator mediator, + CancellationToken cancellationToken) + { + var query = new ResponseCodeQueries.GetResponseCodesQuery(estateId); + var result = await mediator.Send(query, cancellationToken); + + return ResponseFactory.FromResult(result, r => + r.Select(o => new ResponseCode + { + Code = o.Code, + Description = o.Description + }) + .OrderBy(r => r.Code) + .ToList() + ); + } +} \ No newline at end of file diff --git a/EstateReportingAPI/Handlers/FactSettlementsHandlers.cs b/EstateReportingAPI/Handlers/FactSettlementsHandlers.cs new file mode 100644 index 0000000..00f5f02 --- /dev/null +++ b/EstateReportingAPI/Handlers/FactSettlementsHandlers.cs @@ -0,0 +1,112 @@ +using EstateReportingAPI.BusinessLogic.Queries; +using EstateReportingAPI.DataTransferObjects; +using MediatR; +using Microsoft.AspNetCore.Mvc; +using Shared.Results.Web; + +namespace EstateReportingAPI.Handlers; + +public static class FactSettlementsHandlers +{ + public static async Task TodaysSettlement([FromHeader] Guid estateId, + [FromQuery] int? merchantReportingId, + [FromQuery] int? operatorReportingId, + [FromQuery] DateTime comparisonDate, + IMediator mediator, + CancellationToken cancellationToken) + { + var query = new SettlementQueries.GetTodaysSettlementQuery( + estateId, merchantReportingId.GetValueOrDefault(), operatorReportingId.GetValueOrDefault(), comparisonDate); + + var result = await mediator.Send(query, cancellationToken); + + return ResponseFactory.FromResult(result, r => new TodaysSettlement + { + ComparisonSettlementCount = r.ComparisonSettlementCount, + ComparisonSettlementValue = r.ComparisonSettlementValue, + ComparisonPendingSettlementCount = r.ComparisonPendingSettlementCount, + ComparisonPendingSettlementValue = r.ComparisonPendingSettlementValue, + TodaysSettlementCount = r.TodaysSettlementCount, + TodaysSettlementValue = r.TodaysSettlementValue, + TodaysPendingSettlementCount = r.TodaysPendingSettlementCount, + TodaysPendingSettlementValue = r.TodaysPendingSettlementValue + }); + } + + public static async Task LastSettlement([FromHeader] Guid estateId, + IMediator mediator, + CancellationToken cancellationToken) + { + var query = new SettlementQueries.GetLastSettlementQuery(estateId); + var result = await mediator.Send(query, cancellationToken); + + return ResponseFactory.FromResult(result, r => new LastSettlement + { + SalesCount = r.SalesCount, + FeesValue = r.FeesValue, + SalesValue = r.SalesValue, + SettlementDate = r.SettlementDate + }); + } + + public static async Task GetUnsettledFees([FromHeader] Guid estateId, + [FromQuery] DateTime startDate, + [FromQuery] DateTime endDate, + [FromQuery] string? merchantIds, + [FromQuery] string? operatorIds, + [FromQuery] string? productIds, + [FromQuery] GroupByOption? groupByOption, + IMediator mediator, + CancellationToken cancellationToken) + { + var merchantIdFilter = ParseIds(merchantIds); + var operatorIdFilter = ParseIds(operatorIds); + var productIdFilter = ParseIds(productIds); + + var groupByOptionConverted = ConvertGroupByOption(groupByOption.GetValueOrDefault()); + + var query = new SettlementQueries.GetUnsettledFeesQuery( + estateId, + startDate, + endDate, + merchantIdFilter, + operatorIdFilter, + productIdFilter, + groupByOptionConverted); + + var result = await mediator.Send(query, cancellationToken); + + return ResponseFactory.FromResult(result, r => + { + var response = r.Select(u => new DataTransferObjects.UnsettledFee + { + DimensionName = u.DimensionName, + FeesCount = u.FeesCount, + FeesValue = u.FeesValue + }).ToList(); + + return response; + }); + } + + // Helper methods + private static List ParseIds(string? csv) + { + if (string.IsNullOrWhiteSpace(csv)) + return []; + + return csv.Split(',') + .Select(s => int.TryParse(s, out var id) ? id : (int?)null) + .Where(id => id.HasValue) + .Select(id => id!.Value) + .ToList(); + } + + private static Models.GroupByOption ConvertGroupByOption(GroupByOption groupByOption) + => groupByOption switch + { + GroupByOption.Merchant => Models.GroupByOption.Merchant, + GroupByOption.Product => Models.GroupByOption.Product, + _ => Models.GroupByOption.Operator + }; +} \ No newline at end of file diff --git a/EstateReportingAPI/Handlers/FactTransactionsHandler.cs b/EstateReportingAPI/Handlers/FactTransactionsHandler.cs new file mode 100644 index 0000000..bc837d1 --- /dev/null +++ b/EstateReportingAPI/Handlers/FactTransactionsHandler.cs @@ -0,0 +1,334 @@ +using EstateReportingAPI.BusinessLogic.Queries; +using EstateReportingAPI.DataTransferObjects; +using EstateReportingAPI.Models; +using MediatR; +using Microsoft.AspNetCore.Mvc; +using Shared.Results.Web; +using SimpleResults; +using GroupByOption = EstateReportingAPI.DataTransferObjects.GroupByOption; +using LastSettlement = EstateReportingAPI.DataTransferObjects.LastSettlement; +using Merchant = EstateReportingAPI.DataTrasferObjects.Merchant; +using MerchantKpi = EstateReportingAPI.DataTransferObjects.MerchantKpi; +using Operator = EstateReportingAPI.DataTrasferObjects.Operator; +using ResponseCode = EstateReportingAPI.DataTrasferObjects.ResponseCode; +using SortDirection = EstateReportingAPI.DataTransferObjects.SortDirection; +using SortField = EstateReportingAPI.DataTransferObjects.SortField; +using TodaysSales = EstateReportingAPI.DataTransferObjects.TodaysSales; +using TodaysSalesCountByHour = EstateReportingAPI.DataTransferObjects.TodaysSalesCountByHour; +using TodaysSalesValueByHour = EstateReportingAPI.DataTransferObjects.TodaysSalesValueByHour; +using TodaysSettlement = EstateReportingAPI.DataTransferObjects.TodaysSettlement; +using TopBottom = EstateReportingAPI.DataTransferObjects.TopBottom; +using TransactionResult = EstateReportingAPI.DataTransferObjects.TransactionResult; +using TransactionSearchRequest = EstateReportingAPI.DataTransferObjects.TransactionSearchRequest; + +namespace EstateReportingAPI.Handlers +{ + public static class FactTransactionsHandler { + 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 TodaysSalesCountByHour([FromHeader] Guid estateId, + [FromQuery] int? merchantReportingId, + [FromQuery] int? operatorReportingId, + [FromQuery] DateTime comparisonDate, + IMediator mediator, + CancellationToken cancellationToken) { + var query = new TransactionQueries.TodaysSalesCountByHour(estateId, merchantReportingId.GetValueOrDefault(), operatorReportingId.GetValueOrDefault(), comparisonDate); + var result = await mediator.Send(query, cancellationToken); + + return ResponseFactory.FromResult(result, r => { + var response = r.Select(t => new TodaysSalesCountByHour { ComparisonSalesCount = t.ComparisonSalesCount, Hour = t.Hour, TodaysSalesCount = t.TodaysSalesCount }).ToList(); + + return response; + }); + } + + public static async Task TodaysSalesValueByHour([FromHeader] Guid estateId, + [FromQuery] int? merchantReportingId, + [FromQuery] int? operatorReportingId, + [FromQuery] DateTime comparisonDate, + IMediator mediator, + CancellationToken cancellationToken) { + var query = new TransactionQueries.TodaysSalesValueByHour(estateId, merchantReportingId.GetValueOrDefault(), operatorReportingId.GetValueOrDefault(), comparisonDate); + var result = await mediator.Send(query, cancellationToken); + + return ResponseFactory.FromResult(result, r => { + var response = r.Select(t => new TodaysSalesValueByHour { ComparisonSalesValue = t.ComparisonSalesValue, Hour = t.Hour, TodaysSalesValue = t.TodaysSalesValue }).ToList(); + + return response; + }); + } + + public static async Task GetMerchantsByLastSale([FromHeader] Guid estateId, + [FromQuery] DateTime startDate, + [FromQuery] DateTime endDate, + IMediator mediator, + CancellationToken cancellationToken) { + var query = new MerchantQueries.GetByLastSaleQuery(estateId, startDate, endDate); + var result = await mediator.Send(query, cancellationToken); + + return ResponseFactory.FromResult(result, r => { + var response = r.Select(m => new DataTrasferObjects.Merchant { + MerchantReportingId = m.MerchantReportingId, + MerchantId = m.MerchantId, + EstateReportingId = m.EstateReportingId, + Name = m.Name, + LastSaleDateTime = m.LastSaleDateTime, + CreatedDateTime = m.CreatedDateTime, + LastSale = m.LastSale, + LastStatement = m.LastStatement, + PostCode = m.PostCode, + Reference = m.Reference, + Region = m.Region, + Town = m.Town, + }).OrderBy(x => x.Name).ToList(); + + return response; + }); + } + + public static async Task GetMerchantsTransactionKpis([FromHeader] Guid estateId, + IMediator mediator, + CancellationToken cancellationToken) { + var query = new MerchantQueries.GetTransactionKpisQuery(estateId); + var result = await mediator.Send(query, cancellationToken); + + return ResponseFactory.FromResult(result, r => new MerchantKpi { MerchantsWithNoSaleInLast7Days = r.MerchantsWithNoSaleInLast7Days, MerchantsWithNoSaleToday = r.MerchantsWithNoSaleToday, MerchantsWithSaleInLastHour = r.MerchantsWithSaleInLastHour }); + } + + 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, + }); + } + + public static async Task GetTopBottomProductsByValue([FromHeader] Guid estateId, + [FromQuery] TopBottom topOrBottom, + [FromQuery] int count, + IMediator mediator, + CancellationToken cancellationToken) { + var modelTopBottom = Enum.Parse(topOrBottom.ToString()); + + IRequest>> query = modelTopBottom switch { + Models.TopBottom.Bottom => new ProductQueries.GetBottomProductsBySalesValueQuery(estateId, count), + _ => new ProductQueries.GetTopProductsBySalesValueQuery(estateId, count) + }; + + var result = await mediator.Send(query, cancellationToken); + + return ResponseFactory.FromResult(result, r => { + var response = r.Select(t => new TopBottomProductData { ProductName = t.DimensionName, SalesValue = t.SalesValue }).ToList(); + + return response; + }); + } + + public static async Task GetTopBottomMerchantsByValue([FromHeader] Guid estateId, + [FromQuery] TopBottom topOrBottom, + [FromQuery] int count, + IMediator mediator, + CancellationToken cancellationToken) { + var modelTopBottom = Enum.Parse(topOrBottom.ToString()); + + IRequest>> query = modelTopBottom switch { + Models.TopBottom.Bottom => new MerchantQueries.GetBottomMerchantsBySalesValueQuery(estateId, count), + _ => new MerchantQueries.GetTopMerchantsBySalesValueQuery(estateId, count) + }; + + var result = await mediator.Send(query, cancellationToken); + + return ResponseFactory.FromResult(result, r => { + var response = r.Select(t => new TopBottomMerchantData { MerchantName = t.DimensionName, SalesValue = t.SalesValue }).ToList(); + + return response; + }); + } + + public static async Task GetTopBottomOperatorsByValue([FromHeader] Guid estateId, + [FromQuery] TopBottom topOrBottom, + [FromQuery] int count, + IMediator mediator, + CancellationToken cancellationToken) { + var modelTopBottom = Enum.Parse(topOrBottom.ToString()); + + IRequest>> query = modelTopBottom switch { + Models.TopBottom.Bottom => new OperatorQueries.GetBottomOperatorsBySalesValueQuery(estateId, count), + _ => new OperatorQueries.GetTopOperatorsBySalesValueQuery(estateId, count) + }; + + var result = await mediator.Send(query, cancellationToken); + + return ResponseFactory.FromResult(result, r => { + var response = r.Select(t => new TopBottomOperatorData { OperatorName = t.DimensionName, SalesValue = t.SalesValue }).ToList(); + + return response; + }); + } + + public static async Task GetMerchantPerformance([FromHeader] Guid estateId, + [FromQuery] DateTime comparisonDate, + [FromQuery] string? merchantReportingIds, + IMediator mediator, + CancellationToken cancellationToken) { + var merchantIdFilter = ParseCsvIds(merchantReportingIds); + + var query = new MerchantQueries.GetMerchantPerformanceQuery(estateId, comparisonDate, merchantIdFilter); + 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 GetProductPerformance([FromHeader] Guid estateId, + [FromQuery] DateTime comparisonDate, + [FromQuery] string? productReportingIds, + IMediator mediator, + CancellationToken cancellationToken) { + var productIdFilter = ParseCsvIds(productReportingIds); + + var query = new ProductQueries.GetProductPerformanceQuery(estateId, comparisonDate, productIdFilter); + 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 GetOperatorPerformance([FromHeader] Guid estateId, + [FromQuery] DateTime comparisonDate, + [FromQuery] string? operatorReportingIds, + IMediator mediator, + CancellationToken cancellationToken) { + var operatorIdFilter = ParseCsvIds(operatorReportingIds); + + var query = new OperatorQueries.GetOperatorPerformanceQuery(estateId, comparisonDate, operatorIdFilter); + 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 TransactionSearch([FromHeader] Guid estateId, + [FromBody] TransactionSearchRequest request, + [FromQuery] int? page, + [FromQuery] int? pageSize, + [FromQuery] SortField? sortField, + [FromQuery] SortDirection? sortDirection, + IMediator mediator, + CancellationToken cancellationToken) { + var paging = CreatePagingRequest(page, pageSize); + var sorting = CreateSortingRequest(sortField, sortDirection); + + var searchModel = new Models.TransactionSearchRequest { + AuthCode = request.AuthCode, + Merchants = request.Merchants, + Operators = request.Operators, + QueryDate = request.QueryDate, + ResponseCode = request.ResponseCode, + TransactionNumber = request.TransactionNumber, + ValueRange = request.ValueRange == null ? null : new Models.ValueRange { StartValue = request.ValueRange.StartValue, EndValue = request.ValueRange.EndValue } + }; + + var query = new TransactionQueries.TransactionSearchQuery(estateId, searchModel, paging, sorting); + var result = await mediator.Send(query, cancellationToken); + + return ResponseFactory.FromResult(result, r => { + var response = r.Select(t => new TransactionResult { + MerchantReportingId = t.MerchantReportingId, + ResponseCode = t.ResponseCode, + Product = t.Product, + TransactionReportingId = t.TransactionReportingId, + TransactionSource = t.TransactionSource, + IsAuthorised = t.IsAuthorised, + MerchantName = t.MerchantName, + OperatorName = t.OperatorName, + OperatorReportingId = t.OperatorReportingId, + ProductReportingId = t.ProductReportingId, + ResponseMessage = t.ResponseMessage, + TransactionDateTime = t.TransactionDateTime, + TransactionId = t.TransactionId, + TransactionAmount = t.TransactionAmount + }).ToList(); + + return response; + }); + } + + private static PagingRequest CreatePagingRequest(int? page, int? pageSize) => new PagingRequest(page, pageSize); + + private static Models.SortingRequest CreateSortingRequest(SortField? sortField, SortDirection? sortDirection) + { + Models.SortField modelSortField = Models.SortField.TransactionAmount; + Models.SortDirection modelSortDirection = Models.SortDirection.Ascending; + + if (sortField != null) + { + modelSortField = Enum.Parse(sortField.ToString(), true); + } + if (sortDirection != null) + { + modelSortDirection = Enum.Parse(sortDirection.ToString(), true); + } + + return new Models.SortingRequest(modelSortField, modelSortDirection); + } + + private static List ParseCsvIds(string? csv) + { + if (string.IsNullOrWhiteSpace(csv)) + return new List(); + + return csv.Split(',', StringSplitOptions.RemoveEmptyEntries) + .Select(s => s.Trim()) + .Where(s => int.TryParse(s, out _)) + .Select(int.Parse) + .ToList(); + } + } + +} diff --git a/EstateReportingAPI/Startup.cs b/EstateReportingAPI/Startup.cs index 670637d..3491d99 100644 --- a/EstateReportingAPI/Startup.cs +++ b/EstateReportingAPI/Startup.cs @@ -7,6 +7,7 @@ using Shared.General; using Shared.Logger; using System.Diagnostics.CodeAnalysis; +using EstateReportingAPI.Endpoints; namespace EstateReportingAPI { @@ -71,7 +72,11 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerF app.UseEndpoints(endpoints => { - endpoints.MapControllers(); + endpoints.MapDimensionsEndpoints(); + endpoints.MapFactSettlementsEndpoints(); + endpoints.MapFactTransactionEndpoints(); + + //endpoints.MapControllers(); endpoints.MapHealthChecks("health", new HealthCheckOptions() { Predicate = _ => true,