diff --git a/EstateManagementUI.BlazorServer/Components/Pages/Reporting/SettlementSummary.razor b/EstateManagementUI.BlazorServer/Components/Pages/Reporting/SettlementSummary.razor index 968fe4e9..2d8b4d9a 100644 --- a/EstateManagementUI.BlazorServer/Components/Pages/Reporting/SettlementSummary.razor +++ b/EstateManagementUI.BlazorServer/Components/Pages/Reporting/SettlementSummary.razor @@ -1,5 +1,13 @@ @page "/reporting/settlement-summary" @rendermode InteractiveServer +@using MediatR +@using Microsoft.AspNetCore.Components.Forms +@using EstateManagementUI.BlazorServer.Requests +@using EstateManagementUI.BlazorServer.Models +@using static EstateManagementUI.BlazorServer.Requests.Queries +@inject IMediator Mediator +@inject NavigationManager Navigation +@inject ILogger Logger Settlement Summary Report @@ -8,7 +16,7 @@

Settlement Summary Report

-

View settlement summary information

+

View settlement outcomes per merchant with net settlement calculations

@@ -18,10 +26,343 @@
- -
-
-

Settlement summary report functionality will be implemented here.

+ @if (isLoading) + { + +
+
-
+ } + else if (!string.IsNullOrEmpty(errorMessage)) + { + +
+
+
+ + + +
+

Error Loading Data

+

@errorMessage

+
+
+
+
+ } + else + { + +
+
+

Filters

+
+
+
+ +
+ + +
+
+ + +
+ + +
+ + +
+
+
+ +
+ + +
+
+
+ + +
+
+
+ + +
+
+
+ + + +
+
+ Total Merchants + @totalMerchants +
+
+ +
+
+ + + +
+
+ Gross Value + @totalGrossValue.ToString("C") +
+
+ +
+
+ + + +
+
+ Total Fees + @totalFees.ToString("C") +
+
+ +
+
+ + + +
+
+ Net Settlement + @totalNetSettlement.ToString("C") +
+
+
+ + +
+
+

Settlement Summary

+
+
+ @if (summaryData != null && summaryData.Any()) + { +
+ + + + + + + + + + + + + @foreach (var item in summaryData) + { + + + + + + + + + } + +
Merchant NameSettlement PeriodGross ValueTotal FeesNet SettlementStatus
@item.MerchantName +
+
@item.SettlementPeriodStart.ToString("yyyy-MM-dd")
+
to @item.SettlementPeriodEnd.ToString("yyyy-MM-dd")
+
+
@item.GrossTransactionValue.ToString("C")-@item.TotalFees.ToString("C")@item.NetSettlementAmount.ToString("C") + @{ + var statusClass = item.SettlementStatus?.ToLower() switch + { + "settled" => "badge-success", + "pending" => "badge-warning", + "failed" => "badge-danger", + _ => "badge-secondary" + }; + } + + @(item.SettlementStatus?.ToUpper() ?? "UNKNOWN") + +
+
+
+

+ Note: Net Settlement Amount is calculated as: Gross Transaction Value - Total Fees. + Settlement status reflects the current state from the settlement engine. +

+
+ } + else + { +
+ + + +

No settlement data available for the selected period

+
+ } +
+
+ }
+ +@code { + private bool isLoading = true; + private string? errorMessage; + + // Filter states + private DateOnly _startDate = DateOnly.FromDateTime(DateTime.Now.AddDays(-30)); + private DateOnly _endDate = DateOnly.FromDateTime(DateTime.Now); + private string _selectedMerchantId = ""; + private string _selectedStatus = ""; + + // Data + private List? summaryData; + private List? merchants; + + // KPIs + private int totalMerchants = 0; + private decimal totalGrossValue = 0; + private decimal totalFees = 0; + private decimal totalNetSettlement = 0; + + protected override async Task OnInitializedAsync() + { + await LoadData(); + } + + private async Task LoadData() + { + try + { + isLoading = true; + errorMessage = null; + StateHasChanged(); + + var correlationId = new CorrelationId(Guid.NewGuid()); + var estateId = Guid.Parse("11111111-1111-1111-1111-111111111111"); + var accessToken = "stubbed-token"; + + // Load filter options + var merchantsResult = await Mediator.Send(new GetMerchantsQuery(correlationId, accessToken, estateId)); + + if (merchantsResult.IsSuccess) + merchants = merchantsResult.Data; + + // Load summary data + await LoadSummaryData(); + } + catch (Exception ex) + { + errorMessage = $"Failed to load data: {ex.Message}"; + Logger.LogError(ex, "Error loading settlement summary data"); + } + finally + { + isLoading = false; + StateHasChanged(); + } + } + + private async Task LoadSummaryData() + { + try + { + var correlationId = new CorrelationId(Guid.NewGuid()); + var estateId = Guid.Parse("11111111-1111-1111-1111-111111111111"); + var accessToken = "stubbed-token"; + + var startDate = _startDate.ToDateTime(TimeOnly.MinValue); + var endDate = _endDate.ToDateTime(TimeOnly.MaxValue); + + Guid? merchantId = string.IsNullOrEmpty(_selectedMerchantId) ? null : Guid.Parse(_selectedMerchantId); + string? status = string.IsNullOrEmpty(_selectedStatus) ? null : _selectedStatus; + + var result = await Mediator.Send(new GetSettlementSummaryQuery( + correlationId, + accessToken, + estateId, + startDate, + endDate, + merchantId, + status + )); + + if (result.IsSuccess && result.Data != null) + { + summaryData = result.Data; + CalculateKPIs(); + } + else + { + errorMessage = result.Message ?? "Failed to load settlement summary data"; + } + } + catch (Exception ex) + { + errorMessage = $"Failed to load settlement summary data: {ex.Message}"; + Logger.LogError(ex, "Error loading settlement summary data"); + } + } + + private void CalculateKPIs() + { + if (summaryData == null || !summaryData.Any()) + { + totalMerchants = 0; + totalGrossValue = 0; + totalFees = 0; + totalNetSettlement = 0; + return; + } + + totalMerchants = summaryData.Count; + totalGrossValue = summaryData.Sum(s => s.GrossTransactionValue); + totalFees = summaryData.Sum(s => s.TotalFees); + totalNetSettlement = summaryData.Sum(s => s.NetSettlementAmount); + } + + private async Task ApplyFilters() + { + await LoadSummaryData(); + } + + private async Task ClearFilters() + { + _startDate = DateOnly.FromDateTime(DateTime.Now.AddDays(-30)); + _endDate = DateOnly.FromDateTime(DateTime.Now); + _selectedMerchantId = ""; + _selectedStatus = ""; + await LoadSummaryData(); + } +} + diff --git a/EstateManagementUI.BlazorServer/Models/Models.cs b/EstateManagementUI.BlazorServer/Models/Models.cs index de66d550..b61f472a 100644 --- a/EstateManagementUI.BlazorServer/Models/Models.cs +++ b/EstateManagementUI.BlazorServer/Models/Models.cs @@ -252,3 +252,16 @@ public class MerchantSettlementHistoryModel public int TransactionCount { get; set; } public decimal NetAmountPaid { get; set; } } + +// Settlement Summary Models +public class SettlementSummaryModel +{ + public DateTime SettlementPeriodStart { get; set; } + public DateTime SettlementPeriodEnd { get; set; } + public Guid MerchantId { get; set; } + public string? MerchantName { get; set; } + public decimal GrossTransactionValue { get; set; } + public decimal TotalFees { get; set; } + public decimal NetSettlementAmount { get; set; } + public string? SettlementStatus { get; set; } +} diff --git a/EstateManagementUI.BlazorServer/Requests/Requests.cs b/EstateManagementUI.BlazorServer/Requests/Requests.cs index e34c0fee..0c7e48e0 100644 --- a/EstateManagementUI.BlazorServer/Requests/Requests.cs +++ b/EstateManagementUI.BlazorServer/Requests/Requests.cs @@ -42,6 +42,7 @@ public record GetMerchantTransactionSummaryQuery(CorrelationId CorrelationId, st public record GetProductPerformanceQuery(CorrelationId CorrelationId, string AccessToken, Guid EstateId, DateTime StartDate, DateTime EndDate) : IRequest>>; public record GetOperatorTransactionSummaryQuery(CorrelationId CorrelationId, string AccessToken, Guid EstateId, DateTime StartDate, DateTime EndDate, Guid? MerchantId = null, Guid? OperatorId = null) : IRequest>>; public record GetMerchantSettlementHistoryQuery(CorrelationId CorrelationId, string AccessToken, Guid EstateId, Guid? MerchantId, DateTime StartDate, DateTime EndDate) : IRequest>>; + public record GetSettlementSummaryQuery(CorrelationId CorrelationId, string AccessToken, Guid EstateId, DateTime StartDate, DateTime EndDate, Guid? MerchantId = null, string? Status = null) : IRequest>>; } public static class Commands diff --git a/EstateManagementUI.BlazorServer/Services/TestMediatorService.cs b/EstateManagementUI.BlazorServer/Services/TestMediatorService.cs index 43cd0773..057ec3e0 100644 --- a/EstateManagementUI.BlazorServer/Services/TestMediatorService.cs +++ b/EstateManagementUI.BlazorServer/Services/TestMediatorService.cs @@ -60,6 +60,7 @@ public Task Send(IRequest request, Cancellation Queries.GetProductPerformanceQuery query => Task.FromResult((TResponse)(object)Result>.Success(GetMockProductPerformance(query))), Queries.GetOperatorTransactionSummaryQuery query => Task.FromResult((TResponse)(object)Result>.Success(GetMockOperatorTransactionSummary(query))), Queries.GetMerchantSettlementHistoryQuery query => Task.FromResult((TResponse)(object)Result>.Success(GetMockMerchantSettlementHistory(query))), + Queries.GetSettlementSummaryQuery query => Task.FromResult((TResponse)(object)Result>.Success(GetMockSettlementSummary(query))), // Commands - execute against test data store Commands.CreateMerchantCommand cmd => Task.FromResult((TResponse)(object)ExecuteCreateMerchant(cmd)), @@ -734,6 +735,54 @@ private Result ExecuteRemoveOperatorFromEstate(Commands.RemoveOperatorFromEstate return Result.Success(); } + private List GetMockSettlementSummary(Queries.GetSettlementSummaryQuery query) + { + var merchants = _testDataStore.GetMerchants(query.EstateId); + + // Use date range as seed for consistent but varying data + var seed = query.StartDate.GetHashCode() ^ query.EndDate.GetHashCode(); + var random = new Random(seed); + + // Settlement statuses to distribute (mock data for testing) + var statuses = new[] { "settled", "settled", "settled", "pending", "failed" }; + // Mock fee percentage - in production this would come from the settlement engine API + const decimal DefaultFeePercentage = 0.025m; // 2.5% fee rate for mock data + + var summary = merchants.Select(merchant => + { + var grossValue = (decimal)(random.NextDouble() * 100000 + 10000); + var totalFees = Math.Round(grossValue * DefaultFeePercentage, 2); + var netAmount = Math.Round(grossValue - totalFees, 2); + var status = statuses[random.Next(statuses.Length)]; + + return new SettlementSummaryModel + { + SettlementPeriodStart = query.StartDate, + SettlementPeriodEnd = query.EndDate, + MerchantId = merchant.MerchantId, + MerchantName = merchant.MerchantName ?? "Unknown Merchant", + GrossTransactionValue = Math.Round(grossValue, 2), + TotalFees = totalFees, + NetSettlementAmount = netAmount, + SettlementStatus = status + }; + }).ToList(); + + // Apply filters if specified + if (query.MerchantId.HasValue) + { + summary = summary.Where(s => s.MerchantId == query.MerchantId.Value).ToList(); + } + + if (!string.IsNullOrEmpty(query.Status)) + { + summary = summary.Where(s => s.SettlementStatus != null && + s.SettlementStatus.Equals(query.Status, StringComparison.OrdinalIgnoreCase)).ToList(); + } + + return summary; + } + private List GetMockOperatorTransactionSummary(Queries.GetOperatorTransactionSummaryQuery query) { var operators = _testDataStore.GetOperators(query.EstateId);