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
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,9 @@ public record TransactionSummaryByMerchantQuery(Guid EstateId, TransactionSummar
public record TransactionSummaryByOperatorQuery(Guid EstateId, TransactionSummaryByOperatorRequest Request) : IRequest<Result<TransactionSummaryByOperatorResponse>>;
public record ProductPerformanceQuery(Guid EstateId, DateTime StartDate, DateTime EndDate) : IRequest<Result<ProductPerformanceResponse>>;
public record TodaysSalesByHour(Guid estateId, DateTime comparisonDate) : IRequest<Result<List<Models.TodaysSalesByHour>>>;
}

[ExcludeFromCodeCoverage]
public record SettlementQueries {
public record TodaysSettlementQuery(Guid EstateId, DateTime ComparisonDate) : IRequest<Result<Models.TodaysSettlement>>;
}
900 changes: 387 additions & 513 deletions EstateReportingAPI.BusinessLogic/ReportingManager.cs

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,18 @@

namespace EstateReportingAPI.BusinessLogic.RequestHandlers;

public class SettlementRequestHandler : IRequestHandler<SettlementQueries.TodaysSettlementQuery, Result<TodaysSettlement>>
{
private readonly IReportingManager Manager;
public SettlementRequestHandler(IReportingManager manager) {
this.Manager = manager;
}
public async Task<Result<TodaysSettlement>> Handle(SettlementQueries.TodaysSettlementQuery request,
CancellationToken cancellationToken) {
return await this.Manager.GetTodaysSettlement(request, cancellationToken);
}
}

public class TransactionRequestHandler : IRequestHandler<TransactionQueries.TodaysFailedSales, Result<TodaysSales>>,
IRequestHandler<TransactionQueries.TodaysSalesQuery, Result<TodaysSales>>,
IRequestHandler<TransactionQueries.TransactionDetailReportQuery, Result<TransactionDetailReportResponse>>,
Expand Down
142 changes: 142 additions & 0 deletions EstateReportingAPI.IntegrationTests/TransactionsEndpointTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1070,4 +1070,146 @@ public async Task TransactionsEndpoint_TodaysSalesByHour_SummaryDataReturned()

}
}
}


