diff --git a/EstateManagementUI.BlazorServer.Tests/Pages/BaseTest.cs b/EstateManagementUI.BlazorServer.Tests/Pages/BaseTest.cs index 8bfee122..82917c24 100644 --- a/EstateManagementUI.BlazorServer.Tests/Pages/BaseTest.cs +++ b/EstateManagementUI.BlazorServer.Tests/Pages/BaseTest.cs @@ -35,6 +35,7 @@ protected BaseTest() { this.Services.AddSingleton(this.EstateUIService.Object); this.Services.AddSingleton(this.OperatorUIService.Object); this.Services.AddSingleton(this.ContractUIService.Object); + this.Services.AddSingleton(this.MerchantUIService.Object); // Add required permission components that render their children @@ -57,6 +58,8 @@ protected BaseTest() { protected readonly Mock EstateUIService = new Mock(); protected readonly Mock OperatorUIService = new Mock(); protected readonly Mock ContractUIService = new Mock(); + protected readonly Mock MerchantUIService = new Mock(); + /// /// Minimal test double for NavigationManager. /// Register in DI as NavigationManager so components receive it in tests. diff --git a/EstateManagementUI.BlazorServer.Tests/Pages/Merchants/MerchantsIndexPageTests.cs b/EstateManagementUI.BlazorServer.Tests/Pages/Merchants/MerchantsIndexPageTests.cs index 61ee46f4..92cbf549 100644 --- a/EstateManagementUI.BlazorServer.Tests/Pages/Merchants/MerchantsIndexPageTests.cs +++ b/EstateManagementUI.BlazorServer.Tests/Pages/Merchants/MerchantsIndexPageTests.cs @@ -1,5 +1,6 @@ using Bunit; using EstateManagementUI.BlazorServer.Components.Permissions; +using EstateManagementUI.BlazorServer.Models; using EstateManagementUI.BlazorServer.Permissions; using EstateManagementUI.BlazorServer.Tests.Pages.FileProcessing; using EstateManagementUI.BusinessLogic.BackendAPI.DataTransferObjects; @@ -21,16 +22,19 @@ public class MerchantsIndexPageTests : BaseTest public void MerchantsIndex_InitialState_ShowsLoadingIndicator() { // Arrange - var merchants = new List + var merchants = new List { - new MerchantListModel - { + new() { MerchantId = Guid.NewGuid(), MerchantName = "Test Merchant" } }; - _mockMediator.Setup(x => x.Send(It.IsAny(), default)) + this.MerchantUIService.Setup(m => m.GetMerchants(It.IsAny(), It.IsAny(), It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny())) .ReturnsAsync(Result.Success(merchants)); // Act @@ -44,8 +48,12 @@ public void MerchantsIndex_InitialState_ShowsLoadingIndicator() public void MerchantsIndex_WithNoMerchants_ShowsEmptyState() { // Arrange - var merchants = new List(); - _mockMediator.Setup(x => x.Send(It.IsAny(), default)) + var merchants = new List(); + this.MerchantUIService.Setup(m => m.GetMerchants(It.IsAny(), It.IsAny(), It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny())) .ReturnsAsync(Result.Success(merchants)); // Act @@ -60,15 +68,15 @@ public void MerchantsIndex_WithNoMerchants_ShowsEmptyState() public void MerchantsIndex_WithMerchants_DisplaysMerchantList() { // Arrange - var merchants = new List + var merchants = new List { - new MerchantListModel() + new MerchantModels.MerchantListModel() { MerchantId = Guid.NewGuid(), MerchantName = "Test Merchant 1", MerchantReference = "REF001" }, - new MerchantListModel() + new MerchantModels.MerchantListModel() { MerchantId = Guid.NewGuid(), MerchantName = "Test Merchant 2", @@ -76,9 +84,14 @@ public void MerchantsIndex_WithMerchants_DisplaysMerchantList() } }; - _mockMediator.Setup(x => x.Send(It.IsAny(), default)) + this.MerchantUIService.Setup(m => m.GetMerchants(It.IsAny(), It.IsAny(), It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny())) .ReturnsAsync(Result.Success(merchants)); + // Act var cut = RenderComponent(); cut.WaitForState(() => !cut.Markup.Contains("animate-spin"), TimeSpan.FromSeconds(5)); @@ -94,7 +107,7 @@ public void MerchantsIndex_WithMerchants_DisplaysMerchantList() public void MerchantsIndex_WithMerchants_DisplaysSummaryCards() { // Arrange - var merchants = new List + var merchants = new List { new() { @@ -108,9 +121,14 @@ public void MerchantsIndex_WithMerchants_DisplaysSummaryCards() } }; - _mockMediator.Setup(x => x.Send(It.IsAny(), default)) + this.MerchantUIService.Setup(m => m.GetMerchants(It.IsAny(), It.IsAny(), It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny())) .ReturnsAsync(Result.Success(merchants)); + // Act var cut = RenderComponent(); cut.WaitForState(() => !cut.Markup.Contains("animate-spin"), TimeSpan.FromSeconds(5)); @@ -125,9 +143,14 @@ public void MerchantsIndex_WithMerchants_DisplaysSummaryCards() public void MerchantsIndex_HasCorrectPageTitle() { // Arrange - _mockMediator.Setup(x => x.Send(It.IsAny(), default)) - .ReturnsAsync(Result.Success(new List())); - + this.MerchantUIService.Setup(m => m.GetMerchants(It.IsAny(), It.IsAny(), It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny())) + .ReturnsAsync(Result.Success(new List())); + + // Act var cut = RenderComponent(); @@ -140,18 +163,23 @@ public void MerchantsIndex_HasCorrectPageTitle() public void MerchantsIndex_WithMerchants_DisplaysFilters() { // Arrange - var merchants = new List + var merchants = new List { - new MerchantListModel() + new() { MerchantId = Guid.NewGuid(), MerchantName = "Test Merchant" } }; - _mockMediator.Setup(x => x.Send(It.IsAny(), default)) + this.MerchantUIService.Setup(m => m.GetMerchants(It.IsAny(), It.IsAny(), It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny())) .ReturnsAsync(Result.Success(merchants)); + // Act var cut = RenderComponent(); cut.WaitForState(() => !cut.Markup.Contains("animate-spin"), TimeSpan.FromSeconds(5)); @@ -169,15 +197,20 @@ public void MerchantsIndex_WithMerchants_DisplaysFilters() public void MerchantsIndex_WithManyMerchants_DisplaysPagination() { // Arrange - Create more than 10 merchants to trigger pagination - var merchants = Enumerable.Range(1, 12).Select(i => new MerchantListModel() + var merchants = Enumerable.Range(1, 12).Select(i => new MerchantModels.MerchantListModel() { MerchantId = Guid.NewGuid(), MerchantName = $"Merchant {i}" }).ToList(); - _mockMediator.Setup(x => x.Send(It.IsAny(), default)) + this.MerchantUIService.Setup(m => m.GetMerchants(It.IsAny(), It.IsAny(), It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny())) .ReturnsAsync(Result.Success(merchants)); + // Act var cut = RenderComponent(); cut.WaitForState(() => !cut.Markup.Contains("animate-spin"), TimeSpan.FromSeconds(5)); @@ -192,9 +225,9 @@ public void MerchantsIndex_WithManyMerchants_DisplaysPagination() public void MerchantsIndex_WithMerchants_DisplaysRegionAndPostcode() { // Arrange - var merchants = new List + var merchants = new List { - new MerchantListModel + new() { MerchantId = Guid.NewGuid(), MerchantName = "Test Merchant", @@ -203,9 +236,14 @@ public void MerchantsIndex_WithMerchants_DisplaysRegionAndPostcode() } }; - _mockMediator.Setup(x => x.Send(It.IsAny(), default)) + this.MerchantUIService.Setup(m => m.GetMerchants(It.IsAny(), It.IsAny(), It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny())) .ReturnsAsync(Result.Success(merchants)); + // Act var cut = RenderComponent(); cut.WaitForState(() => !cut.Markup.Contains("animate-spin"), TimeSpan.FromSeconds(5)); diff --git a/EstateManagementUI.BlazorServer.Tests/Pages/Merchants/MerchantsViewPageTests.cs b/EstateManagementUI.BlazorServer.Tests/Pages/Merchants/MerchantsViewPageTests.cs index 18235278..88ba5fd9 100644 --- a/EstateManagementUI.BlazorServer.Tests/Pages/Merchants/MerchantsViewPageTests.cs +++ b/EstateManagementUI.BlazorServer.Tests/Pages/Merchants/MerchantsViewPageTests.cs @@ -2,6 +2,7 @@ using EstateManagementUI.BlazorServer.Components.Pages.Merchants; using EstateManagementUI.BlazorServer.Models; using EstateManagementUI.BlazorServer.Tests.Pages.FileProcessing; +using EstateManagementUI.BusinessLogic.BackendAPI.DataTransferObjects; using EstateManagementUI.BusinessLogic.Models; using EstateManagementUI.BusinessLogic.Requests; using MediatR; @@ -24,21 +25,18 @@ public void MerchantsView_InitialState_ShowsLoadingIndicator() { // Arrange var merchantId = Guid.NewGuid(); - var merchant = new MerchantModel + var merchant = new MerchantModels.MerchantModel { MerchantId = merchantId, MerchantName = "Test Merchant", MerchantReference = "REF001" }; - - _mockMediator.Setup(x => x.Send(It.IsAny(), default)) + + this.MerchantUIService.Setup(m => m.GetMerchant(It.IsAny(), It.IsAny(), It.IsAny())) .ReturnsAsync(Result.Success(merchant)); - _mockMediator.Setup(x => x.Send(It.IsAny(), default)) - .ReturnsAsync(Result.Success(new List())); - _mockMediator.Setup(x => x.Send(It.IsAny(), default)) - .ReturnsAsync(Result.Success(new List())); - _mockMediator.Setup(x => x.Send(It.IsAny(), default)) - .ReturnsAsync(Result.Success(new List())); + this.MerchantUIService.Setup(m => m.GetMerchantOperators(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(Result.Success(new List())); + this.MerchantUIService.Setup(m => m.GetMerchantContracts(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(Result.Success(new List())); + this.MerchantUIService.Setup(m => m.GetMerchantDevices(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(Result.Success(new List())); // Act var cut = RenderComponent(parameters => parameters @@ -53,21 +51,19 @@ public void MerchantsView_WithMerchant_DisplaysMerchantName() { // Arrange var merchantId = Guid.NewGuid(); - var merchant = new MerchantModel + var merchant = new MerchantModels.MerchantModel { MerchantId = merchantId, MerchantName = "Test Merchant", MerchantReference = "REF001" }; - - _mockMediator.Setup(x => x.Send(It.IsAny(), default)) + + this.MerchantUIService.Setup(m => m.GetMerchant(It.IsAny(), It.IsAny(), It.IsAny())) .ReturnsAsync(Result.Success(merchant)); - _mockMediator.Setup(x => x.Send(It.IsAny(), default)) - .ReturnsAsync(Result.Success(new List())); - _mockMediator.Setup(x => x.Send(It.IsAny(), default)) - .ReturnsAsync(Result.Success(new List())); - _mockMediator.Setup(x => x.Send(It.IsAny(), default)) - .ReturnsAsync(Result.Success(new List())); + this.MerchantUIService.Setup(m => m.GetMerchantOperators(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(Result.Success(new List())); + this.MerchantUIService.Setup(m => m.GetMerchantContracts(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(Result.Success(new List())); + this.MerchantUIService.Setup(m => m.GetMerchantDevices(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(Result.Success(new List())); + // Act var cut = RenderComponent(parameters => parameters @@ -84,14 +80,19 @@ public void MerchantsView_HasCorrectPageTitle() { // Arrange var merchantId = Guid.NewGuid(); - _mockMediator.Setup(x => x.Send(It.IsAny(), default)) - .ReturnsAsync(Result.Success(new MerchantModel { MerchantId = merchantId })); - _mockMediator.Setup(x => x.Send(It.IsAny(), default)) - .ReturnsAsync(Result.Success(new List())); - _mockMediator.Setup(x => x.Send(It.IsAny(), default)) - .ReturnsAsync(Result.Success(new List())); - _mockMediator.Setup(x => x.Send(It.IsAny(), default)) - .ReturnsAsync(Result.Success(new List())); + var merchant = new MerchantModels.MerchantModel + { + MerchantId = merchantId, + MerchantName = "Test Merchant", + MerchantReference = "REF001" + }; + + this.MerchantUIService.Setup(m => m.GetMerchant(It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(Result.Success(merchant)); + this.MerchantUIService.Setup(m => m.GetMerchantOperators(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(Result.Success(new List())); + this.MerchantUIService.Setup(m => m.GetMerchantContracts(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(Result.Success(new List())); + this.MerchantUIService.Setup(m => m.GetMerchantDevices(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(Result.Success(new List())); + // Act var cut = RenderComponent(parameters => parameters .Add(p => p.MerchantId, merchantId)); @@ -106,21 +107,20 @@ public void MerchantsView_HasBackButton() { // Arrange var merchantId = Guid.NewGuid(); - var merchant = new MerchantModel + var merchant = new MerchantModels.MerchantModel { MerchantId = merchantId, - MerchantName = "Test Merchant" + MerchantName = "Test Merchant", + MerchantReference = "REF001" }; - - _mockMediator.Setup(x => x.Send(It.IsAny(), default)) + + this.MerchantUIService.Setup(m => m.GetMerchant(It.IsAny(), It.IsAny(), It.IsAny())) .ReturnsAsync(Result.Success(merchant)); - _mockMediator.Setup(x => x.Send(It.IsAny(), default)) - .ReturnsAsync(Result.Success(new List())); - _mockMediator.Setup(x => x.Send(It.IsAny(), default)) - .ReturnsAsync(Result.Success(new List())); - _mockMediator.Setup(x => x.Send(It.IsAny(), default)) - .ReturnsAsync(Result.Success(new List())); - + this.MerchantUIService.Setup(m => m.GetMerchantOperators(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(Result.Success(new List())); + this.MerchantUIService.Setup(m => m.GetMerchantContracts(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(Result.Success(new List())); + this.MerchantUIService.Setup(m => m.GetMerchantDevices(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(Result.Success(new List())); + + // Act var cut = RenderComponent(parameters => parameters .Add(p => p.MerchantId, merchantId)); diff --git a/EstateManagementUI.BlazorServer.Tests/UIServices/MerchantUIServiceTests.cs b/EstateManagementUI.BlazorServer.Tests/UIServices/MerchantUIServiceTests.cs new file mode 100644 index 00000000..1f4867c1 --- /dev/null +++ b/EstateManagementUI.BlazorServer.Tests/UIServices/MerchantUIServiceTests.cs @@ -0,0 +1,410 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using EstateManagementUI.BlazorServer.UIServices; +using EstateManagementUI.BusinessLogic.Requests; +using Moq; +using Shouldly; +using SimpleResults; +using Xunit; +using MediatR; + +namespace EstateManagementUI.BlazorServer.Tests.UIServices +{ + public class MerchantUIServiceTests + { + private readonly Mock _mockMediator; + private readonly MerchantUIService _service; + + public MerchantUIServiceTests() + { + _mockMediator = new Mock(); + _service = new MerchantUIService(_mockMediator.Object); + } + + [Fact] + public async Task GetMerchant_ReturnsMappedModel_WhenMediatorSucceeds() + { + // Arrange + var estateId = Guid.NewGuid(); + var merchantId = Guid.NewGuid(); + var biz = new BusinessLogic.Models.MerchantModel + { + MerchantId = merchantId, + MerchantName = "M1", + MerchantReference = "REF1", + Balance = 10m, + AvailableBalance = 8m, + SettlementSchedule = "Daily", + Town = "T", + Region = "R", + PostalCode = "P", + Country = "C", + ContactName = "Contact", + ContactEmailAddress = "c@x", + ContactPhoneNumber = "123", + AddressId = Guid.NewGuid(), + ContactId = Guid.NewGuid() + }; + + _mockMediator + .Setup(m => m.Send(It.IsAny(), It.IsAny())) + .ReturnsAsync(Result.Success(biz)); + + // Act + var result = await _service.GetMerchant(CorrelationIdHelper.New(), estateId, merchantId); + + // Assert + result.IsSuccess.ShouldBeTrue(); + var model = result.Data!; + model.MerchantId.ShouldBe(merchantId); + model.MerchantName.ShouldBe("M1"); + model.MerchantReference.ShouldBe("REF1"); + model.Balance.ShouldBe(10m); + model.AvailableBalance.ShouldBe(8m); + model.SettlementSchedule.ShouldBe("Daily"); + model.ContactName.ShouldBe("Contact"); + } + + [Fact] + public async Task GetMerchant_ReturnsFailure_WhenMediatorFails() + { + // Arrange + _mockMediator + .Setup(m => m.Send(It.IsAny(), It.IsAny())) + .ReturnsAsync(Result.Failure("err")); + + // Act + var result = await _service.GetMerchant(CorrelationIdHelper.New(), Guid.NewGuid(), Guid.NewGuid()); + + // Assert + result.IsFailed.ShouldBeTrue(); + } + + [Fact] + public async Task GetMerchants_ReturnsMappedList_WhenMediatorSucceeds() + { + // Arrange + var estateId = Guid.NewGuid(); + var bizList = new List + { + new() { MerchantId = Guid.NewGuid(), MerchantName = "M1", MerchantReference = "R1", Balance = 1m, AvailableBalance = 1m, SettlementSchedule = "S", Region = "Reg", PostalCode = "PC", CreatedDateTime = DateTime.UtcNow } + }; + + _mockMediator + .Setup(m => m.Send(It.IsAny(), It.IsAny())) + .ReturnsAsync(Result.Success(bizList)); + + // Act + var result = await _service.GetMerchants(CorrelationIdHelper.New(), estateId, "n", "r", null, "reg", "pc"); + + // Assert + result.IsSuccess.ShouldBeTrue(); + result.Data!.Count.ShouldBe(1); + result.Data![0].MerchantName.ShouldBe("M1"); + result.Data![0].MerchantReference.ShouldBe("R1"); + } + + [Fact] + public async Task GetMerchants_ReturnsFailure_WhenMediatorFails() + { + _mockMediator + .Setup(m => m.Send(It.IsAny(), It.IsAny())) + .ReturnsAsync(Result.Failure("err")); + + var result = await _service.GetMerchants(CorrelationIdHelper.New(), Guid.NewGuid(), "", "", null, "", ""); + + result.IsFailed.ShouldBeTrue(); + } + + [Fact] + public async Task GetMerchantOperators_ReturnsMappedList_WhenMediatorSucceeds() + { + var estateId = Guid.NewGuid(); + var merchantId = Guid.NewGuid(); + var bizList = new List + { + new() { MerchantId = merchantId, OperatorId = Guid.NewGuid(), OperatorName = "Op1", MerchantNumber = "MN", TerminalNumber = "TN", IsDeleted = false } + }; + + _mockMediator + .Setup(m => m.Send(It.IsAny(), It.IsAny())) + .ReturnsAsync(Result.Success(bizList)); + + var result = await _service.GetMerchantOperators(CorrelationIdHelper.New(), estateId, merchantId); + + result.IsSuccess.ShouldBeTrue(); + result.Data!.Count.ShouldBe(1); + result.Data![0].OperatorName.ShouldBe("Op1"); + } + + [Fact] + public async Task GetMerchantOperators_ReturnsFailure_WhenMediatorFails() + { + _mockMediator + .Setup(m => m.Send(It.IsAny(), It.IsAny())) + .ReturnsAsync(Result.Failure("err")); + + var result = await _service.GetMerchantOperators(CorrelationIdHelper.New(), Guid.NewGuid(), Guid.NewGuid()); + + result.IsFailed.ShouldBeTrue(); + } + + [Fact] + public async Task GetMerchantContracts_ReturnsMappedList_WhenMediatorSucceeds() + { + var estateId = Guid.NewGuid(); + var merchantId = Guid.NewGuid(); + var bizList = new List + { + new() { MerchantId = merchantId, ContractId = Guid.NewGuid(), ContractName = "C1", OperatorName = "Op" , IsDeleted=false, ContractProducts = new List() } + }; + + _mockMediator + .Setup(m => m.Send(It.IsAny(), It.IsAny())) + .ReturnsAsync(Result.Success(bizList)); + + var result = await _service.GetMerchantContracts(CorrelationIdHelper.New(), estateId, merchantId); + + result.IsSuccess.ShouldBeTrue(); + result.Data!.Count.ShouldBe(1); + result.Data![0].ContractName.ShouldBe("C1"); + } + + [Fact] + public async Task GetMerchantContracts_ReturnsFailure_WhenMediatorFails() + { + _mockMediator + .Setup(m => m.Send(It.IsAny(), It.IsAny())) + .ReturnsAsync(Result.Failure("err")); + + var result = await _service.GetMerchantContracts(CorrelationIdHelper.New(), Guid.NewGuid(), Guid.NewGuid()); + + result.IsFailed.ShouldBeTrue(); + } + + [Fact] + public async Task GetMerchantDevices_ReturnsMappedList_WhenMediatorSucceeds() + { + var estateId = Guid.NewGuid(); + var merchantId = Guid.NewGuid(); + var bizList = new List + { + new() { MerchantId = merchantId, DeviceId = Guid.NewGuid(), DeviceIdentifier = "dev1", IsDeleted = false } + }; + + _mockMediator + .Setup(m => m.Send(It.IsAny(), It.IsAny())) + .ReturnsAsync(Result.Success(bizList)); + + var result = await _service.GetMerchantDevices(CorrelationIdHelper.New(), estateId, merchantId); + + result.IsSuccess.ShouldBeTrue(); + result.Data!.Count.ShouldBe(1); + result.Data![0].DeviceIdentifier.ShouldBe("dev1"); + } + + [Fact] + public async Task GetMerchantDevices_ReturnsFailure_WhenMediatorFails() + { + _mockMediator + .Setup(m => m.Send(It.IsAny(), It.IsAny())) + .ReturnsAsync(Result.Failure("err")); + + var result = await _service.GetMerchantDevices(CorrelationIdHelper.New(), Guid.NewGuid(), Guid.NewGuid()); + + result.IsFailed.ShouldBeTrue(); + } + + [Fact] + public async Task CreateMerchant_SendsCreateCommand_AndReturnsSuccess() + { + var estateId = Guid.NewGuid(); + var createModel = new BlazorServer.Models.MerchantModels.CreateMerchantModel + { + MerchantName = "NewM", + SettlementSchedule = "S", + AddressLine1 = "A1", + Town = "T", + Region = "R", + PostCode = "P", + Country = "C", + ContactName = "CN", + EmailAddress = "e@x", + PhoneNumber = "ph" + }; + + _mockMediator + .Setup(m => m.Send(It.IsAny(), It.IsAny())) + .ReturnsAsync(Result.Success); + + var result = await _service.CreateMerchant(CorrelationIdHelper.New(), estateId, Guid.NewGuid(), createModel); + + result.IsSuccess.ShouldBeTrue(); + _mockMediator.Verify(m => m.Send(It.Is(c => + c.EstateId == estateId && + c.Name == createModel.MerchantName && + c.SettlementSchedule == createModel.SettlementSchedule && + c.MerchantAddress.AddressLine1 == createModel.AddressLine1 && + c.MerchantAddress.Town == createModel.Town && + c.MerchantAddress.Region == createModel.Region && + c.MerchantAddress.PostalCode == createModel.PostCode && + c.MerchantAddress.Country == createModel.Country && + c.MerchantContact.ContactName == createModel.ContactName && + c.MerchantContact.ContactEmail == createModel.EmailAddress && + c.MerchantContact.ContactPhone == createModel.PhoneNumber + ), It.IsAny()), Times.Once); + } + + [Fact] + public async Task CreateMerchant_ReturnsFailure_WhenMediatorFails() + { + _mockMediator + .Setup(m => m.Send(It.IsAny(), It.IsAny())) + .ReturnsAsync(Result.Failure); + + var result = await _service.CreateMerchant(CorrelationIdHelper.New(), Guid.NewGuid(), Guid.NewGuid(), new BlazorServer.Models.MerchantModels.CreateMerchantModel { MerchantName = "x", SettlementSchedule = "s" }); + + result.IsFailed.ShouldBeTrue(); + } + + [Fact] + public async Task UpdateMerchant_SendsUpdateCommand_AndReturnsSuccess() + { + var estateId = Guid.NewGuid(); + var merchantId = Guid.NewGuid(); + + var editModel = new BlazorServer.Models.MerchantModels.MerchantEditModel + { + MerchantName = "Upd", + SettlementSchedule = "S", + AddressLine1 = "A1", + Town = "T", + Region = "R", + PostalCode = "P", + Country = "C", + ContactName = "CN", + ContactEmailAddress = "e@x", + ContactPhoneNumber = "ph" + }; + + _mockMediator + .Setup(m => m.Send(It.IsAny(), It.IsAny())) + .ReturnsAsync(Result.Success); + + var result = await _service.UpdateMerchant(CorrelationIdHelper.New(), estateId, merchantId, editModel); + + result.IsSuccess.ShouldBeTrue(); + _mockMediator.Verify(m => m.Send(It.Is(c => + c.EstateId == estateId && + c.MerchantId == merchantId && + c.Name == editModel.MerchantName && + c.SettlementSchedule == editModel.SettlementSchedule && + c.MerchantAddress.AddressLine1 == editModel.AddressLine1 && + c.MerchantContact.ContactName == editModel.ContactName + ), It.IsAny()), Times.Once); + } + + [Fact] + public async Task UpdateMerchant_ReturnsFailure_WhenMediatorFails() + { + _mockMediator + .Setup(m => m.Send(It.IsAny(), It.IsAny())) + .ReturnsAsync(Result.Failure); + + var result = await _service.UpdateMerchant(CorrelationIdHelper.New(), Guid.NewGuid(), Guid.NewGuid(), new BlazorServer.Models.MerchantModels.MerchantEditModel { MerchantName = "x", SettlementSchedule = "s" }); + + result.IsFailed.ShouldBeTrue(); + } + + [Fact] + public async Task AddAndRemoveOperatorToMerchant_SendCorrectCommands() + { + var estateId = Guid.NewGuid(); + var merchantId = Guid.NewGuid(); + var operatorId = Guid.NewGuid(); + + _mockMediator.Setup(m => m.Send(It.IsAny(), It.IsAny())).ReturnsAsync(Result.Success); + _mockMediator.Setup(m => m.Send(It.IsAny(), It.IsAny())).ReturnsAsync(Result.Success); + + var addResult = await _service.AddOperatorToMerchant(CorrelationIdHelper.New(), estateId, merchantId, operatorId, "MN", "TN"); + var removeResult = await _service.RemoveOperatorFromMerchant(CorrelationIdHelper.New(), estateId, merchantId, operatorId); + + addResult.IsSuccess.ShouldBeTrue(); + removeResult.IsSuccess.ShouldBeTrue(); + + _mockMediator.Verify(m => m.Send(It.Is(c => + c.EstateId == estateId && + c.MerchantId == merchantId && + c.OperatorId == operatorId && + c.MerchantNumber == "MN" && + c.TerminalNumber == "TN" + ), It.IsAny()), Times.Once); + + _mockMediator.Verify(m => m.Send(It.Is(c => + c.EstateId == estateId && + c.MerchantId == merchantId && + c.OperatorId == operatorId + ), It.IsAny()), Times.Once); + } + + [Fact] + public async Task AssignAndRemoveContractFromMerchant_SendCorrectCommands() + { + var estateId = Guid.NewGuid(); + var merchantId = Guid.NewGuid(); + var contractId = Guid.NewGuid(); + + _mockMediator.Setup(m => m.Send(It.IsAny(), It.IsAny())).ReturnsAsync(Result.Success); + _mockMediator.Setup(m => m.Send(It.IsAny(), It.IsAny())).ReturnsAsync(Result.Success); + + var assign = await _service.AssignContractToMerchant(CorrelationIdHelper.New(), estateId, merchantId, contractId); + var remove = await _service.RemoveContractFromMerchant(CorrelationIdHelper.New(), estateId, merchantId, contractId); + + assign.IsSuccess.ShouldBeTrue(); + remove.IsSuccess.ShouldBeTrue(); + + _mockMediator.Verify(m => m.Send(It.Is(c => + c.EstateId == estateId && c.MerchantId == merchantId && c.ContractId == contractId + ), It.IsAny()), Times.Once); + + _mockMediator.Verify(m => m.Send(It.Is(c => + c.EstateId == estateId && c.MerchantId == merchantId && c.ContractId == contractId + ), It.IsAny()), Times.Once); + } + + [Fact] + public async Task AddSwapDeviceAndMakeDeposit_SendCorrectCommands() + { + var estateId = Guid.NewGuid(); + var merchantId = Guid.NewGuid(); + + _mockMediator.Setup(m => m.Send(It.IsAny(), It.IsAny())).ReturnsAsync(Result.Success); + _mockMediator.Setup(m => m.Send(It.IsAny(), It.IsAny())).ReturnsAsync(Result.Success); + _mockMediator.Setup(m => m.Send(It.IsAny(), It.IsAny())).ReturnsAsync(Result.Success); + + var addDevice = await _service.AddMerchantDevice(CorrelationIdHelper.New(), estateId, merchantId, "dev1"); + var swapDevice = await _service.SwapMerchantDevice(CorrelationIdHelper.New(), estateId, merchantId, "old", "new"); + var depositModel = new BlazorServer.Models.MerchantModels.DepositModel { Amount = 12, Date = DateTime.UtcNow, Reference = "note" }; + var deposit = await _service.MakeMerchantDeposit(CorrelationIdHelper.New(), estateId, merchantId, depositModel); + + addDevice.IsSuccess.ShouldBeTrue(); + swapDevice.IsSuccess.ShouldBeTrue(); + deposit.IsSuccess.ShouldBeTrue(); + + _mockMediator.Verify(m => m.Send(It.Is(c => + c.EstateId == estateId && c.MerchantId == merchantId && c.DeviceIdentifier == "dev1" + ), It.IsAny()), Times.Once); + + _mockMediator.Verify(m => m.Send(It.Is(c => + c.EstateId == estateId && c.MerchantId == merchantId && c.OldDevice == "old" && c.NewDevice == "new" + ), It.IsAny()), Times.Once); + + _mockMediator.Verify(m => m.Send(It.Is(c => + c.EstateId == estateId && c.MerchantId == merchantId && c.Amount == depositModel.Amount && c.Reference == depositModel.Reference + ), It.IsAny()), Times.Once); + } + } +} \ No newline at end of file diff --git a/EstateManagementUI.BlazorServer/Components/Pages/Merchants/Deposit.razor b/EstateManagementUI.BlazorServer/Components/Pages/Merchants/Deposit.razor index c6faaa94..24fc0f0a 100644 --- a/EstateManagementUI.BlazorServer/Components/Pages/Merchants/Deposit.razor +++ b/EstateManagementUI.BlazorServer/Components/Pages/Merchants/Deposit.razor @@ -2,11 +2,11 @@ @rendermode InteractiveServer @using System.ComponentModel.DataAnnotations @using EstateManagementUI.BlazorServer.Permissions +@using EstateManagementUI.BlazorServer.UIServices @using EstateManagementUI.BusinessLogic.Requests -@inject IMediator Mediator +@inject IMerchantUIService MerchantUiService @inherits AuthorizedComponentBase @inject NavigationManager NavigationManager -@inject IPermissionService PermissionService Make Merchant Deposit diff --git a/EstateManagementUI.BlazorServer/Components/Pages/Merchants/Deposit.razor.cs b/EstateManagementUI.BlazorServer/Components/Pages/Merchants/Deposit.razor.cs index 780a37be..771dc93d 100644 --- a/EstateManagementUI.BlazorServer/Components/Pages/Merchants/Deposit.razor.cs +++ b/EstateManagementUI.BlazorServer/Components/Pages/Merchants/Deposit.razor.cs @@ -1,6 +1,10 @@ -using EstateManagementUI.BlazorServer.Permissions; +using EstateManagementUI.BlazorServer.Models; +using EstateManagementUI.BlazorServer.Permissions; +using EstateManagementUI.BusinessLogic.BackendAPI.DataTransferObjects; using EstateManagementUI.BusinessLogic.Requests; using Microsoft.AspNetCore.Components; +using Shared.Results; +using SimpleResults; using System.ComponentModel.DataAnnotations; namespace EstateManagementUI.BlazorServer.Components.Pages.Merchants @@ -10,123 +14,68 @@ public partial class Deposit [Parameter] public Guid MerchantId { get; set; } - private DepositModel model = new(); + private MerchantModels.DepositModel model = new(); private bool isSaving = false; - private string? errorMessage; - private string? successMessage; private string? merchantName; - private bool hasPermission = false; protected override async Task OnAfterRenderAsync(bool firstRender) { - if (!firstRender) { - await base.OnAfterRenderAsync(firstRender); + if (!firstRender) + { return; } - await RequirePermission(PermissionSection.Merchant, PermissionFunction.MakeDeposit); - - // Set default date to today - model.Date = DateTime.Today; - - // Load merchant name - try - { - var correlationId = new CorrelationId(Guid.NewGuid()); - var estateId = await this.GetEstateId(); - - var result = await Mediator.Send(new MerchantQueries.GetMerchantQuery(correlationId, estateId, MerchantId)); - if (result.IsSuccess && result.Data != null) - { - merchantName = result.Data.MerchantName; - } - } - catch (Exception ex) + Result result = await OnAfterRender(PermissionSection.Merchant, PermissionFunction.MakeDeposit, this.LoadMerchant); + if (result.IsFailed) { - errorMessage = $"Failed to load merchant details: {ex.Message}"; + return; } + this.StateHasChanged(); } - private async Task HandleSubmit() - { + private async Task LoadMerchant() { + CorrelationId correlationId = new(Guid.NewGuid()); + Guid estateId = await this.GetEstateId(); + + Result getMerchantResult = await this.MerchantUiService.GetMerchant(correlationId, estateId, MerchantId); + + if (getMerchantResult.IsFailed) + return ResultHelpers.CreateFailure(getMerchantResult); + + this.merchantName = getMerchantResult.Data.MerchantName; + + return Result.Success(); + } + + private async Task HandleSubmit() { isSaving = true; - errorMessage = null; - successMessage = null; + this.ClearMessages(); + + var correlationId = new CorrelationId(Guid.NewGuid()); + var estateId = await this.GetEstateId(); + + var result = await this.MerchantUiService.MakeMerchantDeposit(correlationId, estateId, MerchantId, model); + + if (result.IsSuccess) { + successMessage = "Deposit recorded successfully"; - try - { - var correlationId = new CorrelationId(Guid.NewGuid()); - var estateId = await this.GetEstateId(); - - var command = new MerchantCommands.MakeMerchantDepositCommand( - correlationId, - estateId, - MerchantId, - model.Amount, - model.Date, - model.Reference! - ); - - var result = await Mediator.Send(command); - - if (!result.IsSuccess) - { - errorMessage = result.Message ?? "Failed to make deposit"; - return; - } - - // Show success message briefly before navigating away - successMessage = "Deposit recorded successfully."; StateHasChanged(); // Small delay so user sees confirmation (adjust duration as needed) await this.WaitOnUIRefresh(); - // Navigate back to merchant list on success + // Navigate to contracts list with success NavigationManager.NavigateTo("/merchants"); } - catch (Exception ex) - { - errorMessage = $"An error occurred: {ex.Message}"; - } - finally - { - isSaving = false; + else { + this.errorMessage = "Failed to make deposit"; } + + isSaving = false; } private void Cancel() { NavigationManager.NavigateTo("/merchants"); } - - public class DepositModel - { - [Required(ErrorMessage = "Deposit amount is required")] - [Range(1, int.MaxValue, ErrorMessage = "Deposit amount must be greater than 0")] - public int Amount { get; set; } - - [Required(ErrorMessage = "Date of deposit is required")] - [DateNotInFuture(ErrorMessage = "Date cannot be in the future")] - public DateTime Date { get; set; } = DateTime.Today; - - [Required(ErrorMessage = "Reference is required")] - public string? Reference { get; set; } - } - - // Custom validation attribute for date not in future - private class DateNotInFutureAttribute : ValidationAttribute - { - protected override ValidationResult? IsValid(object? value, ValidationContext validationContext) - { - if (value is DateTime date) - { - if (date.Date > DateTime.Today) - { - return new ValidationResult(ErrorMessage ?? "Date cannot be in the future"); - } - } - return ValidationResult.Success; - } - } } } diff --git a/EstateManagementUI.BlazorServer/Components/Pages/Merchants/Edit.razor b/EstateManagementUI.BlazorServer/Components/Pages/Merchants/Edit.razor index 6e030198..1b1618e6 100644 --- a/EstateManagementUI.BlazorServer/Components/Pages/Merchants/Edit.razor +++ b/EstateManagementUI.BlazorServer/Components/Pages/Merchants/Edit.razor @@ -3,9 +3,12 @@ @using System.ComponentModel.DataAnnotations @using EstateManagementUI.BlazorServer.Factories @using EstateManagementUI.BlazorServer.Permissions +@using EstateManagementUI.BlazorServer.UIServices @using EstateManagementUI.BusinessLogic.Requests @inherits AuthorizedComponentBase -@inject IMediator Mediator +@inject IMerchantUIService MerchantUiService +@inject IOperatorUIService OperatorUiService +@inject IContractUIService ContractUiService @inject NavigationManager NavigationManager @inject IPermissionService PermissionService diff --git a/EstateManagementUI.BlazorServer/Components/Pages/Merchants/Edit.razor.cs b/EstateManagementUI.BlazorServer/Components/Pages/Merchants/Edit.razor.cs index ef109644..c47b3e28 100644 --- a/EstateManagementUI.BlazorServer/Components/Pages/Merchants/Edit.razor.cs +++ b/EstateManagementUI.BlazorServer/Components/Pages/Merchants/Edit.razor.cs @@ -10,15 +10,14 @@ using Shared.Results; using SimpleResults; using System.ComponentModel.DataAnnotations; +using static FastExpressionCompiler.ExpressionCompiler; +using Result = SimpleResults.Result; -namespace EstateManagementUI.BlazorServer.Components.Pages.Merchants -{ - public partial class Edit - { - [Parameter] - public Guid MerchantId { get; set; } +namespace EstateManagementUI.BlazorServer.Components.Pages.Merchants; + public partial class Edit { + [Parameter] public Guid MerchantId { get; set; } - private MerchantModel? merchant; + private MerchantModels.MerchantModel? merchant; private bool isLoading = true; private bool isSaving = false; private string? errorMessage; @@ -26,11 +25,11 @@ public partial class Edit private string activeTab = "details"; // Unified model for editing - private MerchantEditModel merchantEditModel = new(); + private MerchantModels.MerchantEditModel merchantEditModel = new(); // Operators private List? availableOperators; - private List assignedOperators = new(); + private List assignedOperators = new(); private bool showAddOperator = false; private string? selectedOperatorId; private OperatorModels.OperatorModel? selectedOperator; @@ -41,69 +40,44 @@ public partial class Edit // Contracts private List? availableContracts; - private List assignedContracts = new(); + private List assignedContracts = new(); private bool showAddContract = false; private string? selectedContractId; // Devices - private List assignedDevices = new(); + private List assignedDevices = new(); private bool showAddDevice = false; private string? deviceIdentifier; - protected override async Task OnAfterRenderAsync(bool firstRender) - { - if (!firstRender) - { - await base.OnAfterRenderAsync(firstRender); + protected override async Task OnAfterRenderAsync(bool firstRender) { + if (!firstRender) { return; } - try - { - await base.OnInitializedAsync(); - await RequirePermission(PermissionSection.Merchant, PermissionFunction.Edit); - - var loadMerchantResult = await LoadMerchant(); - - if (loadMerchantResult.IsFailed) { - this.NavigationManager.NavigateToErrorPage(); - } - - var loadOperatorsTask = LoadOperators(); - var loadContractsTask = LoadContracts(); - - await Task.WhenAll(loadOperatorsTask, loadContractsTask); - - if (loadOperatorsTask.Result.IsFailed || loadContractsTask.Result.IsFailed) { - this.NavigationManager.NavigateToErrorPage(); - } - - } - finally - { - isLoading = false; - this.StateHasChanged(); + Result result = await OnAfterRender(PermissionSection.Merchant, PermissionFunction.Edit, this.LoadMerchant); + if (result.IsFailed) { + return; } + + isLoading = false; + this.StateHasChanged(); } - private async Task LoadMerchant() - { - try - { + private async Task LoadMerchant() { + try { isLoading = true; var correlationId = new CorrelationId(Guid.NewGuid()); var estateId = await this.GetEstateId(); - var getMerchantResult = await Mediator.Send(new MerchantQueries.GetMerchantQuery(correlationId, estateId, MerchantId)); + var getMerchantResult = await this.MerchantUiService.GetMerchant(correlationId, estateId, MerchantId); if (getMerchantResult.IsFailed) return ResultHelpers.CreateFailure(getMerchantResult); - merchant = ModelFactory.ConvertFrom(getMerchantResult.Data); + merchant = getMerchantResult.Data; // Initialize unified model with current values - merchantEditModel = new MerchantEditModel - { + merchantEditModel = new MerchantModels.MerchantEditModel { MerchantName = merchant.MerchantName, SettlementSchedule = merchant.SettlementSchedule ?? "Immediate", AddressId = this.merchant.AddressId, @@ -119,9 +93,9 @@ private async Task LoadMerchant() ContactPhoneNumber = merchant.ContactPhoneNumber, }; - var operatorsResultTask = Mediator.Send(new MerchantQueries.GetMerchantOperatorsQuery(correlationId, estateId, MerchantId)); - var contractsResultTask = Mediator.Send(new MerchantQueries.GetMerchantContractsQuery(correlationId, estateId, MerchantId)); - var devicesResultTask = Mediator.Send(new MerchantQueries.GetMerchantDevicesQuery(correlationId, estateId, MerchantId)); + var operatorsResultTask = this.MerchantUiService.GetMerchantOperators(correlationId, estateId, MerchantId); + var contractsResultTask = this.MerchantUiService.GetMerchantContracts(correlationId, estateId, MerchantId); + var devicesResultTask = this.MerchantUiService.GetMerchantDevices(correlationId, estateId, MerchantId); await Task.WhenAll(operatorsResultTask, contractsResultTask, devicesResultTask); @@ -134,75 +108,46 @@ private async Task LoadMerchant() if (devicesResultTask.Result.IsFailed) return ResultHelpers.CreateFailure(devicesResultTask.Result); - assignedOperators = ModelFactory.ConvertFrom(operatorsResultTask.Result.Data); - assignedContracts = ModelFactory.ConvertFrom(contractsResultTask.Result.Data); - this.assignedDevices = ModelFactory.ConvertFrom(devicesResultTask.Result.Data); + assignedOperators = operatorsResultTask.Result.Data; + assignedContracts = contractsResultTask.Result.Data; + this.assignedDevices = devicesResultTask.Result.Data; - return Result.Success(); - } - finally - { - isLoading = false; - } - } - - private async Task LoadOperators() { - var correlationId = new CorrelationId(Guid.NewGuid()); - var estateId = await this.GetEstateId(); + Task>> operatorsDropDownTask = this.OperatorUiService.GetOperatorsForDropDown(correlationId, estateId); + Task>> contractsDropDownTask = this.ContractUiService.GetContractsForDropDown(correlationId, estateId); - var result = await Mediator.Send(new OperatorQueries.GetOperatorsForDropDownQuery(correlationId, estateId)); + await Task.WhenAll(operatorsDropDownTask, contractsDropDownTask); - if (result.IsFailed) { - return ResultHelpers.CreateFailure(result); - } + if (operatorsDropDownTask.Result.IsFailed) + return ResultHelpers.CreateFailure(operatorsResultTask.Result); - var unfiltered = ModelFactory.ConvertFrom(result.Data); - this.availableOperators = unfiltered.Where(u => this.assignedOperators.Select(a => a.OperatorId).Contains(u.OperatorId) == false).ToList(); + if (contractsDropDownTask.Result.IsFailed) + return ResultHelpers.CreateFailure(contractsResultTask.Result); - return Result.Success(); - } + var unfilteredOperator = operatorsDropDownTask.Result.Data; + this.availableOperators = unfilteredOperator.Where(u => this.assignedOperators.Select(a => a.OperatorId).Contains(u.OperatorId) == false).ToList(); - private async Task LoadContracts() - { - var correlationId = new CorrelationId(Guid.NewGuid()); - var estateId = await this.GetEstateId(); + var unfilteredContracts = contractsDropDownTask.Result.Data; + this.availableContracts = unfilteredContracts.Where(u => this.assignedContracts.Select(a => a.ContractId).Contains(u.ContractId) == false).ToList(); - var result = await Mediator.Send(new ContractQueries.GetContractsForDropDownQuery(correlationId, estateId)); - if (result.IsFailed) - { - return ResultHelpers.CreateFailure(result); + return Result.Success(); + } + finally { + isLoading = false; } - - var unfiltered = ModelFactory.ConvertFrom(result.Data); - this.availableContracts = unfiltered.Where(u => this.assignedContracts.Select(a => a.ContractId).Contains(u.ContractId) == false).ToList(); - - return Result.Success(); - } - - private void SetActiveTab(string tab) - { - activeTab = tab; - ClearMessages(); } - private string GetTabClass(string tab) - { + private string GetTabClass(string tab) { var baseClass = "px-6 py-3 text-sm font-medium border-b-2 transition-colors "; - return tab == activeTab - ? baseClass + "border-blue-600 text-blue-600" - : baseClass + "border-transparent text-gray-600 hover:text-gray-800 hover:border-gray-300"; + return tab == activeTab ? baseClass + "border-blue-600 text-blue-600" : baseClass + "border-transparent text-gray-600 hover:text-gray-800 hover:border-gray-300"; } - private void HandleInvalidSubmit(EditContext editContext) - { + private void HandleInvalidSubmit(EditContext editContext) { // Aggregate validation messages so the user sees why the submit didn't run var errors = editContext.GetValidationMessages().ToList(); - if (errors.Any()) - { + if (errors.Any()) { errorMessage = string.Join(" \u2014 ", errors); // em dash separator } - else - { + else { errorMessage = "Form is invalid. Please check the highlighted fields."; } @@ -210,65 +155,48 @@ private void HandleInvalidSubmit(EditContext editContext) StateHasChanged(); } - private async Task SaveAllChanges() - { + private async Task SaveAllChanges() { isSaving = true; ClearMessages(); - try - { - var correlationId = new CorrelationId(Guid.NewGuid()); - var estateId = await this.GetEstateId(); - - var address = new MerchantCommands.MerchantAddress(this.merchantEditModel.AddressId, this.merchantEditModel.AddressLine1, this.merchantEditModel.Town, this.merchantEditModel.Region, this.merchantEditModel.PostalCode, this.merchantEditModel.Country); - var contact = new MerchantCommands.MerchantContact(this.merchantEditModel.ContactId, this.merchantEditModel.ContactName, this.merchantEditModel.ContactEmailAddress, this.merchantEditModel.ContactPhoneNumber); - var updateMerchantCommand = new MerchantCommands.UpdateMerchantCommand(correlationId, estateId, this.MerchantId, this.merchantEditModel.MerchantName, this.merchantEditModel.SettlementSchedule, address, contact); + var correlationId = new CorrelationId(Guid.NewGuid()); + var estateId = await this.GetEstateId(); - var updateResult = await Mediator.Send(updateMerchantCommand); - if (updateResult.IsFailed) { - errorMessage = updateResult.Message ?? "Failed to update merchant"; - return; - } + var result = await this.MerchantUiService.UpdateMerchant(correlationId, estateId, this.MerchantId, this.merchantEditModel); - // Show success message briefly before navigating away + if (result.IsSuccess) { successMessage = "Merchant details updated successfully"; + StateHasChanged(); // Small delay so user sees confirmation (adjust duration as needed) await this.WaitOnUIRefresh(); - // Navigate to edit page - NavigationManager.NavigateTo($"/merchants"); - } - catch (Exception ex) - { - errorMessage = $"An error occurred: {ex.Message}"; + // Navigate to contracts list with success + NavigationManager.NavigateTo("/merchants"); } - finally - { - isSaving = false; + else { + this.errorMessage = "Failed to update merchant"; } + + isSaving = false; } - private async Task OnOperatorSelected() - { - if (string.IsNullOrEmpty(selectedOperatorId)) - { + private async Task OnOperatorSelected() { + if (string.IsNullOrEmpty(selectedOperatorId)) { selectedOperator = null; merchantNumber = null; terminalNumber = null; merchantNumberError = null; terminalNumberError = null; } - else - { + else { var operatorId = Guid.Parse(selectedOperatorId); - OperatorQueries.GetOperatorQuery query = new OperatorQueries.GetOperatorQuery(CorrelationIdHelper.New(), await this.GetEstateId(), operatorId); - var getOperatorResult = await this.Mediator.Send(query); - if (getOperatorResult.IsFailed) + var result = await this.OperatorUiService.GetOperator(CorrelationIdHelper.New(), await this.GetEstateId(), operatorId); + if (result.IsFailed) NavigationManager.NavigateToErrorPage(); - selectedOperator = ModelFactory.ConvertFrom(getOperatorResult.Data); + selectedOperator = result.Data; merchantNumber = null; terminalNumber = null; merchantNumberError = null; @@ -276,37 +204,29 @@ private async Task OnOperatorSelected() } } - private bool ValidateOperatorFields() - { + private bool ValidateOperatorFields() { bool isValid = true; merchantNumberError = null; terminalNumberError = null; - if (selectedOperator != null) - { - if (selectedOperator.RequireCustomMerchantNumber) - { - if (string.IsNullOrWhiteSpace(merchantNumber)) - { + if (selectedOperator != null) { + if (selectedOperator.RequireCustomMerchantNumber) { + if (string.IsNullOrWhiteSpace(merchantNumber)) { merchantNumberError = "Merchant number is required"; isValid = false; } - else if (!System.Text.RegularExpressions.Regex.IsMatch(merchantNumber, @"^\d+$")) - { + else if (!System.Text.RegularExpressions.Regex.IsMatch(merchantNumber, @"^\d+$")) { merchantNumberError = "Merchant number must be numeric"; isValid = false; } } - if (selectedOperator.RequireCustomTerminalNumber) - { - if (string.IsNullOrWhiteSpace(terminalNumber)) - { + if (selectedOperator.RequireCustomTerminalNumber) { + if (string.IsNullOrWhiteSpace(terminalNumber)) { terminalNumberError = "Terminal number is required"; isValid = false; } - else if (!System.Text.RegularExpressions.Regex.IsMatch(terminalNumber, @"^\d+$")) - { + else if (!System.Text.RegularExpressions.Regex.IsMatch(terminalNumber, @"^\d+$")) { terminalNumberError = "Terminal number must be numeric"; isValid = false; } @@ -316,269 +236,178 @@ private bool ValidateOperatorFields() return isValid; } - private async Task AddOperatorToMerchant() - { + private async Task AddOperatorToMerchant() { if (string.IsNullOrEmpty(selectedOperatorId)) return; ClearMessages(); // Validate fields - if (!ValidateOperatorFields()) - { + if (!ValidateOperatorFields()) { return; } - try - { - var correlationId = new CorrelationId(Guid.NewGuid()); - var estateId = await this.GetEstateId(); - var operatorId = Guid.Parse(selectedOperatorId); + var correlationId = new CorrelationId(Guid.NewGuid()); + var estateId = await this.GetEstateId(); + var operatorId = Guid.Parse(selectedOperatorId); - var command = new MerchantCommands.AddOperatorToMerchantCommand( - correlationId, - estateId, - MerchantId, - operatorId, - merchantNumber, - terminalNumber - ); - - var result = await Mediator.Send(command); - - if (result.IsSuccess) - { - successMessage = "Operator added successfully"; - selectedOperatorId = null; - selectedOperator = null; - merchantNumber = null; - terminalNumber = null; - merchantNumberError = null; - terminalNumberError = null; - showAddOperator = false; - - // Add to assigned list (in real implementation, reload from server) - var op = availableOperators?.FirstOrDefault(o => o.OperatorId == operatorId); - if (op != null && !assignedOperators.Any(a => a.OperatorId == operatorId)) - { - assignedOperators.Add(new MerchantOperatorModel() { - OperatorId = op.OperatorId, - OperatorName = op.OperatorName, - }); - } - } - else - { - errorMessage = result.Message ?? "Failed to add operator"; - } + var result = await this.MerchantUiService.AddOperatorToMerchant(correlationId, estateId, MerchantId, operatorId, merchantNumber, terminalNumber); + + if (result.IsSuccess) { + successMessage = "Operator added successfully"; + selectedOperatorId = null; + selectedOperator = null; + merchantNumber = null; + terminalNumber = null; + merchantNumberError = null; + terminalNumberError = null; + showAddOperator = false; + + //// Add to assigned list (in real implementation, reload from server) + //var op = availableOperators?.FirstOrDefault(o => o.OperatorId == operatorId); + //if (op != null && !assignedOperators.Any(a => a.OperatorId == operatorId)) + //{ + // assignedOperators.Add(new MerchantModels.MerchantOperatorModel() { + // OperatorId = op.OperatorId, + // OperatorName = op.OperatorName, + // }); + //} + await this.LoadMerchant(); } - catch (Exception ex) - { - errorMessage = $"An error occurred: {ex.Message}"; + else { + errorMessage = "Failed to add operator"; } } - private async Task RemoveOperatorFromMerchant(Guid operatorId) - { + private async Task RemoveOperatorFromMerchant(Guid operatorId) { ClearMessages(); - try - { - var correlationId = new CorrelationId(Guid.NewGuid()); - var estateId = await this.GetEstateId(); - - var command = new MerchantCommands.RemoveOperatorFromMerchantCommand( - correlationId, - estateId, - MerchantId, - operatorId - ); + var correlationId = new CorrelationId(Guid.NewGuid()); + var estateId = await this.GetEstateId(); - var result = await Mediator.Send(command); + var result = await this.MerchantUiService.RemoveOperatorFromMerchant(correlationId, estateId, MerchantId, operatorId); - if (result.IsSuccess) - { - successMessage = "Operator removed successfully"; - assignedOperators.RemoveAll(o => o.OperatorId == operatorId); - } - else - { - errorMessage = result.Message ?? "Failed to remove operator"; - } + if (result.IsSuccess) { + successMessage = "Operator removed successfully"; + await this.LoadMerchant(); } - catch (Exception ex) - { - errorMessage = $"An error occurred: {ex.Message}"; + else { + errorMessage = "Failed to remove operator"; } } - private async Task AssignContractToMerchant() - { + private async Task AssignContractToMerchant() { if (string.IsNullOrEmpty(selectedContractId)) return; ClearMessages(); - try - { - var correlationId = new CorrelationId(Guid.NewGuid()); - var estateId = await this.GetEstateId(); - var contractId = Guid.Parse(selectedContractId); - - var command = new MerchantCommands.AssignContractToMerchantCommand( - correlationId, - estateId, - MerchantId, - contractId - ); - - var result = await Mediator.Send(command); - - if (result.IsSuccess) - { - successMessage = "Contract assigned successfully"; - selectedContractId = null; - showAddContract = false; - - // Add to assigned list (in real implementation, reload from server) - var contract = availableContracts?.FirstOrDefault(c => c.ContractId == contractId); - if (contract != null && !assignedContracts.Any(a => a.ContractId == contractId)) - { - assignedContracts.Add(new MerchantContractModel() { - ContractName = contract.Description, - ContractId = contract.ContractId - // TODO: products - }); - } - } - else - { - errorMessage = result.Message ?? "Failed to assign contract"; - } + var correlationId = new CorrelationId(Guid.NewGuid()); + var estateId = await this.GetEstateId(); + var contractId = Guid.Parse(selectedContractId); + + var result = await this.MerchantUiService.AssignContractToMerchant(correlationId, estateId, MerchantId, contractId); + + if (result.IsSuccess) { + successMessage = "Contract assigned successfully"; + selectedContractId = null; + showAddContract = false; + + // Add to assigned list (in real implementation, reload from server) + await this.LoadMerchant(); } - catch (Exception ex) - { - errorMessage = $"An error occurred: {ex.Message}"; + else { + errorMessage = "Failed to assign contract"; } } - private async Task RemoveContractFromMerchant(Guid contractId) - { + private async Task RemoveContractFromMerchant(Guid contractId) { ClearMessages(); - try - { - var correlationId = new CorrelationId(Guid.NewGuid()); - var estateId = await this.GetEstateId(); + var correlationId = new CorrelationId(Guid.NewGuid()); + var estateId = await this.GetEstateId(); - var command = new MerchantCommands.RemoveContractFromMerchantCommand( - correlationId, - estateId, - MerchantId, - contractId - ); + var result = await this.MerchantUiService.RemoveContractFromMerchant(correlationId, estateId, MerchantId, contractId); - var result = await Mediator.Send(command); - if (result.IsSuccess) - { - successMessage = "Contract removed successfully"; - assignedContracts.RemoveAll(c => c.ContractId == contractId); - } - else - { - errorMessage = result.Message ?? "Failed to remove contract"; - } + if (result.IsSuccess) { + successMessage = "Contract removed successfully"; + await this.LoadMerchant(); } - catch (Exception ex) - { - errorMessage = $"An error occurred: {ex.Message}"; + else { + errorMessage = "Failed to remove contract"; } } - private async Task AddDeviceToMerchant() - { + private async Task AddDeviceToMerchant() { if (string.IsNullOrEmpty(deviceIdentifier)) return; ClearMessages(); - try - { - var correlationId = new CorrelationId(Guid.NewGuid()); - var estateId = await this.GetEstateId(); + var correlationId = new CorrelationId(Guid.NewGuid()); + var estateId = await this.GetEstateId(); - IRequest command = new MerchantCommands.AddMerchantDeviceCommand(correlationId, estateId, MerchantId, deviceIdentifier); - var result = await Mediator.Send(command); + var result = await this.MerchantUiService.AddMerchantDevice(correlationId, estateId, MerchantId, deviceIdentifier); - if (result.IsSuccess) { - successMessage = "Device added successfully"; - assignedDevices.Add(new MerchantDeviceModel() { - DeviceIdentifier = this.deviceIdentifier - }); - deviceIdentifier = null; - showAddDevice = false; - } - else - { - errorMessage = result.Message ?? "Failed to add device"; - } + if (result.IsSuccess) { + successMessage = "Device added successfully"; + + await this.LoadMerchant(); + + deviceIdentifier = null; + showAddDevice = false; } - catch (Exception ex) - { - errorMessage = $"An error occurred: {ex.Message}"; + else { + errorMessage = "Failed to add device"; } } - // inside partial class Edit (add the following fields and methods) + // inside partial class Edit (add the following fields and methods) // Swap device UI state private string? selectedDeviceToSwap; - private string? swapDeviceIdentifier; - private string? swapDeviceError; - - private void StartSwapDevice(string device) - { - ClearMessages(); - selectedDeviceToSwap = device; - swapDeviceIdentifier = string.Empty; - swapDeviceError = null; - } + private string? swapDeviceIdentifier; + private string? swapDeviceError; - private void CancelSwapDevice() - { - swapDeviceIdentifier = null; - swapDeviceError = null; - selectedDeviceToSwap = null; - } + private void StartSwapDevice(string device) { + ClearMessages(); + selectedDeviceToSwap = device; + swapDeviceIdentifier = string.Empty; + swapDeviceError = null; + } - private async Task SwapDeviceConfirm(string originalDevice) - { - ClearMessages(); + private void CancelSwapDevice() { + swapDeviceIdentifier = null; + swapDeviceError = null; + selectedDeviceToSwap = null; + } - var newId = swapDeviceIdentifier?.Trim(); + private async Task SwapDeviceConfirm(string originalDevice) { + ClearMessages(); - // Validation - if (string.IsNullOrWhiteSpace(newId)) { - swapDeviceError = "New device identifier is required."; - return; - } + var newId = swapDeviceIdentifier?.Trim(); - // Case-insensitive comparison for equality/duplicates - if (string.Equals(originalDevice?.Trim(), newId, StringComparison.OrdinalIgnoreCase)) { - swapDeviceError = "New device identifier cannot be the same as the current device."; - return; - } + // Validation + if (string.IsNullOrWhiteSpace(newId)) { + swapDeviceError = "New device identifier is required."; + return; + } - if (assignedDevices.Any(d => string.Equals(d.DeviceIdentifier.Trim(), newId, StringComparison.OrdinalIgnoreCase))) { - swapDeviceError = "The specified device identifier is already assigned."; - return; - } + // Case-insensitive comparison for equality/duplicates + if (string.Equals(originalDevice?.Trim(), newId, StringComparison.OrdinalIgnoreCase)) { + swapDeviceError = "New device identifier cannot be the same as the current device."; + return; + } + + if (assignedDevices.Any(d => string.Equals(d.DeviceIdentifier.Trim(), newId, StringComparison.OrdinalIgnoreCase))) { + swapDeviceError = "The specified device identifier is already assigned."; + return; + } - var correlationId = new CorrelationId(Guid.NewGuid()); - var estateId = await this.GetEstateId(); - var command = new MerchantCommands.SwapMerchantDeviceCommand(correlationId, estateId, this.MerchantId, this.assignedDevices.Single().DeviceIdentifier, newId); + var correlationId = new CorrelationId(Guid.NewGuid()); + var estateId = await this.GetEstateId(); - var result = await Mediator.Send(command); + var result = await this.MerchantUiService.SwapMerchantDevice(correlationId, estateId, this.MerchantId, this.assignedDevices.Single().DeviceIdentifier, newId); - if (result.IsSuccess) { + if (result.IsSuccess) { successMessage = $"Device {originalDevice} swapped for {newId}."; } else { @@ -588,56 +417,9 @@ private async Task SwapDeviceConfirm(string originalDevice) // Reset swap UI CancelSwapDevice(); StateHasChanged(); - } - - - private void ClearMessages() - { - errorMessage = null; - successMessage = null; } - private void BackToList() - { + private void BackToList() { NavigationManager.NavigateTo("/merchants"); } - - public class MerchantEditModel - { - [Required(ErrorMessage = "Merchant name is required")] - public string? MerchantName { get; set; } - - public string? SettlementSchedule { get; set; } - - public Guid AddressId { get; set; } - - [Required(ErrorMessage = "Address line 1 is required")] - public string? AddressLine1 { get; set; } - - public string? AddressLine2 { get; set; } - - [Required(ErrorMessage = "Town is required")] - public string? Town { get; set; } - - [Required(ErrorMessage = "Region is required")] - public string? Region { get; set; } - - [Required(ErrorMessage = "PostCode is required")] - public string? PostalCode { get; set; } - - [Required(ErrorMessage = "Country is required")] - public string? Country { get; set; } - - public Guid ContactId { get; set; } - [Required(ErrorMessage = "Contact name is required")] - public string? ContactName { get; set; } - - [Required(ErrorMessage = "Email address is required")] - [EmailAddress(ErrorMessage = "Invalid email address")] - public string? ContactEmailAddress { get; set; } - - [Required(ErrorMessage = "Phone number is required")] - public string? ContactPhoneNumber { get; set; } - } } -} diff --git a/EstateManagementUI.BlazorServer/Components/Pages/Merchants/Index.razor b/EstateManagementUI.BlazorServer/Components/Pages/Merchants/Index.razor index 70c68b36..8339c1c9 100644 --- a/EstateManagementUI.BlazorServer/Components/Pages/Merchants/Index.razor +++ b/EstateManagementUI.BlazorServer/Components/Pages/Merchants/Index.razor @@ -2,8 +2,9 @@ @using EstateManagementUI.BlazorServer.Factories @rendermode InteractiveServer @using EstateManagementUI.BlazorServer.Permissions +@using EstateManagementUI.BlazorServer.UIServices @using EstateManagementUI.BusinessLogic.Requests -@inject IMediator Mediator +@inject IMerchantUIService MerchantUiService @inherits AuthorizedComponentBase @inject NavigationManager NavigationManager @inject IPermissionKeyProvider PermissionKeyProvider diff --git a/EstateManagementUI.BlazorServer/Components/Pages/Merchants/Index.razor.cs b/EstateManagementUI.BlazorServer/Components/Pages/Merchants/Index.razor.cs index 0e06a217..13fc988d 100644 --- a/EstateManagementUI.BlazorServer/Components/Pages/Merchants/Index.razor.cs +++ b/EstateManagementUI.BlazorServer/Components/Pages/Merchants/Index.razor.cs @@ -4,7 +4,9 @@ using EstateManagementUI.BlazorServer.Permissions; using EstateManagementUI.BusinessLogic.Models; using EstateManagementUI.BusinessLogic.Requests; -using MerchantListModel = EstateManagementUI.BlazorServer.Models.MerchantListModel; +using Shared.Results; +using SimpleResults; +using MerchantListModel = EstateManagementUI.BlazorServer.Models.MerchantModels.MerchantListModel; namespace EstateManagementUI.BlazorServer.Components.Pages.Merchants { @@ -78,92 +80,32 @@ protected override async Task OnAfterRenderAsync(bool firstRender) { if (!firstRender) { - await base.OnAfterRenderAsync(firstRender); return; } - try - { - await base.OnInitializedAsync(); - - await RequirePermission(PermissionSection.Merchant, PermissionFunction.List); - - await this.LoadMerchants(); - - } - finally + Result result = await OnAfterRender(PermissionSection.Merchant, PermissionFunction.List, this.LoadMerchants); + if (result.IsFailed) { - isLoading = false; - this.StateHasChanged(); + return; } + + isLoading = false; + this.StateHasChanged(); } - private async Task LoadMerchants() { + private async Task LoadMerchants() { CorrelationId correlationId = new(Guid.NewGuid()); Guid estateId = await this.GetEstateId(); - var result = await Mediator.Send(new MerchantQueries.GetMerchantsQuery(correlationId, estateId, this._filterName, this._filterReference, Int32.Parse(_filterSettlementSchedule), - this._filterRegion, this._filterPostcode)); - if (result.IsFailed) - { - this.NavigationManager.NavigateToErrorPage(); + var result = await this.MerchantUiService.GetMerchants(correlationId, estateId, this._filterName, this._filterReference, Int32.Parse(_filterSettlementSchedule), this._filterRegion, this._filterPostcode); + if (result.IsFailed) { + return ResultHelpers.CreateFailure(result); } - allMerchants = ModelFactory.ConvertFrom(result.Data); + allMerchants = result.Data; this.UpdatePagedMerchants(); - } - - //protected override async Task OnInitializedAsync() - //{ - // try - // { - // await RequirePermission(PermissionSection.Merchant, PermissionFunction.List); - - // var correlationId = new CorrelationId(Guid.NewGuid()); - // var estateId = await this.GetEstateId(); - - // var result = await Mediator.Send(new Queries.GetMerchantsQuery(correlationId, estateId, this._filterName, this._filterReference, Int32.Parse(_filterSettlementSchedule), - // this._filterRegion, this._filterPostcode)); - // if (result.IsSuccess) - // { - // allMerchants = ModelFactory.ConvertFrom(result.Data); - // //UpdateFilteredMerchants(); - // } - // } - // finally - // { - // isLoading = false; - // } - //} - - //private void UpdateFilteredMerchants() - //{ - // if (allMerchants == null) - // { - // _filteredMerchants = new List(); - // _totalPages = 1; - // UpdatePagedMerchants(); - // return; - // } - - // // Apply all filters in a single pass using a combined predicate - // _filteredMerchants = allMerchants.Where(m => - // (string.IsNullOrWhiteSpace(_filterName) || (m.MerchantName?.Contains(_filterName, StringComparison.OrdinalIgnoreCase) ?? false)) && - // (string.IsNullOrWhiteSpace(_filterReference) || (m.MerchantReference?.Contains(_filterReference, StringComparison.OrdinalIgnoreCase) ?? false)) && - // (string.IsNullOrWhiteSpace(_filterSettlementSchedule) || (m.SettlementSchedule?.Equals(_filterSettlementSchedule, StringComparison.OrdinalIgnoreCase) ?? false)) && - // (string.IsNullOrWhiteSpace(_filterRegion) || (m.Region?.Contains(_filterRegion, StringComparison.OrdinalIgnoreCase) ?? false)) && - // (string.IsNullOrWhiteSpace(_filterPostcode) || (m.PostalCode?.Contains(_filterPostcode, StringComparison.OrdinalIgnoreCase) ?? false)) - // ).ToList(); - - // _totalPages = _filteredMerchants.Count > 0 ? (int)Math.Ceiling(_filteredMerchants.Count / (double)_pageSize) : 1; - - // // Ensure currentPage is valid - // if (_currentPage > _totalPages && _totalPages > 0) - // { - // _currentPage = _totalPages; - // } - - // UpdatePagedMerchants(); - //} + return Result.Success(); + } + private void UpdatePagedMerchants() { _pagedMerchants = this.allMerchants.Skip((_currentPage - 1) * _pageSize).Take(_pageSize).ToList(); diff --git a/EstateManagementUI.BlazorServer/Components/Pages/Merchants/New.razor b/EstateManagementUI.BlazorServer/Components/Pages/Merchants/New.razor index a4b50ba6..bec6447c 100644 --- a/EstateManagementUI.BlazorServer/Components/Pages/Merchants/New.razor +++ b/EstateManagementUI.BlazorServer/Components/Pages/Merchants/New.razor @@ -2,8 +2,9 @@ @rendermode InteractiveServer @using System.ComponentModel.DataAnnotations @using EstateManagementUI.BlazorServer.Permissions +@using EstateManagementUI.BlazorServer.UIServices @using EstateManagementUI.BusinessLogic.Requests -@inject IMediator Mediator +@inject IMerchantUIService MerchantUiService @inherits AuthorizedComponentBase @inject NavigationManager NavigationManager @inject IPermissionService PermissionService diff --git a/EstateManagementUI.BlazorServer/Components/Pages/Merchants/New.razor.cs b/EstateManagementUI.BlazorServer/Components/Pages/Merchants/New.razor.cs index d3f0942c..115c169e 100644 --- a/EstateManagementUI.BlazorServer/Components/Pages/Merchants/New.razor.cs +++ b/EstateManagementUI.BlazorServer/Components/Pages/Merchants/New.razor.cs @@ -1,115 +1,63 @@ using EstateManagementUI.BlazorServer.Permissions; using EstateManagementUI.BusinessLogic.Requests; using System.ComponentModel.DataAnnotations; +using EstateManagementUI.BlazorServer.Models; +using SimpleResults; namespace EstateManagementUI.BlazorServer.Components.Pages.Merchants { public partial class New { - private readonly New.CreateMerchantModel model = new(); + private readonly MerchantModels.CreateMerchantModel model = new(); private bool isSaving = false; - private string? successMessage; - private string? errorMessage; protected override async Task OnAfterRenderAsync(bool firstRender) { - if (!firstRender) { - await base.OnAfterRenderAsync(firstRender); + if (!firstRender) + { + return; + } + Result result = await OnAfterRender(PermissionSection.Merchant, PermissionFunction.Create); + if (result.IsFailed) + { return; } - await RequirePermission(PermissionSection.Merchant, PermissionFunction.Create); + this.StateHasChanged(); } - private async Task HandleSubmit() - { + private async Task HandleSubmit() { isSaving = true; errorMessage = null; - try - { - var correlationId = new CorrelationId(Guid.NewGuid()); - var estateId = await this.GetEstateId(); - - var address = new MerchantCommands.MerchantAddress(Guid.Empty, model.AddressLine1, model.Town, model.Region, model.PostCode, model.Country); - var contact = new MerchantCommands.MerchantContact(Guid.Empty, model.ContactName, model.EmailAddress, model.PhoneNumber); - - // Get the newly created merchant ID - var merchantId = Guid.NewGuid(); + var correlationId = new CorrelationId(Guid.NewGuid()); + var estateId = await this.GetEstateId(); - // Create merchant - var createCommand = new MerchantCommands.CreateMerchantCommand( - correlationId, - estateId, merchantId, - this.model.MerchantName, - this.model.SettlementSchedule,address, contact); + // Get the newly created merchant ID + var merchantId = Guid.NewGuid(); - var createResult = await Mediator.Send(createCommand); + var result = await this.MerchantUiService.CreateMerchant(correlationId, estateId, merchantId, this.model); - if (!createResult.IsSuccess) - { - errorMessage = createResult.Message ?? "Failed to create merchant"; - return; - } + if (result.IsSuccess) { + successMessage = "Merchant created successfully"; - // Show success message briefly before navigating away - successMessage = "Merchant created successfully."; StateHasChanged(); // Small delay so user sees confirmation (adjust duration as needed) await this.WaitOnUIRefresh(); - // Navigate to merchant list - NavigationManager.NavigateTo($"/merchants"); - } - catch (Exception ex) - { - errorMessage = $"An error occurred: {ex.Message}"; + // Navigate to contracts list with success + NavigationManager.NavigateTo("/merchants"); } - finally - { - isSaving = false; + else { + this.errorMessage = "Failed to create merchant"; } + + isSaving = false; } private void Cancel() { NavigationManager.NavigateTo("/merchants"); } - - public class CreateMerchantModel - { - [Required(ErrorMessage = "Merchant name is required")] - public string? MerchantName { get; set; } - - [Required(ErrorMessage = "Settlement schedule is required")] - public string? SettlementSchedule { get; set; } - - [Required(ErrorMessage = "Address line 1 is required")] - public string? AddressLine1 { get; set; } - - public string? AddressLine2 { get; set; } - - [Required(ErrorMessage = "Town is required")] - public string? Town { get; set; } - - [Required(ErrorMessage = "Region is required")] - public string? Region { get; set; } - - [Required(ErrorMessage = "PostCode is required")] - public string? PostCode { get; set; } - - [Required(ErrorMessage = "Country is required")] - public string? Country { get; set; } - - [Required(ErrorMessage = "Contact name is required")] - public string? ContactName { get; set; } - - [Required(ErrorMessage = "Email address is required")] - [EmailAddress(ErrorMessage = "Invalid email address")] - public string? EmailAddress { get; set; } - - [Required(ErrorMessage = "Phone number is required")] - public string? PhoneNumber { get; set; } - } } } diff --git a/EstateManagementUI.BlazorServer/Components/Pages/Merchants/View.razor b/EstateManagementUI.BlazorServer/Components/Pages/Merchants/View.razor index 156465bd..7727602e 100644 --- a/EstateManagementUI.BlazorServer/Components/Pages/Merchants/View.razor +++ b/EstateManagementUI.BlazorServer/Components/Pages/Merchants/View.razor @@ -1,10 +1,11 @@ @page "/merchants/{MerchantId:guid}" @using EstateManagementUI.BlazorServer.Factories @rendermode InteractiveServer -@inject IMediator Mediator +@inject IMerchantUIService MerchantUIService @inherits AuthorizedComponentBase @inject NavigationManager NavigationManager @using EstateManagementUI.BlazorServer.Models +@using EstateManagementUI.BlazorServer.UIServices @using EstateManagementUI.BusinessLogic.Requests View Merchant diff --git a/EstateManagementUI.BlazorServer/Components/Pages/Merchants/View.razor.cs b/EstateManagementUI.BlazorServer/Components/Pages/Merchants/View.razor.cs index e35d0bc3..12a75bde 100644 --- a/EstateManagementUI.BlazorServer/Components/Pages/Merchants/View.razor.cs +++ b/EstateManagementUI.BlazorServer/Components/Pages/Merchants/View.razor.cs @@ -15,14 +15,14 @@ public partial class View [Parameter] public Guid MerchantId { get; set; } - private MerchantModel? merchant; + private MerchantModels.MerchantModel? merchant; private bool isLoading = true; private string activeTab = "details"; // Mock assigned data - private List assignedOperators = new(); - private List assignedContracts = new(); - private List assignedDevices = new(); + private List assignedOperators = new(); + private List assignedContracts = new(); + private List assignedDevices = new(); // Settlement transaction history data private List? settlementTransactions; @@ -37,25 +37,16 @@ protected override async Task OnAfterRenderAsync(bool firstRender) { if (!firstRender) { - await base.OnAfterRenderAsync(firstRender); return; } - try + Result result = await OnAfterRender(PermissionSection.Merchant, PermissionFunction.View, this.LoadMerchant); + if (result.IsFailed) { - await base.OnInitializedAsync(); - - await RequirePermission(PermissionSection.Merchant, PermissionFunction.View); - - Result result = await LoadMerchant(); - if (result.IsFailed) { - this.NavigationManager.NavigateToErrorPage(); - } - } - finally - { - isLoading = false; - this.StateHasChanged(); + return; } + + isLoading = false; + this.StateHasChanged(); } private async Task LoadMerchant() @@ -66,16 +57,16 @@ private async Task LoadMerchant() CorrelationId correlationId = new(Guid.NewGuid()); Guid estateId = await this.GetEstateId(); - Result getMerchantResult = await Mediator.Send(new MerchantQueries.GetMerchantQuery(correlationId, estateId, MerchantId)); + Result getMerchantResult = await this.MerchantUIService.GetMerchant(correlationId, estateId, MerchantId); if (getMerchantResult.IsFailed) return ResultHelpers.CreateFailure(getMerchantResult); - merchant = ModelFactory.ConvertFrom(getMerchantResult.Data); + merchant = getMerchantResult.Data; - var operatorsResultTask = Mediator.Send(new MerchantQueries.GetMerchantOperatorsQuery(correlationId, estateId, MerchantId)); - var contractsResultTask = Mediator.Send(new MerchantQueries.GetMerchantContractsQuery(correlationId, estateId, MerchantId)); - var devicesResultTask = Mediator.Send(new MerchantQueries.GetMerchantDevicesQuery(correlationId, estateId, MerchantId)); + var operatorsResultTask = this.MerchantUIService.GetMerchantOperators(correlationId, estateId, MerchantId); + var contractsResultTask = this.MerchantUIService.GetMerchantContracts(correlationId, estateId, MerchantId); + var devicesResultTask = this.MerchantUIService.GetMerchantDevices(correlationId, estateId, MerchantId); await Task.WhenAll(operatorsResultTask, contractsResultTask, devicesResultTask); @@ -88,9 +79,9 @@ private async Task LoadMerchant() if (devicesResultTask.Result.IsFailed) return ResultHelpers.CreateFailure(devicesResultTask.Result); - assignedOperators = ModelFactory.ConvertFrom(operatorsResultTask.Result.Data); - assignedContracts = ModelFactory.ConvertFrom(contractsResultTask.Result.Data); - this.assignedDevices = ModelFactory.ConvertFrom(devicesResultTask.Result.Data); + assignedOperators = operatorsResultTask.Result.Data; + assignedContracts = contractsResultTask.Result.Data; + this.assignedDevices = devicesResultTask.Result.Data; return Result.Success(); } @@ -102,44 +93,44 @@ private async Task LoadMerchant() private async Task LoadSettlementTransactions() { - loadingSettlementTransactions = true; - try - { - var estateId = Guid.Parse("11111111-1111-1111-1111-111111111111"); - var accessToken = "stubbed-token"; - var correlationId = CorrelationIdHelper.New(); - - // Load last 6 months of transaction data for this merchant - var query = new Queries.GetTransactionDetailQuery( - correlationId, - accessToken, - estateId, - DateTime.Today.AddMonths(-6), - DateTime.Today, - new List { MerchantId }, // Filter by this merchant only - null, // All operators - null // All products - ); - - var result = await Mediator.Send(query); - if (result.IsSuccess && result.Data != null) - { - settlementTransactions = ModelFactory.ConvertFrom(result.Data); - } - else - { - settlementTransactions = new List(); - } - } - catch (Exception) - { - // Handle error silently for now - settlementTransactions = new List(); - } - finally - { - loadingSettlementTransactions = false; - } + //loadingSettlementTransactions = true; + //try + //{ + // var estateId = Guid.Parse("11111111-1111-1111-1111-111111111111"); + // var accessToken = "stubbed-token"; + // var correlationId = CorrelationIdHelper.New(); + + // // Load last 6 months of transaction data for this merchant + // var query = new Queries.GetTransactionDetailQuery( + // correlationId, + // accessToken, + // estateId, + // DateTime.Today.AddMonths(-6), + // DateTime.Today, + // new List { MerchantId }, // Filter by this merchant only + // null, // All operators + // null // All products + // ); + + // var result = await Mediator.Send(query); + // if (result.IsSuccess && result.Data != null) + // { + // settlementTransactions = ModelFactory.ConvertFrom(result.Data); + // } + // else + // { + // settlementTransactions = new List(); + // } + //} + //catch (Exception) + //{ + // // Handle error silently for now + // settlementTransactions = new List(); + //} + //finally + //{ + // loadingSettlementTransactions = false; + //} } private string GetResultBadgeClass(string? status) diff --git a/EstateManagementUI.BlazorServer/Components/Pages/Reporting/MerchantSettlementHistory.razor b/EstateManagementUI.BlazorServer/Components/Pages/Reporting/MerchantSettlementHistory.razor index a75ae6bb..1ceb4015 100644 --- a/EstateManagementUI.BlazorServer/Components/Pages/Reporting/MerchantSettlementHistory.razor +++ b/EstateManagementUI.BlazorServer/Components/Pages/Reporting/MerchantSettlementHistory.razor @@ -2,7 +2,7 @@ @using EstateManagementUI.BlazorServer.Factories @using EstateManagementUI.BusinessLogic.Models @using EstateManagementUI.BusinessLogic.Requests -@using MerchantDropDownModel = EstateManagementUI.BlazorServer.Models.MerchantDropDownModel +@using MerchantDropDownModel = EstateManagementUI.BlazorServer.Models.MerchantModels.MerchantDropDownModel @using MerchantSettlementHistoryModel = EstateManagementUI.BlazorServer.Models.MerchantSettlementHistoryModel @rendermode InteractiveServer @inject IMediator Mediator diff --git a/EstateManagementUI.BlazorServer/Components/Pages/Reporting/SettlementSummary.razor b/EstateManagementUI.BlazorServer/Components/Pages/Reporting/SettlementSummary.razor index 61543c3c..c6eb7fa0 100644 --- a/EstateManagementUI.BlazorServer/Components/Pages/Reporting/SettlementSummary.razor +++ b/EstateManagementUI.BlazorServer/Components/Pages/Reporting/SettlementSummary.razor @@ -2,7 +2,7 @@ @using EstateManagementUI.BlazorServer.Factories @using EstateManagementUI.BusinessLogic.Models @using EstateManagementUI.BusinessLogic.Requests -@using MerchantDropDownModel = EstateManagementUI.BlazorServer.Models.MerchantDropDownModel +@using MerchantDropDownModel = EstateManagementUI.BlazorServer.Models.MerchantModels.MerchantDropDownModel @using SettlementSummaryModel = EstateManagementUI.BlazorServer.Models.SettlementSummaryModel @rendermode InteractiveServer @inject IMediator Mediator diff --git a/EstateManagementUI.BlazorServer/Components/Pages/Reporting/TransactionDetail.razor b/EstateManagementUI.BlazorServer/Components/Pages/Reporting/TransactionDetail.razor index 32417d91..1990d980 100644 --- a/EstateManagementUI.BlazorServer/Components/Pages/Reporting/TransactionDetail.razor +++ b/EstateManagementUI.BlazorServer/Components/Pages/Reporting/TransactionDetail.razor @@ -6,8 +6,8 @@ @using EstateManagementUI.BusinessLogic.Models @using EstateManagementUI.BusinessLogic.Requests @using ContractProductModel = EstateManagementUI.BlazorServer.Models.ContractModels.ContractProductModel -@using MerchantDropDownModel = EstateManagementUI.BlazorServer.Models.MerchantDropDownModel -@using MerchantModel = EstateManagementUI.BlazorServer.Models.MerchantModel +@using MerchantDropDownModel = EstateManagementUI.BlazorServer.Models.MerchantModels.MerchantDropDownModel +@using MerchantModel = EstateManagementUI.BlazorServer.Models.MerchantModels.MerchantModel @using OperatorDropDownModel = EstateManagementUI.BlazorServer.Models.OperatorModels.OperatorDropDownModel @using OperatorModel = EstateManagementUI.BlazorServer.Models.OperatorModels.OperatorModel @using TransactionDetailModel = EstateManagementUI.BlazorServer.Models.TransactionDetailModel diff --git a/EstateManagementUI.BlazorServer/Components/Pages/Reporting/TransactionSummaryMerchant.razor b/EstateManagementUI.BlazorServer/Components/Pages/Reporting/TransactionSummaryMerchant.razor index 464d877c..a2cbb641 100644 --- a/EstateManagementUI.BlazorServer/Components/Pages/Reporting/TransactionSummaryMerchant.razor +++ b/EstateManagementUI.BlazorServer/Components/Pages/Reporting/TransactionSummaryMerchant.razor @@ -2,7 +2,7 @@ @using EstateManagementUI.BlazorServer.Factories @using EstateManagementUI.BusinessLogic.Models @using EstateManagementUI.BusinessLogic.Requests -@using MerchantDropDownModel = EstateManagementUI.BlazorServer.Models.MerchantDropDownModel +@using MerchantDropDownModel = EstateManagementUI.BlazorServer.Models.MerchantModels.MerchantDropDownModel @using MerchantTransactionSummaryModel = EstateManagementUI.BlazorServer.Models.MerchantTransactionSummaryModel @using OperatorDropDownModel = EstateManagementUI.BlazorServer.Models.OperatorModels.OperatorDropDownModel @using OperatorModel = EstateManagementUI.BlazorServer.Models.OperatorModels.OperatorModel diff --git a/EstateManagementUI.BlazorServer/Components/Pages/Reporting/TransactionSummaryOperator.razor b/EstateManagementUI.BlazorServer/Components/Pages/Reporting/TransactionSummaryOperator.razor index 1d0aab0e..4ad10bb6 100644 --- a/EstateManagementUI.BlazorServer/Components/Pages/Reporting/TransactionSummaryOperator.razor +++ b/EstateManagementUI.BlazorServer/Components/Pages/Reporting/TransactionSummaryOperator.razor @@ -2,7 +2,7 @@ @using EstateManagementUI.BlazorServer.Factories @using EstateManagementUI.BusinessLogic.Models @using EstateManagementUI.BusinessLogic.Requests -@using MerchantDropDownModel = EstateManagementUI.BlazorServer.Models.MerchantDropDownModel +@using MerchantDropDownModel = EstateManagementUI.BlazorServer.Models.MerchantModels.MerchantDropDownModel @using OperatorDropDownModel = EstateManagementUI.BlazorServer.Models.OperatorModels.OperatorDropDownModel @using OperatorModel = EstateManagementUI.BlazorServer.Models.OperatorModels.OperatorModel @using OperatorTransactionSummaryModel = EstateManagementUI.BlazorServer.Models.OperatorTransactionSummaryModel diff --git a/EstateManagementUI.BlazorServer/Factories/ModelFactory.cs b/EstateManagementUI.BlazorServer/Factories/ModelFactory.cs index 606cc396..d38042dc 100644 --- a/EstateManagementUI.BlazorServer/Factories/ModelFactory.cs +++ b/EstateManagementUI.BlazorServer/Factories/ModelFactory.cs @@ -8,14 +8,14 @@ using ContractProductTransactionFeeModel = EstateManagementUI.BlazorServer.Models.ContractModels.ContractProductTransactionFeeModel; using FileDetailsModel = EstateManagementUI.BlazorServer.Models.FileDetailsModel; using FileImportLogModel = EstateManagementUI.BlazorServer.Models.FileImportLogModel; -using MerchantContractModel = EstateManagementUI.BlazorServer.Models.MerchantContractModel; -using MerchantContractProductModel = EstateManagementUI.BlazorServer.Models.MerchantContractProductModel; -using MerchantDeviceModel = EstateManagementUI.BlazorServer.Models.MerchantDeviceModel; -using MerchantDropDownModel = EstateManagementUI.BlazorServer.Models.MerchantDropDownModel; +using MerchantContractModel = EstateManagementUI.BlazorServer.Models.MerchantModels.MerchantContractModel; +using MerchantContractProductModel = EstateManagementUI.BlazorServer.Models.MerchantModels.MerchantContractProductModel; +using MerchantDeviceModel = EstateManagementUI.BlazorServer.Models.MerchantModels.MerchantDeviceModel; +using MerchantDropDownModel = EstateManagementUI.BlazorServer.Models.MerchantModels.MerchantDropDownModel; using MerchantKpiModel = EstateManagementUI.BlazorServer.Models.MerchantKpiModel; -using MerchantListModel = EstateManagementUI.BlazorServer.Models.MerchantListModel; -using MerchantModel = EstateManagementUI.BlazorServer.Models.MerchantModel; -using MerchantOperatorModel = EstateManagementUI.BlazorServer.Models.MerchantOperatorModel; +using MerchantListModel = EstateManagementUI.BlazorServer.Models.MerchantModels.MerchantListModel; +using MerchantModel = EstateManagementUI.BlazorServer.Models.MerchantModels.MerchantModel; +using MerchantOperatorModel = EstateManagementUI.BlazorServer.Models.MerchantModels.MerchantOperatorModel; using MerchantSettlementHistoryModel = EstateManagementUI.BlazorServer.Models.MerchantSettlementHistoryModel; using MerchantTransactionSummaryModel = EstateManagementUI.BlazorServer.Models.MerchantTransactionSummaryModel; using OperatorModel = EstateManagementUI.BlazorServer.Models.OperatorModels.OperatorModel; diff --git a/EstateManagementUI.BlazorServer/Models/MerchantModels.cs b/EstateManagementUI.BlazorServer/Models/MerchantModels.cs new file mode 100644 index 00000000..94c7316d --- /dev/null +++ b/EstateManagementUI.BlazorServer/Models/MerchantModels.cs @@ -0,0 +1,188 @@ +using System.ComponentModel.DataAnnotations; + +namespace EstateManagementUI.BlazorServer.Models; + +public class MerchantModels +{ + public class MerchantModel + { + public Guid MerchantId { get; set; } + public string? MerchantName { get; set; } + public string? MerchantReference { get; set; } + public decimal? Balance { get; set; } + public decimal? AvailableBalance { get; set; } + public string? SettlementSchedule { get; set; } + public Guid AddressId { get; set; } + public string? AddressLine1 { get; set; } + public string? AddressLine2 { get; set; } + public string? Town { get; set; } + public string? Region { get; set; } + public string? PostalCode { get; set; } + public Guid ContactId { get; set; } + public string? Country { get; set; } + public string? ContactName { get; set; } + public string? ContactEmailAddress { get; set; } + public string? ContactPhoneNumber { get; set; } + } + + public class MerchantOperatorModel + { + public Guid MerchantId { get; set; } + public Guid OperatorId { get; set; } + public String OperatorName { get; set; } + public String MerchantNumber { get; set; } + public String TerminalNumber { get; set; } + public Boolean IsDeleted { get; set; } + } + + public class MerchantContractModel + { + public Guid MerchantId { get; set; } + public Guid ContractId { get; set; } + public String OperatorName { get; set; } + public String ContractName { get; set; } + public Boolean IsDeleted { get; set; } + public List ContractProducts { get; set; } + } + + public class MerchantContractProductModel + { + public Guid MerchantId { get; set; } + public Guid ContractId { get; set; } + public Guid ProductId { get; set; } + public String ProductName { get; set; } + public String DisplayText { get; set; } + public String ProductType { get; set; } + public Decimal? Value { get; set; } + } + + public class MerchantDeviceModel + { + public Guid MerchantId { get; set; } + public Guid DeviceId { get; set; } + public String DeviceIdentifier { get; set; } + public Boolean IsDeleted { get; set; } + } + + public class MerchantDropDownModel + { + public Guid MerchantId { get; set; } + public string? MerchantName { get; set; } + } + + public class MerchantListModel + { + public Guid MerchantId { get; set; } + public string? MerchantName { get; set; } + public string? MerchantReference { get; set; } + public decimal? Balance { get; set; } + public decimal? AvailableBalance { get; set; } + public string? SettlementSchedule { get; set; } + public string? Region { get; set; } + public string? PostalCode { get; set; } + public DateTime CreatedDateTime { get; set; } + } + + public class CreateMerchantModel + { + [Required(ErrorMessage = "Merchant name is required")] + public string? MerchantName { get; set; } + + [Required(ErrorMessage = "Settlement schedule is required")] + public string? SettlementSchedule { get; set; } + + [Required(ErrorMessage = "Address line 1 is required")] + public string? AddressLine1 { get; set; } + + public string? AddressLine2 { get; set; } + + [Required(ErrorMessage = "Town is required")] + public string? Town { get; set; } + + [Required(ErrorMessage = "Region is required")] + public string? Region { get; set; } + + [Required(ErrorMessage = "PostCode is required")] + public string? PostCode { get; set; } + + [Required(ErrorMessage = "Country is required")] + public string? Country { get; set; } + + [Required(ErrorMessage = "Contact name is required")] + public string? ContactName { get; set; } + + [Required(ErrorMessage = "Email address is required")] + [EmailAddress(ErrorMessage = "Invalid email address")] + public string? EmailAddress { get; set; } + + [Required(ErrorMessage = "Phone number is required")] + public string? PhoneNumber { get; set; } + } + + public class MerchantEditModel + { + [Required(ErrorMessage = "Merchant name is required")] + public string? MerchantName { get; set; } + + public string? SettlementSchedule { get; set; } + + public Guid AddressId { get; set; } + + [Required(ErrorMessage = "Address line 1 is required")] + public string? AddressLine1 { get; set; } + + public string? AddressLine2 { get; set; } + + [Required(ErrorMessage = "Town is required")] + public string? Town { get; set; } + + [Required(ErrorMessage = "Region is required")] + public string? Region { get; set; } + + [Required(ErrorMessage = "PostCode is required")] + public string? PostalCode { get; set; } + + [Required(ErrorMessage = "Country is required")] + public string? Country { get; set; } + + public Guid ContactId { get; set; } + [Required(ErrorMessage = "Contact name is required")] + public string? ContactName { get; set; } + + [Required(ErrorMessage = "Email address is required")] + [EmailAddress(ErrorMessage = "Invalid email address")] + public string? ContactEmailAddress { get; set; } + + [Required(ErrorMessage = "Phone number is required")] + public string? ContactPhoneNumber { get; set; } + } + + public class DepositModel + { + [Required(ErrorMessage = "Deposit amount is required")] + [Range(1, int.MaxValue, ErrorMessage = "Deposit amount must be greater than 0")] + public int Amount { get; set; } + + [Required(ErrorMessage = "Date of deposit is required")] + [DateNotInFuture(ErrorMessage = "Date cannot be in the future")] + public DateTime Date { get; set; } = DateTime.Today; + + [Required(ErrorMessage = "Reference is required")] + public string? Reference { get; set; } + } + + private class DateNotInFutureAttribute : ValidationAttribute + { + protected override ValidationResult? IsValid(object? value, ValidationContext validationContext) + { + if (value is DateTime date) + { + if (date.Date > DateTime.Today) + { + return new ValidationResult(ErrorMessage ?? "Date cannot be in the future"); + } + } + return ValidationResult.Success; + } + } +} \ No newline at end of file diff --git a/EstateManagementUI.BlazorServer/Models/Models.cs b/EstateManagementUI.BlazorServer/Models/Models.cs index b272b6ec..d48adcd5 100644 --- a/EstateManagementUI.BlazorServer/Models/Models.cs +++ b/EstateManagementUI.BlazorServer/Models/Models.cs @@ -12,31 +12,6 @@ public record EstateModel(Guid EstateId, string? EstateName, string? Reference) } -public class MerchantModel -{ - public Guid MerchantId { get; set; } - public string? MerchantName { get; set; } - public string? MerchantReference { get; set; } - public decimal? Balance { get; set; } - public decimal? AvailableBalance { get; set; } - public string? SettlementSchedule { get; set; } - public Guid AddressId { get; set; } - public string? AddressLine1 { get; set; } - public string? AddressLine2 { get; set; } - public string? Town { get; set; } - public string? Region { get; set; } - public string? PostalCode { get; set; } - public Guid ContactId { get; set; } - public string? Country { get; set; } - public string? ContactName { get; set; } - public string? ContactEmailAddress { get; set; } - public string? ContactPhoneNumber { get; set; } -} - -// Operator Models - - - // File Processing Models public class FileImportLogModel { @@ -207,60 +182,3 @@ public class RecentContractModel public string? OperatorName { get; set; } } -public class MerchantOperatorModel -{ - public Guid MerchantId { get; set; } - public Guid OperatorId { get; set; } - public String OperatorName { get; set; } - public String MerchantNumber { get; set; } - public String TerminalNumber { get; set; } - public Boolean IsDeleted { get; set; } -} - -public class MerchantContractModel -{ - public Guid MerchantId { get; set; } - public Guid ContractId { get; set; } - public String OperatorName { get; set; } - public String ContractName { get; set; } - public Boolean IsDeleted { get; set; } - public List ContractProducts { get; set; } -} - -public class MerchantContractProductModel -{ - public Guid MerchantId { get; set; } - public Guid ContractId { get; set; } - public Guid ProductId { get; set; } - public String ProductName { get; set; } - public String DisplayText { get; set; } - public String ProductType { get; set; } - public Decimal? Value { get; set; } -} - -public class MerchantDeviceModel -{ - public Guid MerchantId { get; set; } - public Guid DeviceId { get; set; } - public String DeviceIdentifier { get; set; } - public Boolean IsDeleted { get; set; } -} - -public class MerchantDropDownModel -{ - public Guid MerchantId { get; set; } - public string? MerchantName { get; set; } -} - -public class MerchantListModel -{ - public Guid MerchantId { get; set; } - public string? MerchantName { get; set; } - public string? MerchantReference { get; set; } - public decimal? Balance { get; set; } - public decimal? AvailableBalance { get; set; } - public string? SettlementSchedule { get; set; } - public string? Region { get; set; } - public string? PostalCode { get; set; } - public DateTime CreatedDateTime { get; set; } -} \ No newline at end of file diff --git a/EstateManagementUI.BlazorServer/Program.cs b/EstateManagementUI.BlazorServer/Program.cs index 5e568a1b..0da0a81e 100644 --- a/EstateManagementUI.BlazorServer/Program.cs +++ b/EstateManagementUI.BlazorServer/Program.cs @@ -244,6 +244,7 @@ builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); + builder.Services.AddSingleton(); } builder.Host.UseWindowsService(); diff --git a/EstateManagementUI.BlazorServer/UIServices/ContractUIService.cs b/EstateManagementUI.BlazorServer/UIServices/ContractUIService.cs index 99b58d6f..7352431d 100644 --- a/EstateManagementUI.BlazorServer/UIServices/ContractUIService.cs +++ b/EstateManagementUI.BlazorServer/UIServices/ContractUIService.cs @@ -18,6 +18,9 @@ public interface IContractUIService { Guid estateId, Guid contractId); + Task>> GetContractsForDropDown(CorrelationId correlationId, + Guid estateId); + Task AddProductToContract(CorrelationId correlationId, Guid estateId, Guid contractId, @@ -116,4 +119,13 @@ public async Task CreateContract(CorrelationId correlationId, return ResultHelpers.CreateFailure(result); return Result.Success(); } + + public async Task>> GetContractsForDropDown(CorrelationId correlationId, + Guid estateId) { + var result = await this.Mediator.Send(new ContractQueries.GetContractsForDropDownQuery(correlationId, estateId)); + if (result.IsFailed) + return ResultHelpers.CreateFailure(result); + var contracts = ModelFactory.ConvertFrom(result.Data); + return Result.Success(contracts); + } } \ No newline at end of file diff --git a/EstateManagementUI.BlazorServer/UIServices/MerchantUIService.cs b/EstateManagementUI.BlazorServer/UIServices/MerchantUIService.cs new file mode 100644 index 00000000..5f26fe37 --- /dev/null +++ b/EstateManagementUI.BlazorServer/UIServices/MerchantUIService.cs @@ -0,0 +1,269 @@ +using EstateManagementUI.BlazorServer.Factories; +using EstateManagementUI.BlazorServer.Models; +using EstateManagementUI.BusinessLogic.BackendAPI.DataTransferObjects; +using EstateManagementUI.BusinessLogic.Requests; +using MediatR; +using Shared.Results; +using SimpleResults; +using Spectre.Console; +using TransactionProcessor.DataTransferObjects.Responses.Merchant; +using static EstateManagementUI.BlazorServer.Models.MerchantModels; + +namespace EstateManagementUI.BlazorServer.UIServices; + +public interface IMerchantUIService { + + Task> GetMerchant(CorrelationId correlationId, + Guid estateId, + Guid merchantId); + + Task>> GetMerchants(CorrelationId correlationId, + Guid estateId, + String name, + String reference, + Int32? settlementSchedule, + String region, + String postCode); + + Task>> GetMerchantOperators(CorrelationId correlationId, + Guid estateId, + Guid merchantId); + + Task>> GetMerchantContracts(CorrelationId correlationId, + Guid estateId, + Guid merchantId); + + Task>> GetMerchantDevices(CorrelationId correlationId, + Guid estateId, + Guid merchantId); + + Task CreateMerchant(CorrelationId correlationId, + Guid estateId, + Guid merchantId, + MerchantModels.CreateMerchantModel createMerchantModel); + + Task UpdateMerchant(CorrelationId correlationId, + Guid estateId, + Guid merchantId, + MerchantModels.MerchantEditModel editMerchantModel); + + Task AddOperatorToMerchant(CorrelationId correlationId, + Guid estateId, + Guid merchantId, + Guid operatorId, + String? merchantNumber, + String? terminalNumber); + + Task RemoveOperatorFromMerchant(CorrelationId correlationId, + Guid estateId, + Guid merchantId, + Guid operatorId); + + Task AssignContractToMerchant(CorrelationId correlationId, + Guid estateId, + Guid merchantId, + Guid contractId); + + Task RemoveContractFromMerchant(CorrelationId correlationId, + Guid estateId, + Guid merchantId, + Guid contractId); + + Task AddMerchantDevice(CorrelationId correlationId, + Guid estateId, + Guid merchantId, + String deviceIdentifier); + + Task SwapMerchantDevice(CorrelationId correlationId, + Guid estateId, + Guid merchantId, + String originalDeviceIdentifier, + String newDeviceIdentifier); + + Task MakeMerchantDeposit(CorrelationId correlationId, + Guid estateId, + Guid merchantId, + MerchantModels.DepositModel depositModel); + + //Contract UI Service + // GetContractsForDropDownQuery + + +} + +public class MerchantUIService : IMerchantUIService { + private readonly IMediator Mediator; + + public MerchantUIService(IMediator mediator) { + this.Mediator = mediator; + } + + public async Task> GetMerchant(CorrelationId correlationId, + Guid estateId, + Guid merchantId) { + MerchantQueries.GetMerchantQuery query = new(correlationId, estateId, merchantId); + var result = await this.Mediator.Send(query); + if (result.IsFailed) + return ResultHelpers.CreateFailure(result); + var merchant = ModelFactory.ConvertFrom(result.Data); + return Result.Success(merchant); + } + + public async Task>> GetMerchants(CorrelationId correlationId, + Guid estateId, + String name, + String reference, + Int32? settlementSchedule, + String region, + String postCode) { + MerchantQueries.GetMerchantsQuery query = new(correlationId, estateId, name, reference, settlementSchedule, region, postCode); + var result = await this.Mediator.Send(query); + if (result.IsFailed) + return ResultHelpers.CreateFailure(result); + var merchantList = ModelFactory.ConvertFrom(result.Data); + return Result.Success(merchantList); + } + + public async Task>> GetMerchantOperators(CorrelationId correlationId, + Guid estateId, + Guid merchantId) { + MerchantQueries.GetMerchantOperatorsQuery query = new(correlationId, estateId, merchantId); + var result = await this.Mediator.Send(query); + if (result.IsFailed) + return ResultHelpers.CreateFailure(result); + var merchantOperatorList = ModelFactory.ConvertFrom(result.Data); + return Result.Success(merchantOperatorList); + } + + public async Task>> GetMerchantContracts(CorrelationId correlationId, + Guid estateId, + Guid merchantId) { + MerchantQueries.GetMerchantContractsQuery query = new(correlationId, estateId, merchantId); + var result = await this.Mediator.Send(query); + if (result.IsFailed) + return ResultHelpers.CreateFailure(result); + var merchantContractList = ModelFactory.ConvertFrom(result.Data); + return Result.Success(merchantContractList); + } + + public async Task>> GetMerchantDevices(CorrelationId correlationId, + Guid estateId, + Guid merchantId) { + MerchantQueries.GetMerchantDevicesQuery query = new(correlationId, estateId, merchantId); + var result = await this.Mediator.Send(query); + if (result.IsFailed) + return ResultHelpers.CreateFailure(result); + var merchantDeviceList = ModelFactory.ConvertFrom(result.Data); + return Result.Success(merchantDeviceList); + } + + public async Task CreateMerchant(CorrelationId correlationId, + Guid estateId, + Guid merchantId, + MerchantModels.CreateMerchantModel createMerchantModel) { + + MerchantCommands.MerchantAddress address = new(Guid.NewGuid(), createMerchantModel.AddressLine1, createMerchantModel.Town, createMerchantModel.Region, createMerchantModel.PostCode, createMerchantModel.Country); + MerchantCommands.MerchantContact contact = new(Guid.NewGuid(), createMerchantModel.ContactName, createMerchantModel.EmailAddress, createMerchantModel.PhoneNumber); + MerchantCommands.CreateMerchantCommand command = new(correlationId, estateId, merchantId, createMerchantModel.MerchantName, createMerchantModel.SettlementSchedule, + address, contact); + var result = await this.Mediator.Send(command); + if (result.IsFailed) + return ResultHelpers.CreateFailure(result); + return Result.Success(); + } + + public async Task UpdateMerchant(CorrelationId correlationId, + Guid estateId, + Guid merchantId, + MerchantModels.MerchantEditModel editMerchantModel) { + MerchantCommands.MerchantAddress address = new(Guid.NewGuid(), editMerchantModel.AddressLine1, editMerchantModel.Town, editMerchantModel.Region, editMerchantModel.PostalCode, editMerchantModel.Country); + MerchantCommands.MerchantContact contact = new(Guid.NewGuid(), editMerchantModel.ContactName, editMerchantModel.ContactEmailAddress, editMerchantModel.ContactPhoneNumber); + + MerchantCommands.UpdateMerchantCommand command = new(correlationId, estateId, merchantId, editMerchantModel.MerchantName, editMerchantModel.SettlementSchedule, + address,contact); + var result = await this.Mediator.Send(command); + if (result.IsFailed) + return ResultHelpers.CreateFailure(result); + return Result.Success(); + } + + public async Task AddOperatorToMerchant(CorrelationId correlationId, + Guid estateId, + Guid merchantId, + Guid operatorId, + String? merchantNumber, + String? terminalNumber) { + MerchantCommands.AddOperatorToMerchantCommand command = new(correlationId, estateId, merchantId, operatorId, merchantNumber, terminalNumber); + var result = await this.Mediator.Send(command); + if (result.IsFailed) + return ResultHelpers.CreateFailure(result); + return Result.Success(); + } + + public async Task RemoveOperatorFromMerchant(CorrelationId correlationId, + Guid estateId, + Guid merchantId, + Guid operatorId) { + MerchantCommands.RemoveOperatorFromMerchantCommand command = new(correlationId, estateId, merchantId, operatorId); + var result = await this.Mediator.Send(command); + if (result.IsFailed) + return ResultHelpers.CreateFailure(result); + return Result.Success(); + } + + public async Task AssignContractToMerchant(CorrelationId correlationId, + Guid estateId, + Guid merchantId, + Guid contractId) { + MerchantCommands.AssignContractToMerchantCommand command = new(correlationId, estateId, merchantId, contractId); + var result = await this.Mediator.Send(command); + if (result.IsFailed) + return ResultHelpers.CreateFailure(result); + return Result.Success(); + } + + public async Task RemoveContractFromMerchant(CorrelationId correlationId, + Guid estateId, + Guid merchantId, + Guid contractId) { + MerchantCommands.RemoveContractFromMerchantCommand command = new(correlationId, estateId, merchantId, contractId); + var result = await this.Mediator.Send(command); + if (result.IsFailed) + return ResultHelpers.CreateFailure(result); + return Result.Success(); + } + + public async Task AddMerchantDevice(CorrelationId correlationId, + Guid estateId, + Guid merchantId, + String deviceIdentifier) { + MerchantCommands.AddMerchantDeviceCommand command = new(correlationId, estateId, merchantId, deviceIdentifier); + var result = await this.Mediator.Send(command); + if (result.IsFailed) + return ResultHelpers.CreateFailure(result); + return Result.Success(); + } + + public async Task SwapMerchantDevice(CorrelationId correlationId, + Guid estateId, + Guid merchantId, + String originalDeviceIdentifier, + String newDeviceIdentifier) { + MerchantCommands.SwapMerchantDeviceCommand command = new(correlationId, estateId, merchantId, originalDeviceIdentifier, newDeviceIdentifier); + var result = await this.Mediator.Send(command); + if (result.IsFailed) + return ResultHelpers.CreateFailure(result); + return Result.Success(); + } + + public async Task MakeMerchantDeposit(CorrelationId correlationId, + Guid estateId, + Guid merchantId, + MerchantModels.DepositModel depositModel) { + MerchantCommands.MakeMerchantDepositCommand command = new(correlationId, estateId, merchantId, depositModel.Amount, depositModel.Date, depositModel.Reference); + var result = await this.Mediator.Send(command); + if (result.IsFailed) + return ResultHelpers.CreateFailure(result); + return Result.Success(); + } +} \ No newline at end of file