Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions EstateReportingAPI.BusinessLogic/IReportingManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ public interface IReportingManager{
Task<LastSettlement> GetLastSettlement(Guid estateId, CancellationToken cancellationToken);
Task<List<Merchant>> GetMerchants(Guid estateId, CancellationToken cancellationToken);
Task<MerchantKpi> GetMerchantsTransactionKpis(Guid estateId, CancellationToken cancellationToken);
Task<List<Merchant>> GetMerchantsByLastSale(Guid estateId, DateTime startDateTime, DateTime endDateTime, CancellationToken cancellationToken);
Task<List<Operator>> GetOperators(Guid estateId, CancellationToken cancellationToken);
Task<List<ResponseCode>> GetResponseCodes(Guid estateId, CancellationToken cancellationToken);
Task<TodaysSales> GetTodaysFailedSales(Guid estateId, DateTime comparisonDate, String responseCode, CancellationToken cancellationToken);
Expand Down
49 changes: 49 additions & 0 deletions EstateReportingAPI.BusinessLogic/ReportingManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,55 @@ public async Task<MerchantKpi> GetMerchantsTransactionKpis(Guid estateId, Cancel
return response;
}

public async Task<List<Merchant>> GetMerchantsByLastSale(Guid estateId, DateTime startDateTime, DateTime endDateTime, CancellationToken cancellationToken){
EstateManagementGenericContext? context = await this.ContextFactory.GetContext(estateId, ReportingManager.ConnectionStringIdentifier, cancellationToken);

List<Merchant> response = new();

var merchants = await context.Merchants
.Where(m => m.LastSaleDateTime >= startDateTime && m.LastSaleDateTime <= endDateTime)
.Select(m => new
{
MerchantReportingId = m.MerchantReportingId,
EstateReportingId = m.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.MerchantReportingId == m.MerchantReportingId)
.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<List<ResponseCode>> GetResponseCodes(Guid estateId, CancellationToken cancellationToken){
EstateManagementGenericContext? context = await this.ContextFactory.GetContext(estateId, ReportingManager.ConnectionStringIdentifier, cancellationToken);
List<ResponseCode> response = new List<ResponseCode>();
Expand Down
31 changes: 31 additions & 0 deletions EstateReportingAPI.Client/EstateReportingApiClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,37 @@ public async Task<TodaysSales> GetProductPerformance(String accessToken, Guid es
return response;
}

public async Task<List<Merchant>> GetMerchantsByLastSaleDate(String accessToken, Guid estateId, DateTime startDate, DateTime endDate, CancellationToken cancellationToken){
List<Merchant> response = new List<Merchant>();

String requestUri = this.BuildRequestUrl($"/api/facts/transactions/merchants/lastsale?startDate={startDate:yyyy-MM-dd HH:mm:ss}&enddate={endDate:yyyy-MM-dd HH:mm:ss}");

try
{
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, requestUri);
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
request.Headers.Add("EstateId", estateId.ToString());

// Make the Http Call here
HttpResponseMessage httpResponse = await this.HttpClient.SendAsync(request, cancellationToken);

// Process the response
String content = await this.HandleResponse(httpResponse, cancellationToken);

// call was successful so now deserialise the body to the response object
response = JsonConvert.DeserializeObject<List<Merchant>>(content);
}
catch (Exception ex)
{
// An exception has occurred, add some additional information to the message
Exception exception = new Exception($"Error getting merchant by last sale date estate {estateId}.", ex);

throw exception;
}

return response;
}

public async Task<TodaysSales> GetOperatorPerformance(String accessToken, Guid estateId, DateTime comparisonDate, List<Int32> operatorIds, CancellationToken cancellationToken){
// Serialize the integer array into a comma-separated string
string serializedArray = string.Join(",", operatorIds);
Expand Down
29 changes: 16 additions & 13 deletions EstateReportingAPI.Client/IEstateReportingApiClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,29 +12,32 @@ public interface IEstateReportingApiClient{
Task<List<CalendarDate>> GetCalendarDates(String accessToken, Guid estateId, Int32 year, CancellationToken cancellationToken);
Task<List<CalendarYear>> GetCalendarYears(String accessToken, Guid estateId, CancellationToken cancellationToken);
Task<List<ComparisonDate>> GetComparisonDates(String accessToken, Guid estateId, CancellationToken cancellationToken);
Task<TodaysSales> GetTodaysSales(String accessToken, Guid estateId, Guid? merchantId, Guid? operatorId, DateTime comparisonDate, CancellationToken cancellationToken);
Task<List<TodaysSalesCountByHour>> GetTodaysSalesCountByHour(String accessToken, Guid estateId, Guid? merchantId, Guid? operatorId, DateTime comparisonDate, CancellationToken cancellationToken);
Task<List<TodaysSalesValueByHour>> GetTodaysSalesValueByHour(String accessToken, Guid estateId, Guid? merchantId, Guid? operatorId, DateTime comparisonDate, CancellationToken cancellationToken);
Task<TodaysSettlement> GetTodaysSettlement(String accessToken, Guid estateId, Guid? merchantId, Guid? operatorId, DateTime comparisonDate, CancellationToken cancellationToken);
Task<LastSettlement> GetLastSettlement(String accessToken, Guid estateId, CancellationToken cancellationToken);
Task<MerchantKpi> GetMerchantKpi(String accessToken, Guid estateId, CancellationToken cancellationToken);
Task<TodaysSales> GetMerchantPerformance(String accessToken, Guid estateId, DateTime comparisonDate, List<Int32> merchantIds, CancellationToken cancellationToken);
Task<List<Merchant>> GetMerchants(String accessToken, Guid estateId, CancellationToken cancellationToken);

Task<TodaysSales> GetTodaysFailedSales(String accessToken, Guid estateId, Guid? merchantId, Guid? operatorId, String responseCode, DateTime comparisonDate, CancellationToken cancellationToken);
Task<List<Merchant>> GetMerchantsByLastSaleDate(String accessToken, Guid estateId, DateTime startDate, DateTime endDate, CancellationToken cancellationToken);

Task<List<TopBottomOperatorData>> GetTopBottomOperatorData(String accessToken, Guid estateId, TopBottom topBottom, Int32 resultCount, CancellationToken cancellationToken);
Task<List<TopBottomMerchantData>> GetTopBottomMerchantData(String accessToken, Guid estateId, TopBottom topBottom, Int32 resultCount, CancellationToken cancellationToken);
Task<List<TopBottomProductData>> GetTopBottomProductData(String accessToken, Guid estateId, TopBottom topBottom, Int32 resultCount, CancellationToken cancellationToken);
Task<List<Merchant>> GetMerchants(String accessToken, Guid estateId, CancellationToken cancellationToken);
Task<TodaysSales> GetOperatorPerformance(String accessToken, Guid estateId, DateTime comparisonDate, List<Int32> operatorIds, CancellationToken cancellationToken);
Task<List<Operator>> GetOperators(String accessToken, Guid estateId, CancellationToken cancellationToken);
Task<LastSettlement> GetLastSettlement(String accessToken, Guid estateId,CancellationToken cancellationToken);

Task<TodaysSales> GetProductPerformance(String accessToken, Guid estateId, DateTime comparisonDate, List<Int32> productIds, CancellationToken cancellationToken);

Task<List<ResponseCode>> GetResponseCodes(String accessToken, Guid estateId, CancellationToken cancellationToken);
Task<TodaysSales> GetMerchantPerformance(String accessToken, Guid estateId, DateTime comparisonDate, List<Int32> merchantIds,CancellationToken cancellationToken);

Task<TodaysSales> GetProductPerformance(String accessToken, Guid estateId, DateTime comparisonDate, List<Int32> productIds, CancellationToken cancellationToken);
Task<TodaysSales> GetTodaysFailedSales(String accessToken, Guid estateId, Guid? merchantId, Guid? operatorId, String responseCode, DateTime comparisonDate, CancellationToken cancellationToken);
Task<TodaysSales> GetTodaysSales(String accessToken, Guid estateId, Guid? merchantId, Guid? operatorId, DateTime comparisonDate, CancellationToken cancellationToken);
Task<List<TodaysSalesCountByHour>> GetTodaysSalesCountByHour(String accessToken, Guid estateId, Guid? merchantId, Guid? operatorId, DateTime comparisonDate, CancellationToken cancellationToken);
Task<List<TodaysSalesValueByHour>> GetTodaysSalesValueByHour(String accessToken, Guid estateId, Guid? merchantId, Guid? operatorId, DateTime comparisonDate, CancellationToken cancellationToken);
Task<TodaysSettlement> GetTodaysSettlement(String accessToken, Guid estateId, Guid? merchantId, Guid? operatorId, DateTime comparisonDate, CancellationToken cancellationToken);
Task<List<TopBottomMerchantData>> GetTopBottomMerchantData(String accessToken, Guid estateId, TopBottom topBottom, Int32 resultCount, CancellationToken cancellationToken);

Task<TodaysSales> GetOperatorPerformance(String accessToken, Guid estateId, DateTime comparisonDate, List<Int32> operatorIds, CancellationToken cancellationToken);
Task<List<TopBottomOperatorData>> GetTopBottomOperatorData(String accessToken, Guid estateId, TopBottom topBottom, Int32 resultCount, CancellationToken cancellationToken);
Task<List<TopBottomProductData>> GetTopBottomProductData(String accessToken, Guid estateId, TopBottom topBottom, Int32 resultCount, CancellationToken cancellationToken);

Task<List<TransactionResult>> TransactionSearch(String accessToken, Guid estateId, TransactionSearchRequest searchRequest, Int32? page, Int32? pageSize, SortField? sortField, SortDirection? sortDirection, CancellationToken cancellationToken);

#endregion
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>

<DebugType>Full</DebugType>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>
Expand Down
102 changes: 102 additions & 0 deletions EstateReportingAPI.IntegrationTests/FactTransactionsControllerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@
using EstateManagement.Database.Contexts;
using EstateManagement.Database.Entities;
using EstateReportingAPI.DataTransferObjects;
using Microsoft.OpenApi.Services;
using Newtonsoft.Json;
using Shouldly;
using Xunit;
using Merchant = DataTrasferObjects.Merchant;
using SortDirection = DataTransferObjects.SortDirection;

public class FactTransactionsControllerTests : ControllerTestsBase
Expand Down Expand Up @@ -1926,5 +1928,105 @@ public async Task FactTransactionsController_TransactionSearch_SortingTest_Trans
searchResult[1].TransactionAmount.ShouldBe(200);
searchResult[2].TransactionAmount.ShouldBe(100);
}

[Theory]
[InlineData(ClientType.Api)]
[InlineData(ClientType.Direct)]
public async Task FactTransactionsControllerController_GetMerchantsByLastDaleDate_MerchantsReturned(ClientType clientType)
{
DateTime todaysDateTime = DateTime.Now;

await ClearStandingData();

// Last Hour
await helper.AddMerchant("Test Estate", "Merchant 1", todaysDateTime.AddMinutes(-10));
await helper.AddMerchant("Test Estate", "Merchant 2", todaysDateTime.AddMinutes(-10));
await helper.AddMerchant("Test Estate", "Merchant 3", todaysDateTime.AddMinutes(-10));
await helper.AddMerchant("Test Estate", "Merchant 4", todaysDateTime.AddMinutes(-10));

// Yesterday
await helper.AddMerchant("Test Estate", "Merchant 5", todaysDateTime.AddDays(-1));
await helper.AddMerchant("Test Estate", "Merchant 6", todaysDateTime.AddDays(-1));
await helper.AddMerchant("Test Estate", "Merchant 7", todaysDateTime.AddDays(-1));
await helper.AddMerchant("Test Estate", "Merchant 8", todaysDateTime.AddDays(-1));
await helper.AddMerchant("Test Estate", "Merchant 9", todaysDateTime.AddDays(-1));
await helper.AddMerchant("Test Estate", "Merchant 10", todaysDateTime.AddDays(-1));

// 5 days ago
await helper.AddMerchant("Test Estate", "Merchant 11", todaysDateTime.AddDays(-5));
await helper.AddMerchant("Test Estate", "Merchant 12", todaysDateTime.AddDays(-5));
await helper.AddMerchant("Test Estate", "Merchant 13", todaysDateTime.AddDays(-5));

// 10 Days Ago
await helper.AddMerchant("Test Estate", "Merchant 14", todaysDateTime.AddDays(-10));
await helper.AddMerchant("Test Estate", "Merchant 15", todaysDateTime.AddDays(-10));
await helper.AddMerchant("Test Estate", "Merchant 16", todaysDateTime.AddDays(-10));
await helper.AddMerchant("Test Estate", "Merchant 17", todaysDateTime.AddDays(-10));
await helper.AddMerchant("Test Estate", "Merchant 18", todaysDateTime.AddDays(-10));

DateTime startDate = DateTime.Now;
DateTime endDate = DateTime.Now;

Func<Task<List<Merchant>?>> asyncFunction = async () =>
{
List<Merchant>? result = clientType switch
{
ClientType.Api => await this.ApiClient.GetMerchantsByLastSaleDate(String.Empty, Guid.NewGuid(), startDate,endDate, CancellationToken.None),
_ => await CreateAndSendHttpRequestMessage<List<Merchant>>($"api/facts/transactions/merchants/lastsale?startDate={startDate:yyyy-MM-dd HH:mm:ss}&enddate={endDate:yyyy-MM-dd HH:mm:ss}", CancellationToken.None)
};
return result;
};
// Test 1 - sale in last hour
startDate = DateTime.Now.AddHours(-1);
endDate = DateTime.Now;
List<Merchant>? searchResult = await ExecuteAsyncFunction(asyncFunction);
searchResult.ShouldNotBeNull();
searchResult.Count.ShouldBe(4);
searchResult.SingleOrDefault(s => s.Name == "Merchant 1").ShouldNotBeNull();
searchResult.SingleOrDefault(s => s.Name == "Merchant 2").ShouldNotBeNull();
searchResult.SingleOrDefault(s => s.Name == "Merchant 3").ShouldNotBeNull();
searchResult.SingleOrDefault(s => s.Name == "Merchant 4").ShouldNotBeNull();

// Test 2 - sale in last day but over an hour ago
startDate = DateTime.Now.Date.AddDays(-1);
endDate = DateTime.Now.AddHours(-1);
searchResult = await ExecuteAsyncFunction(asyncFunction);
searchResult.ShouldNotBeNull();
searchResult.Count.ShouldBe(6);
searchResult.SingleOrDefault(s => s.Name == "Merchant 5").ShouldNotBeNull();
searchResult.SingleOrDefault(s => s.Name == "Merchant 6").ShouldNotBeNull();
searchResult.SingleOrDefault(s => s.Name == "Merchant 7").ShouldNotBeNull();
searchResult.SingleOrDefault(s => s.Name == "Merchant 8").ShouldNotBeNull();
searchResult.SingleOrDefault(s => s.Name == "Merchant 9").ShouldNotBeNull();
searchResult.SingleOrDefault(s => s.Name == "Merchant 10").ShouldNotBeNull();

// Test 3 - sale in last 7 days but non yesterday
startDate = DateTime.Now.Date.AddDays(-7);
endDate = DateTime.Now.Date.AddDays(-1);
searchResult = await ExecuteAsyncFunction(asyncFunction);
searchResult.ShouldNotBeNull();
searchResult.Count.ShouldBe(3);
searchResult.SingleOrDefault(s => s.Name == "Merchant 11").ShouldNotBeNull();
searchResult.SingleOrDefault(s => s.Name == "Merchant 12").ShouldNotBeNull();
searchResult.SingleOrDefault(s => s.Name == "Merchant 13").ShouldNotBeNull();

// Test 4 - sale more than 7 days ago
startDate = DateTime.Now.Date.AddYears(-1);
endDate = DateTime.Now.Date.AddDays(-7);
searchResult = await ExecuteAsyncFunction(asyncFunction);
searchResult.ShouldNotBeNull();
searchResult.Count.ShouldBe(5);
searchResult.SingleOrDefault(s => s.Name == "Merchant 14").ShouldNotBeNull();
searchResult.SingleOrDefault(s => s.Name == "Merchant 15").ShouldNotBeNull();
searchResult.SingleOrDefault(s => s.Name == "Merchant 16").ShouldNotBeNull();
searchResult.SingleOrDefault(s => s.Name == "Merchant 17").ShouldNotBeNull();
searchResult.SingleOrDefault(s => s.Name == "Merchant 18").ShouldNotBeNull();





}

}

Loading