From 7a25ffbcbf82adbcdcc8a18e70a21c0cd425411c Mon Sep 17 00:00:00 2001 From: Stuart Ferguson Date: Mon, 21 Feb 2022 20:07:43 +0000 Subject: [PATCH] Added initial voucher flow --- .../MerchantRequestHandlerTests.cs | 3 +- .../TransactionRequestHandlerTests.cs | 24 +++ .../RequestTests/RequestTests.cs | 32 +++- .../TestData.cs | 25 ++- ...=> MobileTopupFailedPageViewModelTests.cs} | 4 +- ...bileTopupPerformTopupPageViewModelTests.cs | 14 +- ...leTopupSelectOperatorPageViewModelTests.cs | 4 +- ...> MobileTopupSuccessPageViewModelTests.cs} | 2 +- .../TransactionsPageViewModelTests.cs | 2 +- .../VoucherIssueFailedPageViewModelTests.cs | 19 ++ ...herIssueSelectProductPageViewModelTests.cs | 80 +++++++++ .../VoucherIssueSuccessPageViewModelTests.cs | 19 ++ .../VoucherPerformIssuePageViewModelTests.cs | 166 ++++++++++++++++++ ...VoucherSelectOperatorPageViewModelTests.cs | 52 ++++++ .../Models/PerformVoucherIssueRequestModel.cs | 31 ++++ .../RequestHandlers/MerchantRequestHandler.cs | 9 +- .../TransactionRequestHandler.cs | 28 ++- .../Requests/GetContractProductsRequest.cs | 11 +- .../Requests/PerformMobileTopupRequest.cs | 48 ++--- .../Requests/PerformVoucherIssueRequest.cs | 90 ++++++++++ .../Services/DummyMerchantService.cs | 38 +++- .../Services/ITransactionService.cs | 9 +- .../UIServices/INavigationService.cs | 14 ++ .../ViewModels/LoginPageViewModel.cs | 2 +- .../MobileTopupFailedPageViewModel.cs | 1 + .../MobileTopupPerformTopupPageViewModel.cs | 8 +- .../MobileTopupSelectOperatorPageViewModel.cs | 4 +- .../MobileTopupSelectProductPageViewModel.cs | 2 +- .../Transactions/TransactionsPageViewModel.cs | 2 +- .../VoucherIssueFailedPageViewModel.cs | 37 ++++ .../VoucherIssueSuccessPageViewModel.cs | 38 ++++ .../VoucherPerformIssuePageViewModel.cs | 157 +++++++++++++++++ .../VoucherSelectOperatorPageViewModel.cs | 71 ++++++++ .../VoucherSelectProductPageViewModel.cs | 68 +++++++ TransactionMobile.Maui/App.xaml.cs | 5 + .../Extensions/MauiAppBuilderExtensions.cs | 9 + .../Voucher/VoucherIssueFailedPage.xaml | 25 +++ .../Voucher/VoucherIssueFailedPage.xaml.cs | 14 ++ .../Voucher/VoucherIssueSuccessPage.xaml | 29 +++ .../Voucher/VoucherIssueSuccessPage.xaml.cs | 14 ++ .../Voucher/VoucherPerformIssuePage.xaml | 61 +++++++ .../Voucher/VoucherPerformIssuePage.xaml.cs | 56 ++++++ .../Voucher/VoucherSelectOperatorPage.xaml | 11 ++ .../Voucher/VoucherSelectOperatorPage.xaml.cs | 69 ++++++++ .../Voucher/VoucherSelectProductPage.xaml | 11 ++ .../Voucher/VoucherSelectProductPage.xaml.cs | 69 ++++++++ .../TransactionMobile.Maui.csproj | 33 ++++ .../UIServices/ShellNavigationService.cs | 31 ++++ 48 files changed, 1494 insertions(+), 57 deletions(-) rename TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/{MobileTopupFailedViewModelTests.cs => MobileTopupFailedPageViewModelTests.cs} (81%) rename TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/{MobileTopupSuccessViewModelTests.cs => MobileTopupSuccessPageViewModelTests.cs} (91%) create mode 100644 TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/VoucherIssueFailedPageViewModelTests.cs create mode 100644 TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/VoucherIssueSelectProductPageViewModelTests.cs create mode 100644 TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/VoucherIssueSuccessPageViewModelTests.cs create mode 100644 TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/VoucherPerformIssuePageViewModelTests.cs create mode 100644 TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/VoucherSelectOperatorPageViewModelTests.cs create mode 100644 TransactionMobile.Maui.BusinessLogic/Models/PerformVoucherIssueRequestModel.cs create mode 100644 TransactionMobile.Maui.BusinessLogic/Requests/PerformVoucherIssueRequest.cs create mode 100644 TransactionMobile.Maui.BusinessLogic/ViewModels/Transactions/VoucherIssueFailedPageViewModel.cs create mode 100644 TransactionMobile.Maui.BusinessLogic/ViewModels/Transactions/VoucherIssueSuccessPageViewModel.cs create mode 100644 TransactionMobile.Maui.BusinessLogic/ViewModels/Transactions/VoucherPerformIssuePageViewModel.cs create mode 100644 TransactionMobile.Maui.BusinessLogic/ViewModels/Transactions/VoucherSelectOperatorPageViewModel.cs create mode 100644 TransactionMobile.Maui.BusinessLogic/ViewModels/Transactions/VoucherSelectProductPageViewModel.cs create mode 100644 TransactionMobile.Maui/Pages/Transactions/Voucher/VoucherIssueFailedPage.xaml create mode 100644 TransactionMobile.Maui/Pages/Transactions/Voucher/VoucherIssueFailedPage.xaml.cs create mode 100644 TransactionMobile.Maui/Pages/Transactions/Voucher/VoucherIssueSuccessPage.xaml create mode 100644 TransactionMobile.Maui/Pages/Transactions/Voucher/VoucherIssueSuccessPage.xaml.cs create mode 100644 TransactionMobile.Maui/Pages/Transactions/Voucher/VoucherPerformIssuePage.xaml create mode 100644 TransactionMobile.Maui/Pages/Transactions/Voucher/VoucherPerformIssuePage.xaml.cs create mode 100644 TransactionMobile.Maui/Pages/Transactions/Voucher/VoucherSelectOperatorPage.xaml create mode 100644 TransactionMobile.Maui/Pages/Transactions/Voucher/VoucherSelectOperatorPage.xaml.cs create mode 100644 TransactionMobile.Maui/Pages/Transactions/Voucher/VoucherSelectProductPage.xaml create mode 100644 TransactionMobile.Maui/Pages/Transactions/Voucher/VoucherSelectProductPage.xaml.cs diff --git a/TransactionMobile.Maui.BusinessLogic.Tests/RequestHandlerTests/MerchantRequestHandlerTests.cs b/TransactionMobile.Maui.BusinessLogic.Tests/RequestHandlerTests/MerchantRequestHandlerTests.cs index c1d139ee..63cd7192 100644 --- a/TransactionMobile.Maui.BusinessLogic.Tests/RequestHandlerTests/MerchantRequestHandlerTests.cs +++ b/TransactionMobile.Maui.BusinessLogic.Tests/RequestHandlerTests/MerchantRequestHandlerTests.cs @@ -24,7 +24,8 @@ public async Task MerchantRequestHandler_GetContractProductsRequest_Handle_IsHan GetContractProductsRequest request = GetContractProductsRequest.Create(TestData.AccessToken, TestData.EstateId, - TestData.MerchantId); + TestData.MerchantId, + null); List contractProductModels = await handler.Handle(request, CancellationToken.None); diff --git a/TransactionMobile.Maui.BusinessLogic.Tests/RequestHandlerTests/TransactionRequestHandlerTests.cs b/TransactionMobile.Maui.BusinessLogic.Tests/RequestHandlerTests/TransactionRequestHandlerTests.cs index 3840aedc..3767b2c2 100644 --- a/TransactionMobile.Maui.BusinessLogic.Tests/RequestHandlerTests/TransactionRequestHandlerTests.cs +++ b/TransactionMobile.Maui.BusinessLogic.Tests/RequestHandlerTests/TransactionRequestHandlerTests.cs @@ -52,4 +52,28 @@ public async Task TransactionRequestHandler_PerformMobileTopupRequest_Handle_IsH response.ShouldBeTrue(); } + + [Fact] + public async Task TransactionRequestHandler_PerformVoucherIssueRequest_Handle_IsHandled() + { + Mock transactionService = new Mock(); + transactionService.Setup(t => t.PerformVoucherIssue(It.IsAny(), It.IsAny())).ReturnsAsync(true); + TransactionRequestHandler handler = new TransactionRequestHandler(transactionService.Object); + + PerformVoucherIssueRequest request = PerformVoucherIssueRequest.Create(TestData.TransactionDateTime, + TestData.TransactionNumber, + TestData.DeviceIdentifier, + TestData.ApplicationVersion, + TestData.OperatorId3ContractId, + TestData.Operator3Product_200KES.ProductId, + TestData.OperatorIdentifier3, + TestData.RecipientMobileNumber, + TestData.RecipientEmailAddress, + TestData.Operator3Product_200KES.Value, + TestData.CustomerEmailAddress); + + Boolean response = await handler.Handle(request, CancellationToken.None); + + response.ShouldBeTrue(); + } } \ No newline at end of file diff --git a/TransactionMobile.Maui.BusinessLogic.Tests/RequestTests/RequestTests.cs b/TransactionMobile.Maui.BusinessLogic.Tests/RequestTests/RequestTests.cs index 4ba4c713..bc0443ad 100644 --- a/TransactionMobile.Maui.BusinessLogic.Tests/RequestTests/RequestTests.cs +++ b/TransactionMobile.Maui.BusinessLogic.Tests/RequestTests/RequestTests.cs @@ -17,7 +17,8 @@ public void GetContractProductsRequest_Create_IsCreated() { GetContractProductsRequest request = GetContractProductsRequest.Create(TestData.AccessToken, TestData.EstateId, - TestData.MerchantId); + TestData.MerchantId, + null); request.ShouldNotBeNull(); request.AccessToken.ShouldBe(TestData.AccessToken); @@ -90,4 +91,33 @@ public void PerformMobileTopupRequest_Create_IsCreated() request.TopupAmount.ShouldBe(TestData.Operator1Product_100KES.Value); request.CustomerEmailAddress.ShouldBe(TestData.CustomerEmailAddress); } + + [Fact] + public void PerformVoucherIssueRequest_Create_IsCreated() + { + PerformVoucherIssueRequest request = PerformVoucherIssueRequest.Create(TestData.TransactionDateTime, + TestData.TransactionNumber, + TestData.DeviceIdentifier, + TestData.ApplicationVersion, + TestData.OperatorId3ContractId, + TestData.Operator3Product_200KES.ProductId, + TestData.OperatorIdentifier3, + TestData.RecipientMobileNumber, + TestData.RecipientEmailAddress, + TestData.Operator3Product_200KES.Value, + TestData.CustomerEmailAddress); + + request.ShouldNotBeNull(); + request.TransactionDateTime.ShouldBe(TestData.TransactionDateTime); + request.TransactionNumber.ShouldBe(TestData.TransactionNumber); + request.DeviceIdentifier.ShouldBe(TestData.DeviceIdentifier); + request.ApplicationVersion.ShouldBe(TestData.ApplicationVersion); + request.ContractId.ShouldBe(TestData.OperatorId3ContractId); + request.ProductId.ShouldBe(TestData.Operator3Product_200KES.ProductId); + request.OperatorIdentifier.ShouldBe(TestData.OperatorIdentifier3); + request.RecipientMobileNumber.ShouldBe(TestData.RecipientMobileNumber); + request.RecipientEmailAddress.ShouldBe(TestData.RecipientEmailAddress); + request.VoucherAmount.ShouldBe(TestData.Operator3Product_200KES.Value); + request.CustomerEmailAddress.ShouldBe(TestData.CustomerEmailAddress); + } } \ No newline at end of file diff --git a/TransactionMobile.Maui.BusinessLogic.Tests/TestData.cs b/TransactionMobile.Maui.BusinessLogic.Tests/TestData.cs index 51f0f862..c647bd55 100644 --- a/TransactionMobile.Maui.BusinessLogic.Tests/TestData.cs +++ b/TransactionMobile.Maui.BusinessLogic.Tests/TestData.cs @@ -16,14 +16,19 @@ public static class TestData public static String OperatorIdentifier1 = "Safaricom"; public static String OperatorIdentifier2 = "Safaricom1"; + public static String OperatorIdentifier3 = "Voucher"; + public static String OperatorName1 = "Safaricom"; public static String OperatorName2 = "Safaricom1"; + public static String OperatorName3 = "Voucher"; public static Guid OperatorId1 = Guid.Parse("2CA5F4C7-34EC-425B-BB53-7EEDF48D9967"); public static Guid OperatorId2 = Guid.Parse("1CA5F4C7-34EC-425B-BB53-7EEDF48D9968"); + public static Guid OperatorId3 = Guid.Parse("17FA0431-4DCB-43CF-97B7-2CEDC74E4796"); public static Guid OperatorId1ContractId = Guid.Parse("D57DAC9B-4039-4120-B5A8-F7FDF1D3A3C2"); public static Guid OperatorId2ContractId = Guid.Parse("2471F981-DDA8-4B87-91F4-DD5DCB984FC1"); + public static Guid OperatorId3ContractId = Guid.Parse("(0BCACE74-CBF1-4F2C-AC1C-733BF1F53133)"); public static ContractProductModel Operator1Product_100KES = new ContractProductModel { @@ -75,12 +80,26 @@ public static class TestData ProductType = ProductType.MobileTopup }; + public static ContractProductModel Operator3Product_200KES = new ContractProductModel + { + ContractId = OperatorId3ContractId, + IsFixedValue = false, + OperatorId = TestData.OperatorId3, + OperatorIdentfier = OperatorIdentifier3, + OperatorName = OperatorName3, + ProductDisplayText = "100 KES", + ProductId = Guid.Parse("3270DC5F-1BB3-4ECF-BA06-AFB42B651483"), + ProductType = ProductType.Voucher, + Value = 200 + }; + public static List ContractProductList = new List { Operator1Product_100KES, Operator1Product_200KES, Operator1Product_Custom, - Operator2Product_Custom + Operator2Product_Custom, + TestData.Operator3Product_200KES }; public static ContractOperatorModel ContractOperatorModel = new ContractOperatorModel @@ -104,6 +123,10 @@ public static class TestData public static String CustomerAccountNumber = "0123456"; public static String CustomerEmailAddress = "1@2.com"; + public static String RecipientMobileNumber = "0123456"; + + public static String RecipientEmailAddress = "1@2.com"; + public static Decimal MerchantBalance = 199.99m; } } diff --git a/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/MobileTopupFailedViewModelTests.cs b/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/MobileTopupFailedPageViewModelTests.cs similarity index 81% rename from TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/MobileTopupFailedViewModelTests.cs rename to TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/MobileTopupFailedPageViewModelTests.cs index f0250ff3..783f5a72 100644 --- a/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/MobileTopupFailedViewModelTests.cs +++ b/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/MobileTopupFailedPageViewModelTests.cs @@ -5,10 +5,10 @@ namespace TransactionMobile.Maui.BusinessLogic.Tests.ViewModelTests; using ViewModels.Transactions; using Xunit; -public class MobileTopupFailedViewModelTests +public class MobileTopupFailedPageViewModelTests { [Fact] - public void MobileTopupFailedPageViewModel_CompletedCommand_Execute_IsExecuted() + public void MobileTopupFailedPageViewModel_CancelledCommand_Execute_IsExecuted() { Mock navigationService = new Mock(); MobileTopupFailedPageViewModel viewModel = new MobileTopupFailedPageViewModel(navigationService.Object); diff --git a/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/MobileTopupPerformTopupPageViewModelTests.cs b/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/MobileTopupPerformTopupPageViewModelTests.cs index 42e23ff4..0fca00d9 100644 --- a/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/MobileTopupPerformTopupPageViewModelTests.cs +++ b/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/MobileTopupPerformTopupPageViewModelTests.cs @@ -24,13 +24,13 @@ public void MobileTopupPerformTopupPageViewModel_ApplyQueryAttributes_QueryAttri { {nameof(viewModel.ContractId), TestData.OperatorId1ContractId}, {nameof(viewModel.ProductId), TestData.Operator1Product_100KES.ProductId}, - {nameof(viewModel.OperatorIdentifer), TestData.OperatorIdentifier1}, + {nameof(viewModel.OperatorIdentifier), TestData.OperatorIdentifier1}, {nameof(viewModel.TopupAmount), TestData.Operator1Product_100KES.Value} }); viewModel.ContractId.ShouldBe(TestData.OperatorId1ContractId.ToString()); viewModel.ProductId.ShouldBe(TestData.Operator1Product_100KES.ProductId.ToString()); - viewModel.OperatorIdentifer.ShouldBe(TestData.OperatorIdentifier1); + viewModel.OperatorIdentifier.ShouldBe(TestData.OperatorIdentifier1); viewModel.TopupAmount.ShouldBe(TestData.Operator1Product_100KES.Value); } @@ -50,7 +50,7 @@ public void MobileTopupPerformTopupPageViewModel_CustomerEmailAddressEntryComple { {nameof(viewModel.ContractId), TestData.OperatorId1ContractId}, {nameof(viewModel.ProductId), TestData.Operator1Product_100KES.ProductId}, - {nameof(viewModel.OperatorIdentifer), TestData.OperatorIdentifier1}, + {nameof(viewModel.OperatorIdentifier), TestData.OperatorIdentifier1}, {nameof(viewModel.TopupAmount), TestData.Operator1Product_100KES.Value} }); viewModel.CustomerEmailAddressEntryCompletedCommand.Execute(null); @@ -73,7 +73,7 @@ public void MobileTopupPerformTopupPageViewModel_CustomerMobileNumberEntryComple { {nameof(viewModel.ContractId), TestData.OperatorId1ContractId}, {nameof(viewModel.ProductId), TestData.Operator1Product_100KES.ProductId}, - {nameof(viewModel.OperatorIdentifer), TestData.OperatorIdentifier1}, + {nameof(viewModel.OperatorIdentifier), TestData.OperatorIdentifier1}, {nameof(viewModel.TopupAmount), TestData.Operator1Product_100KES.Value} }); viewModel.CustomerMobileNumberEntryCompletedCommand.Execute(null); @@ -96,7 +96,7 @@ public void MobileTopupPerformTopupPageViewModel_TopupAmountEntryCompletedComman { {nameof(viewModel.ContractId), TestData.OperatorId1ContractId}, {nameof(viewModel.ProductId), TestData.Operator1Product_100KES.ProductId}, - {nameof(viewModel.OperatorIdentifer), TestData.OperatorIdentifier1}, + {nameof(viewModel.OperatorIdentifier), TestData.OperatorIdentifier1}, {nameof(viewModel.TopupAmount), TestData.Operator1Product_100KES.Value} }); viewModel.TopupAmountEntryCompletedCommand.Execute(null); @@ -114,7 +114,7 @@ public void MobileTopupPerformTopupPageViewModel_PerformTopupCommand_Execute_Suc { {nameof(viewModel.ContractId), TestData.OperatorId1ContractId}, {nameof(viewModel.ProductId), TestData.Operator1Product_100KES.ProductId}, - {nameof(viewModel.OperatorIdentifer), TestData.OperatorIdentifier1}, + {nameof(viewModel.OperatorIdentifier), TestData.OperatorIdentifier1}, {nameof(viewModel.TopupAmount), TestData.Operator1Product_100KES.Value} }); viewModel.PerformTopupCommand.Execute(null); @@ -133,7 +133,7 @@ public void MobileTopupPerformTopupPageViewModel_PerformTopupCommand_Execute_Fai { {nameof(viewModel.ContractId), TestData.OperatorId1ContractId}, {nameof(viewModel.ProductId), TestData.Operator1Product_100KES.ProductId}, - {nameof(viewModel.OperatorIdentifer), TestData.OperatorIdentifier1}, + {nameof(viewModel.OperatorIdentifier), TestData.OperatorIdentifier1}, {nameof(viewModel.TopupAmount), TestData.Operator1Product_100KES.Value} }); viewModel.PerformTopupCommand.Execute(null); diff --git a/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/MobileTopupSelectOperatorPageViewModelTests.cs b/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/MobileTopupSelectOperatorPageViewModelTests.cs index fd9ea446..cdf7bfcc 100644 --- a/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/MobileTopupSelectOperatorPageViewModelTests.cs +++ b/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/MobileTopupSelectOperatorPageViewModelTests.cs @@ -24,7 +24,7 @@ public async Task MobileTopupSelectOperatorPageViewModel_Initialise_IsInitialise await viewModel.Initialise(CancellationToken.None); mediator.Verify(x => x.Send(It.IsAny(), It.IsAny()), Times.Once); - viewModel.Operators.Count.ShouldBe(2); + viewModel.Operators.Count.ShouldBe(3); } [Fact] @@ -37,7 +37,7 @@ public async Task MobileTopupSelectOperatorPageViewModel_OperatorSelectedCommand await viewModel.Initialise(CancellationToken.None); - viewModel.Operators.Count.ShouldBe(2); + viewModel.Operators.Count.ShouldBe(3); ItemSelected selectedContractOperator = new ItemSelected { diff --git a/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/MobileTopupSuccessViewModelTests.cs b/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/MobileTopupSuccessPageViewModelTests.cs similarity index 91% rename from TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/MobileTopupSuccessViewModelTests.cs rename to TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/MobileTopupSuccessPageViewModelTests.cs index 7a10deb6..dc9d21e4 100644 --- a/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/MobileTopupSuccessViewModelTests.cs +++ b/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/MobileTopupSuccessPageViewModelTests.cs @@ -5,7 +5,7 @@ namespace TransactionMobile.Maui.BusinessLogic.Tests.ViewModelTests; using ViewModels.Transactions; using Xunit; -public class MobileTopupSuccessViewModelTests +public class MobileTopupSuccessPageViewModelTests { [Fact] public void MobileTopupSuccessPageViewModel_CompletedCommand_Execute_IsExecuted() diff --git a/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/TransactionsPageViewModelTests.cs b/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/TransactionsPageViewModelTests.cs index 2e7350d6..e9b98529 100644 --- a/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/TransactionsPageViewModelTests.cs +++ b/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/TransactionsPageViewModelTests.cs @@ -54,6 +54,6 @@ public void TransactionsPageViewModel_VoucherCommand_Execute_IsExecuted() TransactionsPageViewModel viewModel = new TransactionsPageViewModel(navigationService.Object); viewModel.VoucherCommand.Execute(null); - navigationService.Verify(n => n.GoToHome(), Times.Once); + navigationService.Verify(n => n.GoToVoucherSelectOperatorPage(), Times.Once); } } \ No newline at end of file diff --git a/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/VoucherIssueFailedPageViewModelTests.cs b/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/VoucherIssueFailedPageViewModelTests.cs new file mode 100644 index 00000000..34df6879 --- /dev/null +++ b/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/VoucherIssueFailedPageViewModelTests.cs @@ -0,0 +1,19 @@ +namespace TransactionMobile.Maui.BusinessLogic.Tests.ViewModelTests; + +using Moq; +using UIServices; +using ViewModels.Transactions; +using Xunit; + +public class VoucherIssueFailedPageViewModelTests +{ + [Fact] + public void VoucherIssueFailedPageViewModel_CancelledCommand_Execute_IsExecuted() + { + Mock navigationService = new Mock(); + VoucherIssueFailedPageViewModel viewModel = new VoucherIssueFailedPageViewModel(navigationService.Object); + + viewModel.CancelledCommand.Execute(null); + navigationService.Verify(n => n.PopToRoot(), Times.Once); + } +} \ No newline at end of file diff --git a/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/VoucherIssueSelectProductPageViewModelTests.cs b/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/VoucherIssueSelectProductPageViewModelTests.cs new file mode 100644 index 00000000..cbddeb80 --- /dev/null +++ b/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/VoucherIssueSelectProductPageViewModelTests.cs @@ -0,0 +1,80 @@ +namespace TransactionMobile.Maui.BusinessLogic.Tests.ViewModelTests; + +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using MediatR; +using Models; +using Moq; +using Requests; +using Shouldly; +using UIServices; +using ViewModels.Transactions; +using Xunit; + +public class VoucherIssueSelectProductPageViewModelTests +{ + [Fact] + public async Task VoucherIssueSelectProductPageViewModel_ApplyQueryAttributes_QueryAttributesApplied() + { + Mock mediator = new Mock(); + mediator.Setup(m => m.Send(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.ContractProductList); + Mock navigationService = new Mock(); + VoucherSelectProductPageViewModel viewModel = new VoucherSelectProductPageViewModel(mediator.Object, navigationService.Object); + + viewModel.ApplyQueryAttributes(new Dictionary + { + {nameof(viewModel.OperatorIdentifier), TestData.OperatorIdentifier1} + }); + viewModel.OperatorIdentifier.ShouldBe(TestData.OperatorIdentifier1); + } + + [Fact] + public async Task VoucherIssueSelectProductPageViewModel_Initialise_IsInitialised() + { + Mock mediator = new Mock(); + mediator.Setup(m => m.Send(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.ContractProductList); + Mock navigationService = new Mock(); + VoucherSelectProductPageViewModel viewModel = new VoucherSelectProductPageViewModel(mediator.Object, navigationService.Object); + + viewModel.ApplyQueryAttributes(new Dictionary + { + {nameof(viewModel.OperatorIdentifier), TestData.OperatorIdentifier1} + }); + await viewModel.Initialise(CancellationToken.None); + mediator.Verify(x => x.Send(It.IsAny(), It.IsAny()), Times.Once); + + viewModel.Products.Count.ShouldBe(3); + } + + [Fact] + public async Task VoucherIssueSelectProductPageViewModel_ProductSelectedCommand_Execute_IsExecuted() + { + Mock mediator = new Mock(); + mediator.Setup(m => m.Send(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.ContractProductList); + Mock navigationService = new Mock(); + VoucherSelectProductPageViewModel viewModel = new VoucherSelectProductPageViewModel(mediator.Object, navigationService.Object); + + viewModel.ApplyQueryAttributes(new Dictionary + { + {nameof(viewModel.OperatorIdentifier), TestData.OperatorIdentifier1} + }); + await viewModel.Initialise(CancellationToken.None); + mediator.Verify(x => x.Send(It.IsAny(), It.IsAny()), Times.Once); + + viewModel.Products.Count.ShouldBe(3); + + ItemSelected selectedContractProduct = new ItemSelected + { + SelectedItemIndex = 1, + SelectedItem = TestData.Operator1Product_100KES + }; + + viewModel.ProductSelectedCommand.Execute(selectedContractProduct); + + navigationService.Verify(n => n.GoToVoucherIssueVoucherPage(It.IsAny(), It.IsAny(), + It.IsAny(), It.IsAny()), Times.Once); + + } +} \ No newline at end of file diff --git a/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/VoucherIssueSuccessPageViewModelTests.cs b/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/VoucherIssueSuccessPageViewModelTests.cs new file mode 100644 index 00000000..8ddc15a6 --- /dev/null +++ b/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/VoucherIssueSuccessPageViewModelTests.cs @@ -0,0 +1,19 @@ +namespace TransactionMobile.Maui.BusinessLogic.Tests.ViewModelTests; + +using Moq; +using UIServices; +using ViewModels.Transactions; +using Xunit; + +public class VoucherIssueSuccessPageViewModelTests +{ + [Fact] + public void VoucherIssueSuccessPageViewModel_CompletedCommand_Execute_IsExecuted() + { + Mock navigationService = new Mock(); + VoucherIssueSuccessPageViewModel viewModel = new VoucherIssueSuccessPageViewModel(navigationService.Object); + + viewModel.CompletedCommand.Execute(null); + navigationService.Verify(n => n.PopToRoot(), Times.Once); + } +} \ No newline at end of file diff --git a/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/VoucherPerformIssuePageViewModelTests.cs b/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/VoucherPerformIssuePageViewModelTests.cs new file mode 100644 index 00000000..2ab91d8c --- /dev/null +++ b/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/VoucherPerformIssuePageViewModelTests.cs @@ -0,0 +1,166 @@ +namespace TransactionMobile.Maui.BusinessLogic.Tests.ViewModelTests; + +using System; +using System.Collections.Generic; +using System.Threading; +using MediatR; +using Moq; +using Requests; +using Shouldly; +using UIServices; +using ViewModels.Transactions; +using Xunit; + +public class VoucherPerformIssuePageViewModelTests +{ + [Fact] + public void VoucherPerformIssuePageViewModel_ApplyQueryAttributes_QueryAttributesApplied() + { + Mock mediator = new Mock(); + Mock navigationService = new Mock(); + VoucherPerformIssuePageViewModel viewModel = new VoucherPerformIssuePageViewModel(mediator.Object, navigationService.Object); + + viewModel.ApplyQueryAttributes(new Dictionary + { + {nameof(viewModel.ContractId), TestData.OperatorId1ContractId}, + {nameof(viewModel.ProductId), TestData.Operator1Product_100KES.ProductId}, + {nameof(viewModel.OperatorIdentifier), TestData.OperatorIdentifier1}, + {nameof(viewModel.VoucherAmount), TestData.Operator1Product_100KES.Value} + }); + + viewModel.ContractId.ShouldBe(TestData.OperatorId1ContractId.ToString()); + viewModel.ProductId.ShouldBe(TestData.Operator1Product_100KES.ProductId.ToString()); + viewModel.OperatorIdentifier.ShouldBe(TestData.OperatorIdentifier1); + viewModel.VoucherAmount.ShouldBe(TestData.Operator1Product_100KES.Value); + } + + [Fact] + public void VoucherPerformIssuePageViewModel_CustomerEmailAddressEntryCompletedCommand_Execute_IsExecuted() + { + Mock mediator = new Mock(); + Mock navigationService = new Mock(); + VoucherPerformIssuePageViewModel viewModel = new VoucherPerformIssuePageViewModel(mediator.Object, navigationService.Object); + Boolean isCompletedCalled = false; + viewModel.OnCustomerEmailAddressEntryCompleted = () => + { + isCompletedCalled = true; + }; + + viewModel.ApplyQueryAttributes(new Dictionary + { + {nameof(viewModel.ContractId), TestData.OperatorId1ContractId}, + {nameof(viewModel.ProductId), TestData.Operator1Product_100KES.ProductId}, + {nameof(viewModel.OperatorIdentifier), TestData.OperatorIdentifier1}, + {nameof(viewModel.VoucherAmount), TestData.Operator1Product_100KES.Value} + }); + viewModel.CustomerEmailAddressEntryCompletedCommand.Execute(null); + isCompletedCalled.ShouldBeTrue(); + } + + [Fact] + public void VoucherPerformIssuePageViewModel_RecipientMobileNumberEntryCompletedCommand_Execute_IsExecuted() + { + Mock mediator = new Mock(); + Mock navigationService = new Mock(); + VoucherPerformIssuePageViewModel viewModel = new VoucherPerformIssuePageViewModel(mediator.Object, navigationService.Object); + Boolean isCompletedCalled = false; + viewModel.OnRecipientMobileNumberEntryCompleted = () => + { + isCompletedCalled = true; + }; + + viewModel.ApplyQueryAttributes(new Dictionary + { + {nameof(viewModel.ContractId), TestData.OperatorId1ContractId}, + {nameof(viewModel.ProductId), TestData.Operator1Product_100KES.ProductId}, + {nameof(viewModel.OperatorIdentifier), TestData.OperatorIdentifier1}, + {nameof(viewModel.VoucherAmount), TestData.Operator1Product_100KES.Value} + }); + viewModel.RecipientMobileNumberEntryCompletedCommand.Execute(null); + isCompletedCalled.ShouldBeTrue(); + } + + [Fact] + public void VoucherPerformIssuePageViewModel_RecipientEmailAddressEntryCompleted_Execute_IsExecuted() + { + Mock mediator = new Mock(); + Mock navigationService = new Mock(); + VoucherPerformIssuePageViewModel viewModel = new VoucherPerformIssuePageViewModel(mediator.Object, navigationService.Object); + Boolean isCompletedCalled = false; + viewModel.OnRecipientEmailAddressEntryCompleted = () => + { + isCompletedCalled = true; + }; + + viewModel.ApplyQueryAttributes(new Dictionary + { + {nameof(viewModel.ContractId), TestData.OperatorId1ContractId}, + {nameof(viewModel.ProductId), TestData.Operator1Product_100KES.ProductId}, + {nameof(viewModel.OperatorIdentifier), TestData.OperatorIdentifier1}, + {nameof(viewModel.VoucherAmount), TestData.Operator1Product_100KES.Value} + }); + viewModel.RecipientEmailAddressEntryCompletedCommand.Execute(null); + isCompletedCalled.ShouldBeTrue(); + } + + [Fact] + public void VoucherPerformIssuePageViewModel_VoucherAmountEntryCompletedCommand_Execute_IsExecuted() + { + Mock mediator = new Mock(); + Mock navigationService = new Mock(); + VoucherPerformIssuePageViewModel viewModel = new VoucherPerformIssuePageViewModel(mediator.Object, navigationService.Object); + Boolean isCompletedCalled = false; + viewModel.OnVoucherAmountEntryCompleted = () => + { + isCompletedCalled = true; + }; + + viewModel.ApplyQueryAttributes(new Dictionary + { + {nameof(viewModel.ContractId), TestData.OperatorId1ContractId}, + {nameof(viewModel.ProductId), TestData.Operator1Product_100KES.ProductId}, + {nameof(viewModel.OperatorIdentifier), TestData.OperatorIdentifier1}, + {nameof(viewModel.VoucherAmount), TestData.Operator1Product_100KES.Value} + }); + viewModel.VoucherAmountEntryCompletedCommand.Execute(null); + isCompletedCalled.ShouldBeTrue(); + } + + [Fact] + public void VoucherPerformIssuePageViewModel_IssueVoucherCommand_Execute_SuccessfulVoucher_IsExecuted() + { + Mock mediator = new Mock(); + mediator.Setup(m => m.Send(It.IsAny(), It.IsAny())).ReturnsAsync(true); + Mock navigationService = new Mock(); + VoucherPerformIssuePageViewModel viewModel = new VoucherPerformIssuePageViewModel(mediator.Object, navigationService.Object); + viewModel.ApplyQueryAttributes(new Dictionary + { + {nameof(viewModel.ContractId), TestData.OperatorId1ContractId}, + {nameof(viewModel.ProductId), TestData.Operator1Product_100KES.ProductId}, + {nameof(viewModel.OperatorIdentifier), TestData.OperatorIdentifier1}, + {nameof(viewModel.VoucherAmount), TestData.Operator1Product_100KES.Value} + }); + viewModel.IssueVoucherCommand.Execute(null); + mediator.Verify(m => m.Send(It.IsAny(), It.IsAny()), Times.Once); + navigationService.Verify(v => v.GoToVoucherIssueSuccessPage(), Times.Once); + } + + [Fact] + public void VoucherPerformIssuePageViewModel_IssueVoucherCommand_Execute_FailedVoucher_IsExecuted() + { + Mock mediator = new Mock(); + mediator.Setup(m => m.Send(It.IsAny(), It.IsAny())).ReturnsAsync(false); + Mock navigationService = new Mock(); + VoucherPerformIssuePageViewModel viewModel = new VoucherPerformIssuePageViewModel(mediator.Object, navigationService.Object); + viewModel.ApplyQueryAttributes(new Dictionary + { + {nameof(viewModel.ContractId), TestData.OperatorId1ContractId}, + {nameof(viewModel.ProductId), TestData.Operator1Product_100KES.ProductId}, + {nameof(viewModel.OperatorIdentifier), TestData.OperatorIdentifier1}, + {nameof(viewModel.VoucherAmount), TestData.Operator1Product_100KES.Value} + }); + viewModel.IssueVoucherCommand.Execute(null); + mediator.Verify(m => m.Send(It.IsAny(), It.IsAny()), Times.Once); + navigationService.Verify(v => v.GoToVoucherIssueFailedPage(), Times.Once); + } +} \ No newline at end of file diff --git a/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/VoucherSelectOperatorPageViewModelTests.cs b/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/VoucherSelectOperatorPageViewModelTests.cs new file mode 100644 index 00000000..7f47134b --- /dev/null +++ b/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/VoucherSelectOperatorPageViewModelTests.cs @@ -0,0 +1,52 @@ +namespace TransactionMobile.Maui.BusinessLogic.Tests.ViewModelTests; + +using System.Threading; +using System.Threading.Tasks; +using MediatR; +using Models; +using Moq; +using Requests; +using Shouldly; +using UIServices; +using ViewModels.Transactions; +using Xunit; + +public class VoucherSelectOperatorPageViewModelTests +{ + [Fact] + public async Task VoucherSelectOperatorPageViewModel_Initialise_IsInitialised() + { + Mock mediator = new Mock(); + mediator.Setup(m => m.Send(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.ContractProductList); + Mock navigationService = new Mock(); + VoucherSelectOperatorPageViewModel viewModel = new VoucherSelectOperatorPageViewModel(mediator.Object, navigationService.Object); + + await viewModel.Initialise(CancellationToken.None); + mediator.Verify(x => x.Send(It.IsAny(), It.IsAny()), Times.Once); + + viewModel.Operators.Count.ShouldBe(3); + } + + [Fact] + public async Task VoucherSelectOperatorPageViewModel_OperatorSelectedCommand_Execute_IsExecuted() + { + Mock mediator = new Mock(); + mediator.Setup(m => m.Send(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.ContractProductList); + Mock navigationService = new Mock(); + VoucherSelectOperatorPageViewModel viewModel = new VoucherSelectOperatorPageViewModel(mediator.Object, navigationService.Object); + + await viewModel.Initialise(CancellationToken.None); + + viewModel.Operators.Count.ShouldBe(3); + + ItemSelected selectedContractOperator = new ItemSelected + { + SelectedItemIndex = 1, + SelectedItem = TestData.ContractOperatorModel + }; + + viewModel.OperatorSelectedCommand.Execute(selectedContractOperator); + + navigationService.Verify(n => n.GoToVoucherSelectProductPage(TestData.OperatorIdentifier1), Times.Once); + } +} \ No newline at end of file diff --git a/TransactionMobile.Maui.BusinessLogic/Models/PerformVoucherIssueRequestModel.cs b/TransactionMobile.Maui.BusinessLogic/Models/PerformVoucherIssueRequestModel.cs new file mode 100644 index 00000000..627345bc --- /dev/null +++ b/TransactionMobile.Maui.BusinessLogic/Models/PerformVoucherIssueRequestModel.cs @@ -0,0 +1,31 @@ +namespace TransactionMobile.Maui.BusinessLogic.Models; + +public class PerformVoucherIssueRequestModel +{ + // TODO: should we have a base transaction request model ? + + #region Properties + + public String ApplicationVersion { get; set; } + + public Guid ContractId { get; set; } + + public String RecipientMobileNumber { get; set; } + public String RecipientEmailAddress { get; set; } + + public String CustomerEmailAddress { get; set; } + + public String DeviceIdentifier { get; set; } + + public String OperatorIdentifier { get; set; } + + public Guid ProductId { get; set; } + + public Decimal VoucherAmount { get; set; } + + public DateTime TransactionDateTime { get; set; } + + public String TransactionNumber { get; set; } + + #endregion +} \ No newline at end of file diff --git a/TransactionMobile.Maui.BusinessLogic/RequestHandlers/MerchantRequestHandler.cs b/TransactionMobile.Maui.BusinessLogic/RequestHandlers/MerchantRequestHandler.cs index d6a0595f..2854e0f8 100644 --- a/TransactionMobile.Maui.BusinessLogic/RequestHandlers/MerchantRequestHandler.cs +++ b/TransactionMobile.Maui.BusinessLogic/RequestHandlers/MerchantRequestHandler.cs @@ -26,7 +26,14 @@ public MerchantRequestHandler(IMerchantService merchantService) public async Task> Handle(GetContractProductsRequest request, CancellationToken cancellationToken) { - return await this.MerchantService.GetContractProducts(request.AccessToken, request.EstateId, request.MerchantId, cancellationToken); + List products = await this.MerchantService.GetContractProducts(request.AccessToken, request.EstateId, request.MerchantId, cancellationToken); + + if (request.ProductType.HasValue) + { + products = products.Where(p => p.ProductType == request.ProductType).ToList(); + } + + return products; } public async Task Handle(GetMerchantBalanceRequest request, diff --git a/TransactionMobile.Maui.BusinessLogic/RequestHandlers/TransactionRequestHandler.cs b/TransactionMobile.Maui.BusinessLogic/RequestHandlers/TransactionRequestHandler.cs index 92e167e3..b716e4ce 100644 --- a/TransactionMobile.Maui.BusinessLogic/RequestHandlers/TransactionRequestHandler.cs +++ b/TransactionMobile.Maui.BusinessLogic/RequestHandlers/TransactionRequestHandler.cs @@ -5,7 +5,9 @@ using Requests; using Services; -public class TransactionRequestHandler : IRequestHandler, IRequestHandler +public class TransactionRequestHandler : IRequestHandler, + IRequestHandler, + IRequestHandler { #region Fields @@ -65,4 +67,28 @@ public async Task Handle(LogonTransactionRequest request, } #endregion + + public async Task Handle(PerformVoucherIssueRequest request, + CancellationToken cancellationToken) + { + // TODO: Factory + PerformVoucherIssueRequestModel model = new PerformVoucherIssueRequestModel + { + ApplicationVersion = request.ApplicationVersion, + ContractId = request.ContractId, + RecipientEmailAddress = request.RecipientEmailAddress, + RecipientMobileNumber = request.RecipientMobileNumber, + CustomerEmailAddress = request.CustomerEmailAddress, + DeviceIdentifier = request.DeviceIdentifier, + OperatorIdentifier = request.OperatorIdentifier, + ProductId = request.ProductId, + VoucherAmount = request.VoucherAmount, + TransactionDateTime = request.TransactionDateTime, + TransactionNumber = request.TransactionNumber + }; + + Boolean result = await this.TransactionService.PerformVoucherIssue(model, cancellationToken); + + return result; + } } \ No newline at end of file diff --git a/TransactionMobile.Maui.BusinessLogic/Requests/GetContractProductsRequest.cs b/TransactionMobile.Maui.BusinessLogic/Requests/GetContractProductsRequest.cs index 40b8e43d..8a6c51ee 100644 --- a/TransactionMobile.Maui.BusinessLogic/Requests/GetContractProductsRequest.cs +++ b/TransactionMobile.Maui.BusinessLogic/Requests/GetContractProductsRequest.cs @@ -9,11 +9,13 @@ public class GetContractProductsRequest : IRequest> private GetContractProductsRequest(String accessToken, Guid estateId, - Guid merchantId) + Guid merchantId, + ProductType? productType) { this.AccessToken = accessToken; this.EstateId = estateId; this.MerchantId = merchantId; + this.ProductType = productType; } #endregion @@ -25,16 +27,17 @@ private GetContractProductsRequest(String accessToken, public Guid EstateId { get; } public Guid MerchantId { get; } - + public ProductType? ProductType { get; } #endregion #region Methods public static GetContractProductsRequest Create(String accessToken, Guid estateId, - Guid merchantId) + Guid merchantId, + ProductType? productType) { - return new GetContractProductsRequest(accessToken, estateId, merchantId); + return new GetContractProductsRequest(accessToken, estateId, merchantId,productType); } #endregion diff --git a/TransactionMobile.Maui.BusinessLogic/Requests/PerformMobileTopupRequest.cs b/TransactionMobile.Maui.BusinessLogic/Requests/PerformMobileTopupRequest.cs index 63f1c6fe..f632f0dc 100644 --- a/TransactionMobile.Maui.BusinessLogic/Requests/PerformMobileTopupRequest.cs +++ b/TransactionMobile.Maui.BusinessLogic/Requests/PerformMobileTopupRequest.cs @@ -31,6 +31,32 @@ private PerformMobileTopupRequest(DateTime transactionDateTime, #endregion + #region Properties + + public String ApplicationVersion { get; } + + public Guid ContractId { get; } + + public String CustomerAccountNumber { get; } + + public String CustomerEmailAddress { get; } + + public String DeviceIdentifier { get; } + + public String OperatorIdentifier { get; } + + public Guid ProductId { get; } + + public Decimal TopupAmount { get; } + + public DateTime TransactionDateTime { get; } + + public String TransactionNumber { get; } + + #endregion + + #region Methods + public static PerformMobileTopupRequest Create(DateTime transactionDateTime, String transactionNumber, String deviceIdentifier, @@ -54,27 +80,5 @@ public static PerformMobileTopupRequest Create(DateTime transactionDateTime, customerEmailAddress); } - #region Properties - - public String ApplicationVersion { get; } - - public Guid ContractId { get; } - - public String CustomerAccountNumber { get; } - - public String CustomerEmailAddress { get; } - - public String DeviceIdentifier { get; } - - public String OperatorIdentifier { get; } - - public Guid ProductId { get; } - - public Decimal TopupAmount { get; } - - public DateTime TransactionDateTime { get; } - - public String TransactionNumber { get; } - #endregion } \ No newline at end of file diff --git a/TransactionMobile.Maui.BusinessLogic/Requests/PerformVoucherIssueRequest.cs b/TransactionMobile.Maui.BusinessLogic/Requests/PerformVoucherIssueRequest.cs new file mode 100644 index 00000000..b644763b --- /dev/null +++ b/TransactionMobile.Maui.BusinessLogic/Requests/PerformVoucherIssueRequest.cs @@ -0,0 +1,90 @@ +namespace TransactionMobile.Maui.BusinessLogic.Requests; + +using MediatR; + +public class PerformVoucherIssueRequest : IRequest +{ + #region Constructors + + private PerformVoucherIssueRequest(DateTime transactionDateTime, + String transactionNumber, + String deviceIdentifier, + String applicationVersion, + Guid contractId, + Guid productId, + String operatorIdentifier, + String recipientMobileNumber, + String recipientEmailAddress, + Decimal voucherAmount, + String customerEmailAddress) + { + this.ApplicationVersion = applicationVersion; + this.DeviceIdentifier = deviceIdentifier; + this.TransactionDateTime = transactionDateTime; + this.TransactionNumber = transactionNumber; + this.ContractId = contractId; + this.ProductId = productId; + this.OperatorIdentifier = operatorIdentifier; + this.RecipientEmailAddress = recipientEmailAddress; + this.RecipientMobileNumber = recipientMobileNumber; + this.VoucherAmount = voucherAmount; + this.CustomerEmailAddress = customerEmailAddress; + } + + #endregion + + #region Properties + + public String ApplicationVersion { get; } + + public Guid ContractId { get; } + + public String RecipientMobileNumber { get; } + + public String RecipientEmailAddress { get; } + + public String CustomerEmailAddress { get; } + + public String DeviceIdentifier { get; } + + public String OperatorIdentifier { get; } + + public Guid ProductId { get; } + + public DateTime TransactionDateTime { get; } + + public String TransactionNumber { get; } + + public Decimal VoucherAmount { get; } + + #endregion + + #region Methods + + public static PerformVoucherIssueRequest Create(DateTime transactionDateTime, + String transactionNumber, + String deviceIdentifier, + String applicationVersion, + Guid contractId, + Guid productId, + String operatorIdentifier, + String recipientMobileNumber, + String recipientEmailAddress, + Decimal voucherAmount, + String customerEmailAddress) + { + return new PerformVoucherIssueRequest(transactionDateTime, + transactionNumber, + deviceIdentifier, + applicationVersion, + contractId, + productId, + operatorIdentifier, + recipientMobileNumber, + recipientEmailAddress, + voucherAmount, + customerEmailAddress); + } + + #endregion +} \ No newline at end of file diff --git a/TransactionMobile.Maui.BusinessLogic/Services/DummyMerchantService.cs b/TransactionMobile.Maui.BusinessLogic/Services/DummyMerchantService.cs index 768de82c..0cd5f471 100644 --- a/TransactionMobile.Maui.BusinessLogic/Services/DummyMerchantService.cs +++ b/TransactionMobile.Maui.BusinessLogic/Services/DummyMerchantService.cs @@ -11,6 +11,41 @@ public async Task> GetContractProducts(String accessT { return new List { + new ContractProductModel + { + ContractId = Guid.Parse("21DA6AC5-70E6-478E-A5E9-74C9B27F5725"), + IsFixedValue = true, + OperatorId = Guid.Parse("2CA5F4C7-34EC-425B-BB53-7EEDF48D9967"), + OperatorIdentfier = "Safaricom", + OperatorName = "Safaricom", + ProductDisplayText = "100 KES", + ProductId = Guid.Parse("CBF55D95-306A-4E85-B367-24FA442998F6"), + ProductType = ProductType.Voucher, + Value = 100 + }, + new ContractProductModel + { + ContractId = Guid.Parse("21DA6AC5-70E6-478E-A5E9-74C9B27F5725"), + IsFixedValue = true, + OperatorId = Guid.Parse("2CA5F4C7-34EC-425B-BB53-7EEDF48D9967"), + OperatorIdentfier = "Safaricom", + OperatorName = "Safaricom", + ProductDisplayText = "200 KES", + ProductId = Guid.Parse("F5F9B63F-9F68-45E8-960B-CE0FC15ED672"), + ProductType = ProductType.Voucher, + Value = 200 + }, + new ContractProductModel + { + ContractId = Guid.Parse("21DA6AC5-70E6-478E-A5E9-74C9B27F5725"), + IsFixedValue = false, + OperatorId = Guid.Parse("2CA5F4C7-34EC-425B-BB53-7EEDF48D9967"), + OperatorIdentfier = "Safaricom", + OperatorName = "Safaricom", + ProductDisplayText = "Custom", + ProductId = Guid.Parse("268CBAF5-95E0-4D4C-9725-3BB2B76E4273"), + ProductType = ProductType.Voucher + }, new ContractProductModel { ContractId = Guid.Parse("D57DAC9B-4039-4120-B5A8-F7FDF1D3A3C2"), @@ -45,8 +80,7 @@ public async Task> GetContractProducts(String accessT ProductDisplayText = "Custom", ProductId = Guid.Parse("63821A48-D35E-451D-9E88-C8DCB548E7ED"), ProductType = ProductType.MobileTopup - } - , + }, new ContractProductModel { ContractId = Guid.Parse("D57DAC9B-4039-4120-B5A8-F7FDF1D3A3C2"), diff --git a/TransactionMobile.Maui.BusinessLogic/Services/ITransactionService.cs b/TransactionMobile.Maui.BusinessLogic/Services/ITransactionService.cs index 07b53665..4ff5fa38 100644 --- a/TransactionMobile.Maui.BusinessLogic/Services/ITransactionService.cs +++ b/TransactionMobile.Maui.BusinessLogic/Services/ITransactionService.cs @@ -12,7 +12,7 @@ public interface ITransactionService Task PerformReconciliation(CancellationToken cancellationToken); - Task PerformVoucherIssue(CancellationToken cancellationToken); + Task PerformVoucherIssue(PerformVoucherIssueRequestModel model, CancellationToken cancellationToken); #endregion } @@ -39,8 +39,13 @@ public async Task PerformReconciliation(CancellationToken cancellationT return true; } - public async Task PerformVoucherIssue(CancellationToken cancellationToken) + public async Task PerformVoucherIssue(PerformVoucherIssueRequestModel model, CancellationToken cancellationToken) { + if (model.VoucherAmount == 100) + { + return false; + } + return true; } } diff --git a/TransactionMobile.Maui.BusinessLogic/UIServices/INavigationService.cs b/TransactionMobile.Maui.BusinessLogic/UIServices/INavigationService.cs index 8eb9bb0f..a1e1c796 100644 --- a/TransactionMobile.Maui.BusinessLogic/UIServices/INavigationService.cs +++ b/TransactionMobile.Maui.BusinessLogic/UIServices/INavigationService.cs @@ -19,7 +19,21 @@ Task GoToMobileTopupPerformTopupPage(String operatorIdentifier, Task GoToMobileTopupSuccessPage(); + Task GoToVoucherIssueSuccessPage(); + + Task GoToVoucherIssueFailedPage(); + Task PopToRoot(); + Task GoToVoucherSelectOperatorPage(); + + Task GoToVoucherSelectProductPage(String operatorIdentifier); + + Task GoToVoucherIssueVoucherPage(String operatorIdentifier, + Guid contractId, + Guid productId, + Decimal voucherAmount); + + #endregion } \ No newline at end of file diff --git a/TransactionMobile.Maui.BusinessLogic/ViewModels/LoginPageViewModel.cs b/TransactionMobile.Maui.BusinessLogic/ViewModels/LoginPageViewModel.cs index c8d9accf..2cc88d99 100644 --- a/TransactionMobile.Maui.BusinessLogic/ViewModels/LoginPageViewModel.cs +++ b/TransactionMobile.Maui.BusinessLogic/ViewModels/LoginPageViewModel.cs @@ -55,7 +55,7 @@ private async Task LoginCommandExecute() var merchantId = Guid.Parse("E746EACB-4E73-4E78-B732-53B9C65E5BDA"); // TODO: Get Contracts & Balance ?? - GetContractProductsRequest getContractProductsRequest = GetContractProductsRequest.Create("", estateId, merchantId); + GetContractProductsRequest getContractProductsRequest = GetContractProductsRequest.Create("", estateId, merchantId, null); // TODO: Cache the result, but will add this to a timer call to keep up to date... List products = await this.Mediator.Send(getContractProductsRequest); diff --git a/TransactionMobile.Maui.BusinessLogic/ViewModels/Transactions/MobileTopupFailedPageViewModel.cs b/TransactionMobile.Maui.BusinessLogic/ViewModels/Transactions/MobileTopupFailedPageViewModel.cs index 922a8c06..f8f28cf6 100644 --- a/TransactionMobile.Maui.BusinessLogic/ViewModels/Transactions/MobileTopupFailedPageViewModel.cs +++ b/TransactionMobile.Maui.BusinessLogic/ViewModels/Transactions/MobileTopupFailedPageViewModel.cs @@ -15,6 +15,7 @@ public MobileTopupFailedPageViewModel(INavigationService navigationService) { this.NavigationService = navigationService; this.CancelledCommand = new AsyncCommand(this.CancelledCommandExecute); + this.Title = "Mobile Topup Failed"; } #endregion diff --git a/TransactionMobile.Maui.BusinessLogic/ViewModels/Transactions/MobileTopupPerformTopupPageViewModel.cs b/TransactionMobile.Maui.BusinessLogic/ViewModels/Transactions/MobileTopupPerformTopupPageViewModel.cs index 8411d8a1..366653ad 100644 --- a/TransactionMobile.Maui.BusinessLogic/ViewModels/Transactions/MobileTopupPerformTopupPageViewModel.cs +++ b/TransactionMobile.Maui.BusinessLogic/ViewModels/Transactions/MobileTopupPerformTopupPageViewModel.cs @@ -32,7 +32,7 @@ public void ApplyQueryAttributes(IDictionary query) { this.ContractId = HttpUtility.UrlDecode(query[nameof(ContractId)].ToString()); this.ProductId = HttpUtility.UrlDecode(query[nameof(ProductId)].ToString()); - this.OperatorIdentifer = HttpUtility.UrlDecode(query[nameof(OperatorIdentifer)].ToString()); + this.OperatorIdentifier = HttpUtility.UrlDecode(query[nameof(OperatorIdentifier)].ToString()); this.TopupAmount = Decimal.Parse(HttpUtility.UrlDecode(query[nameof(TopupAmount)].ToString())); } @@ -75,7 +75,7 @@ public String CustomerMobileNumber public Action OnTopupAmountEntryCompleted { get; set; } - public String OperatorIdentifer { get; set; } + public String OperatorIdentifier { get; set; } public ICommand PerformTopupCommand { get; } @@ -112,8 +112,8 @@ private async Task PerformTopupCommandExecute() "", Guid.Parse(this.ContractId), Guid.Parse(this.ProductId), - this.OperatorIdentifer, - this.CustomerEmailAddress, + this.OperatorIdentifier, + this.CustomerMobileNumber, this.TopupAmount, this.CustomerEmailAddress); diff --git a/TransactionMobile.Maui.BusinessLogic/ViewModels/Transactions/MobileTopupSelectOperatorPageViewModel.cs b/TransactionMobile.Maui.BusinessLogic/ViewModels/Transactions/MobileTopupSelectOperatorPageViewModel.cs index fbd9af4a..43114374 100644 --- a/TransactionMobile.Maui.BusinessLogic/ViewModels/Transactions/MobileTopupSelectOperatorPageViewModel.cs +++ b/TransactionMobile.Maui.BusinessLogic/ViewModels/Transactions/MobileTopupSelectOperatorPageViewModel.cs @@ -42,12 +42,12 @@ public MobileTopupSelectOperatorPageViewModel(IMediator mediator, INavigationSer public async Task Initialise(CancellationToken cancellationToken) { - GetContractProductsRequest request = GetContractProductsRequest.Create("", Guid.Empty, Guid.Empty); + GetContractProductsRequest request = GetContractProductsRequest.Create("", Guid.Empty, Guid.Empty, ProductType.MobileTopup); List products = await this.Mediator.Send(request, cancellationToken); // TODO: Should this logic live in the Reqest handler ??? - List operators = products.Where(c => c.ProductType == ProductType.MobileTopup).GroupBy(c => new + List operators = products.GroupBy(c => new { c.OperatorName, c.OperatorId, diff --git a/TransactionMobile.Maui.BusinessLogic/ViewModels/Transactions/MobileTopupSelectProductPageViewModel.cs b/TransactionMobile.Maui.BusinessLogic/ViewModels/Transactions/MobileTopupSelectProductPageViewModel.cs index a5d826c9..c30b1d92 100644 --- a/TransactionMobile.Maui.BusinessLogic/ViewModels/Transactions/MobileTopupSelectProductPageViewModel.cs +++ b/TransactionMobile.Maui.BusinessLogic/ViewModels/Transactions/MobileTopupSelectProductPageViewModel.cs @@ -51,7 +51,7 @@ public MobileTopupSelectProductPageViewModel(IMediator mediator, INavigationServ public async Task Initialise(CancellationToken cancellationToken) { - GetContractProductsRequest request = GetContractProductsRequest.Create("",Guid.Empty, Guid.Empty); + GetContractProductsRequest request = GetContractProductsRequest.Create("",Guid.Empty, Guid.Empty, ProductType.MobileTopup); List products = await this.Mediator.Send(request, cancellationToken); diff --git a/TransactionMobile.Maui.BusinessLogic/ViewModels/Transactions/TransactionsPageViewModel.cs b/TransactionMobile.Maui.BusinessLogic/ViewModels/Transactions/TransactionsPageViewModel.cs index 669f8560..4fee1492 100644 --- a/TransactionMobile.Maui.BusinessLogic/ViewModels/Transactions/TransactionsPageViewModel.cs +++ b/TransactionMobile.Maui.BusinessLogic/ViewModels/Transactions/TransactionsPageViewModel.cs @@ -62,7 +62,7 @@ private async Task MobileWalletCommandExecute() private async Task VoucherCommandExecute() { - await this.NavigationService.GoToHome(); + await this.NavigationService.GoToVoucherSelectOperatorPage(); } #endregion diff --git a/TransactionMobile.Maui.BusinessLogic/ViewModels/Transactions/VoucherIssueFailedPageViewModel.cs b/TransactionMobile.Maui.BusinessLogic/ViewModels/Transactions/VoucherIssueFailedPageViewModel.cs new file mode 100644 index 00000000..11627331 --- /dev/null +++ b/TransactionMobile.Maui.BusinessLogic/ViewModels/Transactions/VoucherIssueFailedPageViewModel.cs @@ -0,0 +1,37 @@ +namespace TransactionMobile.Maui.BusinessLogic.ViewModels.Transactions; + +using System.Windows.Input; +using MvvmHelpers; +using MvvmHelpers.Commands; +using UIServices; + +public class VoucherIssueFailedPageViewModel : BaseViewModel +{ + private readonly INavigationService NavigationService; + + #region Constructors + + public VoucherIssueFailedPageViewModel(INavigationService navigationService) + { + this.NavigationService = navigationService; + this.CancelledCommand = new AsyncCommand(this.CancelledCommandExecute); + this.Title = "Voucher Issue Failed"; + } + + #endregion + + #region Properties + + public ICommand CancelledCommand { get; } + + #endregion + + #region Methods + + private async Task CancelledCommandExecute() + { + await this.NavigationService.PopToRoot(); + } + + #endregion +} \ No newline at end of file diff --git a/TransactionMobile.Maui.BusinessLogic/ViewModels/Transactions/VoucherIssueSuccessPageViewModel.cs b/TransactionMobile.Maui.BusinessLogic/ViewModels/Transactions/VoucherIssueSuccessPageViewModel.cs new file mode 100644 index 00000000..50ad93cd --- /dev/null +++ b/TransactionMobile.Maui.BusinessLogic/ViewModels/Transactions/VoucherIssueSuccessPageViewModel.cs @@ -0,0 +1,38 @@ +namespace TransactionMobile.Maui.BusinessLogic.ViewModels.Transactions +{ + using System.Windows.Input; + using MvvmHelpers; + using MvvmHelpers.Commands; + using UIServices; + + public class VoucherIssueSuccessPageViewModel : BaseViewModel + { + private readonly INavigationService NavigationService; + + #region Constructors + + public VoucherIssueSuccessPageViewModel(INavigationService navigationService) + { + this.NavigationService = navigationService; + this.CompletedCommand = new AsyncCommand(this.CompletedCommandExecute); + this.Title = "Voucher Issue Successful"; + } + + #endregion + + #region Properties + + public ICommand CompletedCommand { get; } + + #endregion + + #region Methods + + private async Task CompletedCommandExecute() + { + await this.NavigationService.PopToRoot(); + } + + #endregion + } +} \ No newline at end of file diff --git a/TransactionMobile.Maui.BusinessLogic/ViewModels/Transactions/VoucherPerformIssuePageViewModel.cs b/TransactionMobile.Maui.BusinessLogic/ViewModels/Transactions/VoucherPerformIssuePageViewModel.cs new file mode 100644 index 00000000..c059c26c --- /dev/null +++ b/TransactionMobile.Maui.BusinessLogic/ViewModels/Transactions/VoucherPerformIssuePageViewModel.cs @@ -0,0 +1,157 @@ +namespace TransactionMobile.Maui.BusinessLogic.ViewModels.Transactions; + +using System.Web; +using System.Windows.Input; +using MediatR; +using MvvmHelpers; +using MvvmHelpers.Commands; +using Requests; +using UIServices; +using Command = Microsoft.Maui.Controls.Command; + +public class VoucherPerformIssuePageViewModel : BaseViewModel, IQueryAttributable +{ + #region Fields + + private String customerEmailAddress; + + private String recipientMobileNumber; + + private String recipientEmailAddress; + + private readonly IMediator Mediator; + + private readonly INavigationService NavigationService; + + private Decimal voucherAmount; + + #endregion + + #region Constructors + + public void ApplyQueryAttributes(IDictionary query) + { + this.ContractId = HttpUtility.UrlDecode(query[nameof(this.ContractId)].ToString()); + this.ProductId = HttpUtility.UrlDecode(query[nameof(this.ProductId)].ToString()); + this.OperatorIdentifier = HttpUtility.UrlDecode(query[nameof(this.OperatorIdentifier)].ToString()); + this.VoucherAmount = Decimal.Parse(HttpUtility.UrlDecode(query[nameof(this.VoucherAmount)].ToString())); + } + + public VoucherPerformIssuePageViewModel(IMediator mediator, INavigationService navigationService) + { + this.Mediator = mediator; + this.NavigationService = navigationService; + this.IssueVoucherCommand = new AsyncCommand(this.IssueVoucherCommandExecute); + this.RecipientMobileNumberEntryCompletedCommand = new Command(this.RecipientMobileNumberEntryCompletedCommandExecute); + this.RecipientEmailAddressEntryCompletedCommand = new Command(this.RecipientEmailAddressEntryCompletedCommandExecute); + this.VoucherAmountEntryCompletedCommand = new Command(this.VoucherAmountEntryCompletedCommandExecute); + this.CustomerEmailAddressEntryCompletedCommand = new Command(this.CustomerEmailAddressEntryCompletedCommandExecute); + this.Title = "Enter Voucher Issue Details"; + } + + #endregion + + #region Properties + + public String ContractId { get; set; } + + public String CustomerEmailAddress + { + get => this.customerEmailAddress; + set => this.SetProperty(ref this.customerEmailAddress, value); + } + + public ICommand CustomerEmailAddressEntryCompletedCommand { get; } + + public String RecipientMobileNumber + { + get => this.recipientMobileNumber; + set => this.SetProperty(ref this.recipientMobileNumber, value); + } + + public String RecipientEmailAddress + { + get => this.recipientEmailAddress; + set => this.SetProperty(ref this.recipientEmailAddress, value); + } + + public ICommand RecipientMobileNumberEntryCompletedCommand { get; } + + public ICommand RecipientEmailAddressEntryCompletedCommand { get; } + + public Action OnCustomerEmailAddressEntryCompleted { get; set; } + + public Action OnRecipientEmailAddressEntryCompleted { get; set; } + + public Action OnRecipientMobileNumberEntryCompleted { get; set; } + + public Action OnVoucherAmountEntryCompleted { get; set; } + + public String OperatorIdentifier { get; set; } + + public ICommand IssueVoucherCommand { get; } + + public String ProductId { get; set; } + + public Decimal VoucherAmount + { + get => this.voucherAmount; + set => this.SetProperty(ref this.voucherAmount, value); + } + + public ICommand VoucherAmountEntryCompletedCommand { get; } + + #endregion + + #region Methods + + private void CustomerEmailAddressEntryCompletedCommandExecute() + { + this.OnCustomerEmailAddressEntryCompleted(); + } + + private void RecipientMobileNumberEntryCompletedCommandExecute() + { + this.OnRecipientMobileNumberEntryCompleted(); + } + + private void RecipientEmailAddressEntryCompletedCommandExecute() + { + this.OnRecipientEmailAddressEntryCompleted(); + } + + private async Task IssueVoucherCommandExecute() + { + // TODO: Create Command and Send + PerformVoucherIssueRequest request = PerformVoucherIssueRequest.Create(DateTime.Now, + "1", + "", + "", + Guid.Parse(this.ContractId), + Guid.Parse(this.ProductId), + this.OperatorIdentifier, + this.RecipientMobileNumber, + this.recipientMobileNumber, + this.VoucherAmount, + this.CustomerEmailAddress); + + Boolean response = await this.Mediator.Send(request); + + if (response) + { + await this.NavigationService.GoToVoucherIssueSuccessPage(); + + } + else + { + await this.NavigationService.GoToVoucherIssueFailedPage(); + } + } + + private void VoucherAmountEntryCompletedCommandExecute() + { + this.OnVoucherAmountEntryCompleted(); + } + + #endregion +} \ No newline at end of file diff --git a/TransactionMobile.Maui.BusinessLogic/ViewModels/Transactions/VoucherSelectOperatorPageViewModel.cs b/TransactionMobile.Maui.BusinessLogic/ViewModels/Transactions/VoucherSelectOperatorPageViewModel.cs new file mode 100644 index 00000000..e9bd5c15 --- /dev/null +++ b/TransactionMobile.Maui.BusinessLogic/ViewModels/Transactions/VoucherSelectOperatorPageViewModel.cs @@ -0,0 +1,71 @@ +namespace TransactionMobile.Maui.BusinessLogic.ViewModels.Transactions; + +using System.Windows.Input; +using MediatR; +using Models; +using MvvmHelpers; +using MvvmHelpers.Commands; +using Requests; +using UIServices; + +public class VoucherSelectOperatorPageViewModel : BaseViewModel +{ + #region Fields + + private readonly IMediator Mediator; + + private readonly INavigationService NavigationService; + + #endregion + + #region Constructors + + public VoucherSelectOperatorPageViewModel(IMediator mediator, INavigationService navigationService) + { + this.Mediator = mediator; + this.NavigationService = navigationService; + this.OperatorSelectedCommand = new AsyncCommand>(this.OperatorSelectedCommandExecute); + this.Title = "Select an Operator"; + } + + #endregion + + #region Properties + + public List Operators { get; private set; } + + public ICommand OperatorSelectedCommand { get; } + + #endregion + + #region Methods + + public async Task Initialise(CancellationToken cancellationToken) + { + GetContractProductsRequest request = GetContractProductsRequest.Create("", Guid.Empty, Guid.Empty, ProductType.Voucher); + + List products = await this.Mediator.Send(request, cancellationToken); + + // TODO: Should this logic live in the Reqest handler ??? + List operators = products.GroupBy(c => new + { + c.OperatorName, + c.OperatorId, + c.OperatorIdentfier + }).Select(g => new ContractOperatorModel + { + OperatorId = g.Key.OperatorId, + OperatorName = g.Key.OperatorName, + OperatorIdentfier = g.Key.OperatorIdentfier + }).ToList(); + + this.Operators = operators; + } + + private async Task OperatorSelectedCommandExecute(ItemSelected e) + { + await this.NavigationService.GoToVoucherSelectProductPage(e.SelectedItem.OperatorIdentfier); + } + + #endregion +} \ No newline at end of file diff --git a/TransactionMobile.Maui.BusinessLogic/ViewModels/Transactions/VoucherSelectProductPageViewModel.cs b/TransactionMobile.Maui.BusinessLogic/ViewModels/Transactions/VoucherSelectProductPageViewModel.cs new file mode 100644 index 00000000..31f2a260 --- /dev/null +++ b/TransactionMobile.Maui.BusinessLogic/ViewModels/Transactions/VoucherSelectProductPageViewModel.cs @@ -0,0 +1,68 @@ +namespace TransactionMobile.Maui.BusinessLogic.ViewModels.Transactions; + +using System.Web; +using System.Windows.Input; +using MediatR; +using Models; +using MvvmHelpers; +using MvvmHelpers.Commands; +using Requests; +using UIServices; + +public class VoucherSelectProductPageViewModel : BaseViewModel, IQueryAttributable +{ + #region Fields + + private readonly IMediator Mediator; + + private readonly INavigationService NavigationService; + + #endregion + + #region Constructors + + public void ApplyQueryAttributes(IDictionary query) + { + this.OperatorIdentifier = HttpUtility.UrlDecode(query[nameof(this.OperatorIdentifier)].ToString()); + } + + public VoucherSelectProductPageViewModel(IMediator mediator, INavigationService navigationService) + { + this.Mediator = mediator; + this.NavigationService = navigationService; + this.ProductSelectedCommand = new AsyncCommand>(this.ProductSelectedCommandExecute); + this.Title = "Select a Product"; + } + + #endregion + + #region Properties + + public String OperatorIdentifier { get; private set; } + + public List Products { get; private set; } + + public ICommand ProductSelectedCommand { get; } + + #endregion + + #region Methods + + public async Task Initialise(CancellationToken cancellationToken) + { + GetContractProductsRequest request = GetContractProductsRequest.Create("", Guid.Empty, Guid.Empty, ProductType.Voucher); + + List products = await this.Mediator.Send(request, cancellationToken); + + products = products.Where(p => p.OperatorIdentfier == this.OperatorIdentifier).ToList(); + + this.Products = products; + } + + private async Task ProductSelectedCommandExecute(ItemSelected e) + { + await this.NavigationService.GoToVoucherIssueVoucherPage(e.SelectedItem.OperatorIdentfier, e.SelectedItem.ContractId, e.SelectedItem.ProductId, e.SelectedItem.Value); + } + + #endregion +} \ No newline at end of file diff --git a/TransactionMobile.Maui/App.xaml.cs b/TransactionMobile.Maui/App.xaml.cs index 3ff57d98..17fbe1b9 100644 --- a/TransactionMobile.Maui/App.xaml.cs +++ b/TransactionMobile.Maui/App.xaml.cs @@ -28,5 +28,10 @@ public App() Routing.RegisterRoute(nameof(MobileTopupPerformTopupPage), typeof(MobileTopupPerformTopupPage)); Routing.RegisterRoute(nameof(MobileTopupSuccessPage), typeof(MobileTopupSuccessPage)); Routing.RegisterRoute(nameof(MobileTopupFailedPage), typeof(MobileTopupFailedPage)); + Routing.RegisterRoute(nameof(VoucherSelectOperatorPage), typeof(VoucherSelectOperatorPage)); + Routing.RegisterRoute(nameof(VoucherSelectProductPage), typeof(VoucherSelectProductPage)); + Routing.RegisterRoute(nameof(VoucherPerformIssuePage), typeof(VoucherPerformIssuePage)); + Routing.RegisterRoute(nameof(VoucherIssueSuccessPage), typeof(VoucherIssueSuccessPage)); + Routing.RegisterRoute(nameof(VoucherIssueFailedPage), typeof(VoucherIssueFailedPage)); } } diff --git a/TransactionMobile.Maui/Extensions/MauiAppBuilderExtensions.cs b/TransactionMobile.Maui/Extensions/MauiAppBuilderExtensions.cs index 8139dac3..7c45621c 100644 --- a/TransactionMobile.Maui/Extensions/MauiAppBuilderExtensions.cs +++ b/TransactionMobile.Maui/Extensions/MauiAppBuilderExtensions.cs @@ -37,6 +37,7 @@ public static MauiAppBuilder ConfigureRequestHandlers(this MauiAppBuilder builde builder.Services.AddSingleton, MerchantRequestHandler>(); builder.Services.AddSingleton, TransactionRequestHandler>(); builder.Services.AddSingleton, TransactionRequestHandler>(); + builder.Services.AddSingleton, TransactionRequestHandler>(); builder.Services.AddSingleton(ctx => { return t => ctx.GetService(t); }); return builder; @@ -46,12 +47,20 @@ public static MauiAppBuilder ConfigureViewModels(this MauiAppBuilder builder) { builder.Services.AddTransient(); builder.Services.AddTransient(); + builder.Services.AddTransient(); builder.Services.AddTransient(); builder.Services.AddTransient(); builder.Services.AddTransient(); builder.Services.AddTransient(); + builder.Services.AddTransient(); + builder.Services.AddTransient(); + builder.Services.AddTransient(); + builder.Services.AddTransient(); + builder.Services.AddTransient(); + + return builder; } diff --git a/TransactionMobile.Maui/Pages/Transactions/Voucher/VoucherIssueFailedPage.xaml b/TransactionMobile.Maui/Pages/Transactions/Voucher/VoucherIssueFailedPage.xaml new file mode 100644 index 00000000..703872d7 --- /dev/null +++ b/TransactionMobile.Maui/Pages/Transactions/Voucher/VoucherIssueFailedPage.xaml @@ -0,0 +1,25 @@ + + + +