public class SettlmentsEndpointTests : ControllerTestsBase {
private String BaseRoute = "api/settlements";

public SettlmentsEndpointTests(ITestOutputHelper testOutputHelper) {
this.TestOutputHelper = testOutputHelper;
}


protected override async Task ClearStandingData() {

}

protected override async Task SetupStandingData() {
Stopwatch sw = Stopwatch.StartNew();
this.TestOutputHelper.WriteLine("Setting up standing data");

// Estates
await this.helper.AddEstate("Test Estate", "Ref1");
sw.Stop();
this.TestOutputHelper.WriteLine($"Setup Estate {sw.ElapsedMilliseconds}ms");
sw.Restart();

// Operators
await this.helper.AddOperator("Test Estate", "Safaricom");
await this.helper.AddOperator("Test Estate", "Voucher");
await this.helper.AddOperator("Test Estate", "PataPawa PostPay");
await this.helper.AddOperator("Test Estate", "PataPawa PrePay");

sw.Stop();
this.TestOutputHelper.WriteLine($"Setup Operators {sw.ElapsedMilliseconds}ms");
sw.Restart();
// Merchants
await this.helper.AddMerchant("Test Estate", "Test Merchant 1", DateTime.MinValue, DateTime.MinValue, default, default);
await this.helper.AddMerchant("Test Estate", "Test Merchant 2", DateTime.MinValue, DateTime.MinValue, default, default);
await this.helper.AddMerchant("Test Estate", "Test Merchant 3", DateTime.MinValue, DateTime.MinValue, default, default);
await this.helper.AddMerchant("Test Estate", "Test Merchant 4", DateTime.MinValue, DateTime.MinValue, default, default);
sw.Stop();
this.TestOutputHelper.WriteLine($"Setup Merchants {sw.ElapsedMilliseconds}ms");
sw.Restart();

// Contracts & Products
List<(string productName, int productType, decimal? value)> safaricomProductList = new() { ("200 KES Topup", 0, 200.00m), ("100 KES Topup", 0, 100.00m), ("50 KES Topup", 0, 50.00m), ("Custom", 0, null) };
await this.helper.AddContractWithProducts("Test Estate", "Safaricom Contract", "Safaricom", safaricomProductList);

List<(string productName, int productType, decimal? value)> voucherProductList = new() { ("10 KES Voucher", 0, 10.00m), ("Custom", 0, null) };
await this.helper.AddContractWithProducts("Test Estate", "Healthcare Centre 1 Contract", "Voucher", voucherProductList);

List<(string productName, int productType, decimal? value)> postPayProductList = new() { ("Post Pay Bill Pay", 0, null) };
await this.helper.AddContractWithProducts("Test Estate", "PataPawa PostPay Contract", "PataPawa PostPay", postPayProductList);

List<(string productName, int productType, decimal? value)> prePayProductList = new() { ("Pre Pay Bill Pay", 0, null) };
await this.helper.AddContractWithProducts("Test Estate", "PataPawa PrePay Contract", "PataPawa PrePay", prePayProductList);

sw.Stop();
this.TestOutputHelper.WriteLine($"Setup Contracts {sw.ElapsedMilliseconds}ms");
sw.Restart();

// Response Codes
await this.helper.AddResponseCode(0, "Success");
await this.helper.AddResponseCode(1000, "Unknown Device");
await this.helper.AddResponseCode(1001, "Unknown Estate");
await this.helper.AddResponseCode(1002, "Unknown Merchant");
await this.helper.AddResponseCode(1003, "No Devices Configured");

sw.Stop();
this.TestOutputHelper.WriteLine($"Setup Response Codes {sw.ElapsedMilliseconds}ms");
sw.Restart();

this.merchantsList = this.context.Merchants.Select(m => m).ToList();

this.contractList = this.context.Contracts.Join(this.context.Operators, c => c.OperatorId, o => o.OperatorId, (c,
o) => new { c.ContractId, c.Description, o.OperatorId, o.Name }).ToList().Select(x => (x.ContractId, x.Description, x.OperatorId, x.Name)).ToList();

var query1 = this.context.Contracts.GroupJoin(this.context.ContractProducts, c => c.ContractId, cp => cp.ContractId, (c,
productGroup) => new { c.ContractId, Products = productGroup.Select(p => new { p.ContractProductReportingId, p.ContractProductId, p.ProductName, p.Value }).OrderBy(p => p.ContractProductId).Select(p => new { p.ContractProductId, p.ProductName, p.Value, p.ContractProductReportingId }).ToList() }).ToList();

this.contractProducts = query1.ToDictionary(item => item.ContractId, item => item.Products.Select(i => (i.ContractProductId, i.ProductName, i.Value, i.ContractProductReportingId)).ToList());

this.operatorsList = this.context.Operators.ToList();

sw.Stop();
this.TestOutputHelper.WriteLine($"Data Caching {sw.ElapsedMilliseconds}ms");
sw.Restart();
}

[Fact]
public async Task SettlementEndpoints_TodaysSettlement_SettlementReturned()
{
int overallTodaysSettlementTransactionCount = 0;
int overallTodaysPendingSettlementTransactionCount = 0;

int overallComparisonSettlementTransactionCount = 0;
int overallComparisonPendingSettlementTransactionCount = 0;
List<(decimal settledTransactions, decimal pendingSettlementTransactions, decimal settlementFees, decimal pendingSettlementFees)> todayOverallTotals = new();
List<(decimal settledTransactions, decimal pendingSettlementTransactions, decimal settlementFees, decimal pendingSettlementFees)> comparisonOverallTotals = new();

DateTime todaysDate = DateTime.Now;
DateTime comparisonDate = DateTime.Now.AddDays(-1);
foreach (var merchant in merchantsList)
{
int todaysSettlementTransactionCount = 5;
int todaysPendingSettlementTransactionCount = 9;
var contract = this.contractList.Single(c => c.operatorName == "Safaricom");
(decimal settledTransactions, decimal pendingSettlementTransactions, decimal settlementFees, decimal pendingSettlementFees) todayTotals = await helper.AddSettlementRecord(merchant.EstateId, merchant.MerchantId, contract.operatorId, todaysDate, todaysSettlementTransactionCount, todaysPendingSettlementTransactionCount);
todayOverallTotals.Add(todayTotals);

overallTodaysSettlementTransactionCount += todaysSettlementTransactionCount;
;
overallTodaysPendingSettlementTransactionCount += todaysPendingSettlementTransactionCount;

int comparisonSettlementTransactionCount = 12;
int comparisonPendingSettlementTransactionCount = 15;
var comparisonTotals = await helper.AddSettlementRecord(merchant.EstateId, merchant.MerchantId, contract.operatorId, comparisonDate, comparisonSettlementTransactionCount, comparisonPendingSettlementTransactionCount);
comparisonOverallTotals.Add(comparisonTotals);

overallComparisonSettlementTransactionCount += comparisonSettlementTransactionCount;
overallComparisonPendingSettlementTransactionCount += comparisonPendingSettlementTransactionCount;
}

await helper.RunTodaysTransactionsSummaryProcessing(comparisonDate.Date.AddDays(-1));
await helper.RunHistoricTransactionsSummaryProcessing(comparisonDate.Date.AddDays(-1));
await helper.RunTodaysTransactionsSummaryProcessing(todaysDate.Date.AddDays(-1));
await helper.RunSettlementSummaryProcessing(comparisonDate.Date);


var result = await this.CreateAndSendHttpRequestMessage<DataTransferObjects.TodaysSettlement>($"{this.BaseRoute}/todayssettlements?comparisondate={DateTime.Now.AddDays(-1):yyyy-MM-dd}", CancellationToken.None);
result.IsSuccess.ShouldBeTrue();
var todaysSettlement = result.Data;
todaysSettlement.ShouldNotBeNull();
todaysSettlement.ComparisonSettlementCount.ShouldBe(overallComparisonSettlementTransactionCount);
todaysSettlement.ComparisonSettlementValue.ShouldBe(comparisonOverallTotals.Sum(c => c.settlementFees));
todaysSettlement.ComparisonPendingSettlementCount.ShouldBe(overallComparisonPendingSettlementTransactionCount);
todaysSettlement.ComparisonPendingSettlementValue.ShouldBe(comparisonOverallTotals.Sum(c => c.pendingSettlementFees));

todaysSettlement.TodaysSettlementCount.ShouldBe(overallTodaysSettlementTransactionCount);
todaysSettlement.TodaysSettlementValue.ShouldBe(todayOverallTotals.Sum(c => c.settlementFees));
todaysSettlement.TodaysPendingSettlementCount.ShouldBe(overallTodaysPendingSettlementTransactionCount);
todaysSettlement.TodaysPendingSettlementValue.ShouldBe(todayOverallTotals.Sum(c => c.pendingSettlementFees));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ public class TransactionSummaryByOperatorResponse
{
public List<OperatorDetail> Operators { get; set; }
public OperatorDetailSummary Summary { get; set; }
}
}

3 changes: 3 additions & 0 deletions EstateReportingAPI/Bootstrapper/MediatorRegistry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ public MediatorRegistry() {
this.AddSingleton<IRequestHandler<TransactionQueries.ProductPerformanceQuery, Result<ProductPerformanceResponse>>, TransactionRequestHandler>();
this.AddSingleton<IRequestHandler<TransactionQueries.TodaysSalesByHour, Result<List<TodaysSalesByHour>>>, TransactionRequestHandler>();

this.AddSingleton<IRequestHandler<SettlementQueries.TodaysSettlementQuery, Result<TodaysSettlement>>,SettlementRequestHandler>();


this.AddSingleton<IRequestHandler<CalendarQueries.GetYearsQuery, Result<List<Int32>>>, CalendarRequestHandler>();
this.AddSingleton<IRequestHandler<CalendarQueries.GetAllDatesQuery, Result<List<Calendar>>>, CalendarRequestHandler>();
this.AddSingleton<IRequestHandler<CalendarQueries.GetComparisonDatesQuery, Result<List<Calendar>>>, CalendarRequestHandler>();
Expand Down
15 changes: 15 additions & 0 deletions EstateReportingAPI/Endpoints/TransactionEndpoints.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,21 @@

namespace EstateReportingAPI.Endpoints;

public static class SettlementEndpoints {
private const string BaseRoute = "api/settlements";

public static void MapSettlementEndpoints(this IEndpointRouteBuilder app) {
RouteGroupBuilder group = app.MapGroup(BaseRoute).WithTags("Settlements");

Boolean disableAuthorisation = ConfigurationReader.GetValueOrDefault<Boolean>("AppSettings", "DisableAuthorisation", false);
if (disableAuthorisation == false) {
group = group.RequireAuthorization();
}

group.MapGet("todayssettlements", SettlementHandler.TodaysSettlements).WithStandardProduces<TodaysSettlement>();
}
}

public static class TransactionEndpoints
{
private const string BaseRoute = "api/transactions";
Expand Down
21 changes: 21 additions & 0 deletions EstateReportingAPI/Handlers/TransactionHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,27 @@

namespace EstateReportingAPI.Handlers;

public static class SettlementHandler {
public static async Task<IResult> TodaysSettlements([FromHeader] Guid estateId,
[FromQuery] DateTime comparisonDate,
IMediator mediator,
CancellationToken cancellationToken) {
var query = new SettlementQueries.TodaysSettlementQuery(estateId, comparisonDate);
var result = await mediator.Send(query, cancellationToken);

return ResponseFactory.FromResult(result, r => new TodaysSettlement() {
ComparisonPendingSettlementCount = r.ComparisonPendingSettlementCount,
ComparisonPendingSettlementValue = r.ComparisonPendingSettlementValue,
ComparisonSettlementCount = r.ComparisonSettlementCount,
ComparisonSettlementValue = r.ComparisonSettlementValue,
TodaysPendingSettlementCount = r.TodaysPendingSettlementCount,
TodaysPendingSettlementValue = r.TodaysPendingSettlementValue,
TodaysSettlementCount = r.TodaysSettlementCount,
TodaysSettlementValue = r.TodaysSettlementValue
});
}
}

public static class TransactionHandler {
public static async Task<IResult> TodaysSales([FromHeader] Guid estateId,
[FromQuery] int? merchantReportingId,
Expand Down
1 change: 1 addition & 0 deletions EstateReportingAPI/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerF
endpoints.MapMerchantEndpoints();
endpoints.MapContractEndpoints();
endpoints.MapTransactionEndpoints();
endpoints.MapSettlementEndpoints();

// Old Endpoints
//endpoints.MapFactSettlementsEndpoints();
Expand Down
Loading