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

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions EstateManagementUI.BlazorServer/Models/Models.cs
Original file line number Diff line number Diff line change
Expand Up @@ -223,3 +223,11 @@ public class MerchantTransactionSummaryModel
public int SuccessfulTransactionCount { get; set; }
public int FailedTransactionCount { get; set; }
}

public class ProductPerformanceModel
{
public string? ProductName { get; set; }
public int TransactionCount { get; set; }
public decimal TransactionValue { get; set; }
public decimal PercentageContribution { get; set; }
}
1 change: 1 addition & 0 deletions EstateManagementUI.BlazorServer/Requests/Requests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ public record GetBottomOperatorDataQuery(CorrelationId CorrelationId, string Acc
public record GetLastSettlementQuery(CorrelationId CorrelationId, string AccessToken, Guid EstateId) : IRequest<Result<LastSettlementModel>>;
public record GetMerchantQuery(CorrelationId CorrelationId, string AccessToken, Guid EstateId, Guid MerchantId) : IRequest<Result<MerchantModel>>;
public record GetMerchantTransactionSummaryQuery(CorrelationId CorrelationId, string AccessToken, Guid EstateId, DateTime StartDate, DateTime EndDate, Guid? MerchantId = null, Guid? OperatorId = null, Guid? ProductId = null) : IRequest<Result<List<MerchantTransactionSummaryModel>>>;
public record GetProductPerformanceQuery(CorrelationId CorrelationId, string AccessToken, Guid EstateId, DateTime StartDate, DateTime EndDate) : IRequest<Result<List<ProductPerformanceModel>>>;
}

public static class Commands
Expand Down
69 changes: 69 additions & 0 deletions EstateManagementUI.BlazorServer/Services/TestMediatorService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
Queries.GetBottomOperatorDataQuery => Task.FromResult((TResponse)(object)Result<List<TopBottomOperatorDataModel>>.Success(GetMockBottomOperators())),
Queries.GetLastSettlementQuery => Task.FromResult((TResponse)(object)Result<LastSettlementModel>.Success(GetMockLastSettlement())),
Queries.GetMerchantTransactionSummaryQuery query => Task.FromResult((TResponse)(object)Result<List<MerchantTransactionSummaryModel>>.Success(GetMockMerchantTransactionSummary(query))),
Queries.GetProductPerformanceQuery query => Task.FromResult((TResponse)(object)Result<List<ProductPerformanceModel>>.Success(GetMockProductPerformance(query))),

// Commands - execute against test data store
Commands.CreateMerchantCommand cmd => Task.FromResult((TResponse)(object)ExecuteCreateMerchant(cmd)),
Expand Down Expand Up @@ -576,6 +577,74 @@
return summary;
}

private List<ProductPerformanceModel> GetMockProductPerformance(Queries.GetProductPerformanceQuery query)
{
var contracts = _testDataStore.GetContracts(query.EstateId);

// Calculate days in the date range to vary data based on period
var daysInRange = (query.EndDate - query.StartDate).Days + 1;

// Use date range as seed for consistent but varying data
var seed = query.StartDate.GetHashCode() ^ query.EndDate.GetHashCode();
var random = new Random(seed);

// Collect all unique products from all contracts
var productNames = contracts
.SelectMany(c => c.Products ?? new List<ContractProductModel>())
.Select(p => p.ProductName)
.Where(p => !string.IsNullOrEmpty(p))
.Distinct()
.ToList();

var products = new List<ProductPerformanceModel>();
decimal totalValue = 0;

// Generate mock transaction data for each product
// Scale transaction counts based on the date range length
var countMultiplier = Math.Max(1, daysInRange / 30.0); // Scale based on 30-day baseline

foreach (var productName in productNames)
{
var baseTransactionCount = random.Next(50, 500);

Check failure on line 608 in EstateManagementUI.BlazorServer/Services/TestMediatorService.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

EstateManagementUI.BlazorServer/Services/TestMediatorService.cs#L608

Depending on the context, generating weak random numbers may expose cryptographic functions which rely on these numbers to be exploitable.
var transactionCount = (int)(baseTransactionCount * countMultiplier);
var transactionValue = Math.Round((decimal)(random.NextDouble() * 30000 + 5000) * (decimal)countMultiplier, 2);

Check failure on line 610 in EstateManagementUI.BlazorServer/Services/TestMediatorService.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

EstateManagementUI.BlazorServer/Services/TestMediatorService.cs#L610

Depending on the context, generating weak random numbers may expose cryptographic functions which rely on these numbers to be exploitable.
totalValue += transactionValue;

products.Add(new ProductPerformanceModel
{
ProductName = productName,
TransactionCount = transactionCount,
TransactionValue = transactionValue,
PercentageContribution = 0 // Will be calculated after total is known
});
}

// Calculate percentage contributions (ensure they sum to 100%)
if (totalValue > 0)
{
decimal percentageSum = 0;
for (int i = 0; i < products.Count; i++)
{
if (i == products.Count - 1)
{
// Last item gets the remainder to ensure exact 100% (protected against negative values)
products[i].PercentageContribution = Math.Max(0, Math.Round(100 - percentageSum, 2));
}
else
{
var percentage = Math.Round((products[i].TransactionValue / totalValue) * 100, 2);
products[i].PercentageContribution = percentage;
percentageSum += percentage;
}
}
}

// Sort by transaction value descending
products = products.OrderByDescending(p => p.TransactionValue).ToList();

return products;
}

