diff --git a/Directory.Packages.props b/Directory.Packages.props
index 0b5a68e8..fc5b0573 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -44,7 +44,7 @@
-
+
diff --git a/TransactionProcessor.Mobile.BusinessLogic.Tests/RequestHandlerTests/ReportRequestHandlerTests.cs b/TransactionProcessor.Mobile.BusinessLogic.Tests/RequestHandlerTests/ReportRequestHandlerTests.cs
index d02db4ca..55609806 100644
--- a/TransactionProcessor.Mobile.BusinessLogic.Tests/RequestHandlerTests/ReportRequestHandlerTests.cs
+++ b/TransactionProcessor.Mobile.BusinessLogic.Tests/RequestHandlerTests/ReportRequestHandlerTests.cs
@@ -1,9 +1,11 @@
using MediatR;
+using Moq;
using SimpleResults;
using Shouldly;
using TransactionProcessor.Mobile.BusinessLogic.RequestHandlers;
using TransactionProcessor.Mobile.BusinessLogic.Requests;
using TransactionProcessor.Mobile.BusinessLogic.Models;
+using TransactionProcessor.Mobile.BusinessLogic.Services;
namespace TransactionProcessor.Mobile.BusinessLogic.Tests.RequestHandlerTests;
@@ -12,7 +14,23 @@ public class ReportRequestHandlerTests
[Fact]
public async Task GetDailyPerformanceSummaryQuery_ReturnsMockedSummaryForToday()
{
- ReportRequestHandler handler = new();
+ Mock reportsService = new();
+ Mock applicationCache = new();
+ MerchantDetailsModel merchantDetails = new()
+ {
+ MerchantReportingId = 12345
+ };
+
+ applicationCache.Setup(a => a.GetMerchantDetails()).Returns(merchantDetails);
+ reportsService.Setup(r => r.GetDailyPerformanceSummary(
+ PerformanceSummaryPeriod.Today,
+ merchantDetails.MerchantReportingId,
+ It.IsAny(),
+ It.IsAny(),
+ It.IsAny()))
+ .ReturnsAsync(Result.Success(DailyPerformanceSummaryModel.CreateMock(PerformanceSummaryPeriod.Today)));
+
+ ReportRequestHandler handler = new(reportsService.Object, applicationCache.Object);
Result result = await handler.Handle(new ReportQueries.GetDailyPerformanceSummaryQuery(PerformanceSummaryPeriod.Today), CancellationToken.None);
@@ -20,5 +38,12 @@ public async Task GetDailyPerformanceSummaryQuery_ReturnsMockedSummaryForToday()
result.Data.Period.ShouldBe(PerformanceSummaryPeriod.Today);
result.Data.Metrics.ShouldContain(m => m.Title == "Total transaction count" && m.Value == "48");
result.Data.DrillDownTransactions.ShouldContain(t => t.Reference == "TXN-00048");
+ reportsService.Verify(r => r.GetDailyPerformanceSummary(
+ PerformanceSummaryPeriod.Today,
+ merchantDetails.MerchantReportingId,
+ It.IsAny(),
+ It.IsAny(),
+ It.IsAny()),
+ Times.Once);
}
}
diff --git a/TransactionProcessor.Mobile.BusinessLogic.Tests/ServicesTests/MerchantServiceTests.cs b/TransactionProcessor.Mobile.BusinessLogic.Tests/ServicesTests/MerchantServiceTests.cs
index 7bd51c08..45f7fccc 100644
--- a/TransactionProcessor.Mobile.BusinessLogic.Tests/ServicesTests/MerchantServiceTests.cs
+++ b/TransactionProcessor.Mobile.BusinessLogic.Tests/ServicesTests/MerchantServiceTests.cs
@@ -75,7 +75,7 @@ public async Task MerchantService_GetContractProducts_ContractProductsAreReturne
});
- this.MockHttpMessageHandler.When($"http://localhost/api/merchants/contracts?application_version=1.0.0")
+ this.MockHttpMessageHandler.When($"http://localhost/api/merchants/contracts?applicationVersion=1.0.0")
.Respond("application/json", StringSerialiser.Serialise(contracts)); // Respond with JSON
var result = await this.MerchantService.GetContractProducts(CancellationToken.None);
@@ -86,7 +86,7 @@ public async Task MerchantService_GetContractProducts_ContractProductsAreReturne
[Fact]
public async Task MerchantService_GetContractProducts_HttpCallFailed_FailureReturned(){
- this.MockHttpMessageHandler.When($"http://localhost/api/merchants/contracts?application_version=1.0.0")
+ this.MockHttpMessageHandler.When($"http://localhost/api/merchants/contracts?applicationVersion=1.0.0")
.Respond(HttpStatusCode.InternalServerError);
Result> result = await this.MerchantService.GetContractProducts(CancellationToken.None);
@@ -122,7 +122,7 @@ public async Task MerchantService_GetMerchantDetails_MerchantDetailsReturned(){
}
};
- this.MockHttpMessageHandler.When($"http://localhost/api/merchants?application_version=1.0.0")
+ this.MockHttpMessageHandler.When($"http://localhost/api/merchants?applicationVersion=1.0.0")
.Respond("application/json", StringSerialiser.Serialise(merchantResponse)); // Respond with JSON
Result merchantDetails = await this.MerchantService.GetMerchantDetails(CancellationToken.None);
@@ -147,7 +147,7 @@ public async Task MerchantService_GetMerchantDetails_MerchantDetailsReturned(){
[Fact]
public async Task MerchantService_GetMerchantDetails_ResultFailed_FailedResultIsReturned()
{
- this.MockHttpMessageHandler.When($"http://localhost/api/merchants?application_version=1.0.0")
+ this.MockHttpMessageHandler.When($"http://localhost/api/merchants?applicationVersion=1.0.0")
.Respond(HttpStatusCode.BadRequest);
Result merchantDetails = await this.MerchantService.GetMerchantDetails(CancellationToken.None);
@@ -156,7 +156,7 @@ public async Task MerchantService_GetMerchantDetails_ResultFailed_FailedResultIs
[Fact]
public async Task MerchantService_GetMerchantDetails_ExceptionThrown_FailedResultReturned(){
- this.MockHttpMessageHandler.When($"http://localhost/api/merchants?application_version=1.0.0")
+ this.MockHttpMessageHandler.When($"http://localhost/api/merchants?applicationVersion=1.0.0")
.Respond(HttpStatusCode.InternalServerError);
Result merchantDetails = await this.MerchantService.GetMerchantDetails(CancellationToken.None);
@@ -196,4 +196,4 @@ public void GetProductSubType_CorrectProductSubTypeReturned(String operatorName,
Models.ProductSubType result = Services.MerchantService.GetProductSubType(operatorName);
result.ShouldBe(expectedProductSubType);
}
-}
\ No newline at end of file
+}
diff --git a/TransactionProcessor.Mobile.BusinessLogic/Models/DailyPerformanceSummaryModel.cs b/TransactionProcessor.Mobile.BusinessLogic/Models/DailyPerformanceSummaryModel.cs
index c1cd666f..41f5b71f 100644
--- a/TransactionProcessor.Mobile.BusinessLogic/Models/DailyPerformanceSummaryModel.cs
+++ b/TransactionProcessor.Mobile.BusinessLogic/Models/DailyPerformanceSummaryModel.cs
@@ -1,3 +1,5 @@
+using TransactionProcessor.Mobile.BusinessLogic.Services;
+
namespace TransactionProcessor.Mobile.BusinessLogic.Models;
public enum PerformanceSummaryPeriod
@@ -8,14 +10,15 @@ public enum PerformanceSummaryPeriod
MonthToDate = 3,
}
-public sealed record DailyPerformanceSummaryModel(
- PerformanceSummaryPeriod Period,
- DateTime FromDate,
- DateTime ToDate,
- string PeriodLabel,
- IReadOnlyList Metrics,
- IReadOnlyList DrillDownTransactions)
+public sealed record DailyPerformanceSummaryModel()
{
+ public PerformanceSummaryPeriod Period { get; set; }
+ public DateTime FromDate { get; set; }
+ public DateTime ToDate { get; set; }
+ public string PeriodLabel { get; set; }
+ public List Metrics { get; set; }
+ public List DrillDownTransactions { get; set; }
+
public static DailyPerformanceSummaryModel CreateMock(PerformanceSummaryPeriod period)
{
DateTime today = DateTime.Today;
@@ -46,15 +49,22 @@ public static DailyPerformanceSummaryModel CreateMock(PerformanceSummaryPeriod p
List drillDownTransactions = new()
{
- new DailyPerformanceTransactionModel("TXN-00048", "Mobile Topup", "Success", "250.00 KES", today.AddHours(-1)),
- new DailyPerformanceTransactionModel("TXN-00047", "Bill Payment", "Success", "1,500.00 KES", today.AddHours(-2)),
- new DailyPerformanceTransactionModel("TXN-00046", "Voucher Issue", "Failed", "0.00 KES", today.AddHours(-3)),
+ new DailyPerformanceTransactionModel("TXN-00048", "Mobile Topup", "Success", 250.00m, today.AddHours(-1)),
+ new DailyPerformanceTransactionModel("TXN-00047", "Bill Payment", "Success", 1500.00m, today.AddHours(-2)),
+ new DailyPerformanceTransactionModel("TXN-00046", "Voucher Issue", "Failed", 0.00m, today.AddHours(-3)),
};
- return new DailyPerformanceSummaryModel(period, fromDate, toDate, period.ToString(), metrics, drillDownTransactions);
+ return new DailyPerformanceSummaryModel() {
+ DrillDownTransactions = drillDownTransactions,
+ FromDate = fromDate,
+ ToDate = toDate,
+ Metrics = metrics,
+ Period = period,
+ PeriodLabel = period.ToString()
+ };
}
- private static DateTime StartOfWeek(DateTime date)
+ internal static DateTime StartOfWeek(DateTime date)
{
int daysSinceSunday = (int)date.DayOfWeek;
return date.Date.AddDays(-daysSinceSunday);
@@ -67,8 +77,10 @@ public enum DailyPerformanceMetricCategory
Total = 1,
Success = 2,
Failure = 3,
+ Average = 4,
+ TopSalesCount = 5
}
public sealed record DailyPerformanceMetricModel(string Title, string Value, string Description, DailyPerformanceMetricCategory Category = DailyPerformanceMetricCategory.Neutral);
-public sealed record DailyPerformanceTransactionModel(string Reference, string Product, string Status, string Amount, DateTime TransactionDateTime);
+public sealed record DailyPerformanceTransactionModel(string Reference, string Product, string Status, Decimal Amount, DateTime TransactionDateTime);
diff --git a/TransactionProcessor.Mobile.BusinessLogic/Models/MerchantDetailsModel.cs b/TransactionProcessor.Mobile.BusinessLogic/Models/MerchantDetailsModel.cs
index a1270f8e..6f28c387 100644
--- a/TransactionProcessor.Mobile.BusinessLogic/Models/MerchantDetailsModel.cs
+++ b/TransactionProcessor.Mobile.BusinessLogic/Models/MerchantDetailsModel.cs
@@ -15,4 +15,5 @@ public class MerchantDetailsModel
public String SettlementSchedule { get; set; }
public AddressModel Address { get; set; }
public ContactModel Contact { get; set; }
+ public int MerchantReportingId { get; set; }
}
\ No newline at end of file
diff --git a/TransactionProcessor.Mobile.BusinessLogic/RequestHandlers/ReportRequestHandler.cs b/TransactionProcessor.Mobile.BusinessLogic/RequestHandlers/ReportRequestHandler.cs
index c11e4ddc..201261d7 100644
--- a/TransactionProcessor.Mobile.BusinessLogic/RequestHandlers/ReportRequestHandler.cs
+++ b/TransactionProcessor.Mobile.BusinessLogic/RequestHandlers/ReportRequestHandler.cs
@@ -2,14 +2,39 @@
using SimpleResults;
using TransactionProcessor.Mobile.BusinessLogic.Models;
using TransactionProcessor.Mobile.BusinessLogic.Requests;
+using TransactionProcessor.Mobile.BusinessLogic.Services;
namespace TransactionProcessor.Mobile.BusinessLogic.RequestHandlers;
-public sealed class ReportRequestHandler : IRequestHandler>
-{
- public Task> Handle(ReportQueries.GetDailyPerformanceSummaryQuery request, CancellationToken cancellationToken)
- {
- DailyPerformanceSummaryModel summary = DailyPerformanceSummaryModel.CreateMock(request.Period);
- return Task.FromResult(Result.Success(summary));
+public sealed class ReportRequestHandler : IRequestHandler> {
+ private readonly IReportsService ReportsService;
+ private readonly IApplicationCache ApplicationCache;
+
+ public ReportRequestHandler(IReportsService reportsService, IApplicationCache applicationCache) {
+ ReportsService = reportsService;
+ this.ApplicationCache = applicationCache;
+ }
+
+
+ public async Task> Handle(ReportQueries.GetDailyPerformanceSummaryQuery request,
+ CancellationToken cancellationToken) {
+
+ DateTime current = DateTime.Now.Date;
+
+ (DateTime startDate, DateTime endDate) dates = request.Period switch {
+ PerformanceSummaryPeriod.Today => (current, current),
+ PerformanceSummaryPeriod.Yesterday => (current.AddDays(-1), current.AddDays(-1)),
+ PerformanceSummaryPeriod.ThisWeek => (DailyPerformanceSummaryModel.StartOfWeek(current), current),
+ PerformanceSummaryPeriod.MonthToDate => (new DateTime(current.Year, current.Month, 1), current),
+ _ => throw new ArgumentOutOfRangeException(nameof(request.Period), request.Period, "Invalid performance summary period.")
+ };
+
+ MerchantDetailsModel merchant = this.ApplicationCache.GetMerchantDetails();
+ if (merchant == null)
+ {
+ return Result.Failure("Merchant details are not available.");
+ }
+
+ return await this.ReportsService.GetDailyPerformanceSummary(request.Period, merchant.MerchantReportingId, dates.startDate, dates.endDate, cancellationToken);
}
}
diff --git a/TransactionProcessor.Mobile.BusinessLogic/RequestHandlers/TransactionRequestHandler.cs b/TransactionProcessor.Mobile.BusinessLogic/RequestHandlers/TransactionRequestHandler.cs
index b19f3cb7..5968e397 100644
--- a/TransactionProcessor.Mobile.BusinessLogic/RequestHandlers/TransactionRequestHandler.cs
+++ b/TransactionProcessor.Mobile.BusinessLogic/RequestHandlers/TransactionRequestHandler.cs
@@ -113,7 +113,7 @@ public async Task> Handle(TransactionCommands.
TransactionDateTime = request.TransactionDateTime,
TransactionNumber = transaction.transactionNumber.ToString(),
DeviceIdentifier = this.DeviceService.GetIdentifier(),
- ApplicationVersion = "1.0.5"//this.ApplicationInfoService.VersionString
+ ApplicationVersion = this.ApplicationInfoService.VersionString
};
Result result = await transactionService.PerformLogon(model, cancellationToken);
diff --git a/TransactionProcessor.Mobile.BusinessLogic/Services/MerchantService.cs b/TransactionProcessor.Mobile.BusinessLogic/Services/MerchantService.cs
index d4eb0d7b..66e302a8 100644
--- a/TransactionProcessor.Mobile.BusinessLogic/Services/MerchantService.cs
+++ b/TransactionProcessor.Mobile.BusinessLogic/Services/MerchantService.cs
@@ -65,7 +65,7 @@ public async Task>> GetContractProducts(Cancel
TokenResponseModel accessToken = this.ApplicationCache.GetAccessToken();
- String requestUri = this.BuildRequestUrl($"/api/merchants/contracts?application_version={this.ApplicationInfoService.VersionString}");
+ String requestUri = this.BuildRequestUrl($"/api/merchants/contracts?applicationVersion={this.ApplicationInfoService.VersionString}");
Logger.LogInformation("About to request merchant contracts");
Logger.LogDebug($"Merchant Contract Request details: Access Token {accessToken.AccessToken}");
@@ -122,7 +122,7 @@ public async Task> GetMerchantBalance(CancellationToken cancella
public async Task> GetMerchantDetails(CancellationToken cancellationToken) {
TokenResponseModel accessToken = this.ApplicationCache.GetAccessToken();
- String requestUri = this.BuildRequestUrl($"/api/merchants?application_version={this.ApplicationInfoService.VersionString}");
+ String requestUri = this.BuildRequestUrl($"/api/merchants?applicationVersion={this.ApplicationInfoService.VersionString}");
Logger.LogInformation("About to request merchant details");
Logger.LogDebug($"Merchant Details Request details: Access Token {accessToken.AccessToken}");
@@ -141,6 +141,7 @@ public async Task> GetMerchantDetails(CancellationT
MerchantDetailsModel model = new MerchantDetailsModel {
EstateId = responseDataResult.Data.EstateId,
MerchantId = responseDataResult.Data.MerchantId,
+ MerchantReportingId = responseDataResult.Data.MerchantReportingId,
MerchantName = responseDataResult.Data.MerchantName,
NextStatementDate = responseDataResult.Data.NextStatementDate,
LastStatementDate = new DateTime(),
@@ -202,4 +203,4 @@ public static ProductSubType GetProductSubType(String operatorName)
}
#endregion
-}
\ No newline at end of file
+}
diff --git a/TransactionProcessor.Mobile.BusinessLogic/Services/ReportsService.cs b/TransactionProcessor.Mobile.BusinessLogic/Services/ReportsService.cs
new file mode 100644
index 00000000..be46913c
--- /dev/null
+++ b/TransactionProcessor.Mobile.BusinessLogic/Services/ReportsService.cs
@@ -0,0 +1,121 @@
+using Shared.Results;
+using SimpleResults;
+using System;
+using System.Collections.Generic;
+using System.Text;
+using TransactionProcessor.Mobile.BusinessLogic.Logging;
+using TransactionProcessor.Mobile.BusinessLogic.Models;
+using TransactionProcessor.Mobile.BusinessLogic.Serialisation;
+using TransactionProcessor.Mobile.BusinessLogic.UIServices;
+using TransactionProcessorACL.DataTransferObjects.Responses;
+
+namespace TransactionProcessor.Mobile.BusinessLogic.Services
+{
+ public interface IReportsService {
+ Task> GetDailyPerformanceSummary(PerformanceSummaryPeriod period, int merchantReportingId, DateTime startDate, DateTime endDate, CancellationToken cancellationToken);
+ }
+ public class ReportsService : ClientProxyBase.ClientProxyBase, IReportsService
+ {
+ #region Fields
+
+ private readonly IApplicationCache ApplicationCache;
+ private readonly IApplicationInfoService ApplicationInfoService;
+
+ private readonly Func BaseAddressResolver;
+
+ #endregion
+ public ReportsService(Func baseAddressResolver,
+ HttpClient httpClient,
+ IApplicationCache applicationCache, Func