private Result ExecuteAddOperatorToEstate(Commands.AddOperatorToEstateCommand cmd)
{
var operator1 = _testDataStore.GetOperator(cmd.EstateId, cmd.OperatorId);
Expand Down
1 change: 1 addition & 0 deletions EstateManagementUI.BusinessLogic.Tests/MediatorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ public MediatorTests() {
this.Requests.Add(TestData.GetTopOperatorDataQuery);
this.Requests.Add(TestData.GetBottomOperatorDataQuery);
this.Requests.Add(TestData.GetLastSettlementQuery);
this.Requests.Add(TestData.GetProductPerformanceQuery);

// Commands
this.Requests.Add(TestData.AddNewOperatorCommand);
Expand Down
12 changes: 12 additions & 0 deletions EstateManagementUI.BusinessLogic/Models/ProductPerformanceModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System.Diagnostics.CodeAnalysis;

namespace EstateManagementUI.BusinessLogic.Models;

[ExcludeFromCodeCoverage]
public class ProductPerformanceModel
{
public string? ProductName { get; set; }
public int TransactionCount { get; set; }
public decimal TransactionValue { get; set; }
public decimal PercentageContribution { get; set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
IRequestHandler<GetTopOperatorDataQuery, Result<List<TopBottomOperatorDataModel>>>,
IRequestHandler<GetBottomOperatorDataQuery, Result<List<TopBottomOperatorDataModel>>>,
IRequestHandler<GetLastSettlementQuery, Result<LastSettlementModel>>,
IRequestHandler<GetMerchantTransactionSummaryQuery, Result<List<MerchantTransactionSummaryModel>>> {
IRequestHandler<GetMerchantTransactionSummaryQuery, Result<List<MerchantTransactionSummaryModel>>>,
IRequestHandler<GetProductPerformanceQuery, Result<List<ProductPerformanceModel>>> {

private readonly IApiClient ApiClient;
public ReportingRequestHandler(IApiClient apiClient)
Expand Down Expand Up @@ -166,4 +167,73 @@

return Result.Success(summary);
}

public async Task<Result<List<ProductPerformanceModel>>> Handle(GetProductPerformanceQuery request,
CancellationToken cancellationToken) {
// TODO: Replace with actual API call when endpoint is available
// For now, return mock data for testing
var contracts = await this.ApiClient.GetContracts(request.AccessToken, Guid.Empty, request.EstateId, cancellationToken);

if (!contracts.IsSuccess) {
return Result.Failure<List<ProductPerformanceModel>>(contracts.Message);
}

var products = new List<ProductPerformanceModel>();

// Calculate days in the date range to vary data based on period
var daysInRange = (request.EndDate - request.StartDate).Days + 1;

// Use date range as seed for consistent but varying data
var seed = request.StartDate.GetHashCode() ^ request.EndDate.GetHashCode();
var random = new Random(seed);

// Collect all unique products from all contracts
var productNames = contracts.Data
.SelectMany(c => c.Products ?? new List<ContractProductModel>())
.Select(p => p.ProductName)
.Distinct()
.ToList();

decimal totalValue = 0;

// Generate mock transaction data for each product
// Scale transaction counts based on the date range length
var countMultiplier = Math.Max(1, daysInRange / 30.0); // Scale based on 30-day baseline

foreach (var productName in productNames) {
if (string.IsNullOrEmpty(productName)) continue;

var baseTransactionCount = random.Next(50, 500);

Check failure on line 206 in EstateManagementUI.BusinessLogic/RequestHandlers/ReportingRequestHandler.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

EstateManagementUI.BusinessLogic/RequestHandlers/ReportingRequestHandler.cs#L206

Depending on the context, generating weak random numbers may expose cryptographic functions which rely on these numbers to be exploitable.
var transactionCount = (int)(baseTransactionCount * countMultiplier);
var transactionValue = Math.Round((decimal)(random.NextDouble() * 30000 + 5000) * (decimal)countMultiplier, 2);

Check failure on line 208 in EstateManagementUI.BusinessLogic/RequestHandlers/ReportingRequestHandler.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

EstateManagementUI.BusinessLogic/RequestHandlers/ReportingRequestHandler.cs#L208

Depending on the context, generating weak random numbers may expose cryptographic functions which rely on these numbers to be exploitable.
totalValue += transactionValue;

products.Add(new ProductPerformanceModel {
ProductName = productName,
TransactionCount = transactionCount,
TransactionValue = transactionValue,
PercentageContribution = 0 // Will be calculated after total is known
});
}

// Calculate percentage contributions (ensure they sum to 100%)
if (totalValue > 0) {
decimal percentageSum = 0;
for (int i = 0; i < products.Count; i++) {
if (i == products.Count - 1) {
// Last item gets the remainder to ensure exact 100% (protected against negative values)
products[i].PercentageContribution = Math.Max(0, Math.Round(100 - percentageSum, 2));
} else {
var percentage = Math.Round((products[i].TransactionValue / totalValue) * 100, 2);
products[i].PercentageContribution = percentage;
percentageSum += percentage;
}
}
}

// Sort by transaction value descending
products = products.OrderByDescending(p => p.TransactionValue).ToList();

return Result.Success(products);
}
}
2 changes: 2 additions & 0 deletions EstateManagementUI.BusinessLogic/Requests/Queries.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,5 +70,7 @@ public record GetLastSettlementQuery(CorrelationId CorrelationId, String AccessT
public record GetMerchantQuery(CorrelationId CorrelationId, String AccessToken, Guid EstateId, Guid MerchantId) : IRequest<Result<MerchantModel>>;

public record GetMerchantTransactionSummaryQuery(CorrelationId CorrelationId, String AccessToken, Guid EstateId, DateTime StartDate, DateTime EndDate, Guid? MerchantId = null, Guid? OperatorId = null, Guid? ProductId = null) : IRequest<Result<List<MerchantTransactionSummaryModel>>>;

public record GetProductPerformanceQuery(CorrelationId CorrelationId, String AccessToken, Guid EstateId, DateTime StartDate, DateTime EndDate) : IRequest<Result<List<ProductPerformanceModel>>>;
}
}
2 changes: 2 additions & 0 deletions EstateManagementUI.Testing/TestData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ public static class TestData {

public static Queries.GetLastSettlementQuery GetLastSettlementQuery => new(CorrelationId, AccessToken, EstateId);

public static Queries.GetProductPerformanceQuery GetProductPerformanceQuery => new(CorrelationId, AccessToken, EstateId, DateTime.Now.AddDays(-30), DateTime.Now);

public static Commands.AddMerchantCommand AddNewMerchantCommand => new(CorrelationId, AccessToken, EstateId, CreateMerchantModel(BusinessLogic.Models.SettlementSchedule.Immediate));

public static Commands.UpdateMerchantCommand UpdateMerchantCommand => new(CorrelationId, AccessToken, EstateId, Merchant1Id, new UpdateMerchantModel {
Expand Down