Skip to content

Commit 62b8509

Browse files
Merge pull request #1831 from SkillsFundingAgency/APPMAN-1019-Add-LTM
APPMAN-1019 added LTM for selecting funding
2 parents a9e3b14 + 782dd26 commit 62b8509

File tree

10 files changed

+232
-59
lines changed

10 files changed

+232
-59
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
using System;
2+
using System.Net;
3+
using System.Threading;
4+
using System.Threading.Tasks;
5+
using AutoFixture.NUnit3;
6+
using FluentAssertions;
7+
using MediatR;
8+
using Microsoft.AspNetCore.Mvc;
9+
using Moq;
10+
using NUnit.Framework;
11+
using SFA.DAS.Approvals.Api.Controllers;
12+
using SFA.DAS.Approvals.Application.LevyTransferMatching.Queries.GetApprovedAccountApplication;
13+
using SFA.DAS.Approvals.Application.SelectDirectTransferConnection.Queries;
14+
using SFA.DAS.Testing.AutoFixture;
15+
16+
namespace SFA.DAS.Approvals.Api.UnitTests.Controllers.SelectLevyConnection;
17+
18+
public class WhenGettingSelectLevyConnection
19+
{
20+
[Test, MoqAutoData]
21+
public async Task Then_Get_Returns_LevyConnections_From_Mediator(
22+
long accountId,
23+
GetAcceptedEmployerAccountApplicationsQueryResult mediatorResult,
24+
[Frozen] Mock<IMediator> mockMediator,
25+
[Greedy] SelectAcceptedLevyApplicationsController controller)
26+
{
27+
mockMediator.Setup(mediator => mediator.Send(
28+
It.Is<GetAcceptedEmployerAccountApplicationsQuery>(x => x.EmployerAccountId == accountId),
29+
It.IsAny<CancellationToken>()))
30+
.ReturnsAsync(mediatorResult);
31+
32+
var controllerResult = await controller.Get(accountId) as ObjectResult;
33+
34+
controllerResult.Should().NotBeNull();
35+
controllerResult.StatusCode.Should().Be((int)HttpStatusCode.OK);
36+
var model = controllerResult.Value as GetAcceptedEmployerAccountApplicationsQueryResult;
37+
model.Should().NotBeNull();
38+
model.Should().BeEquivalentTo(mediatorResult);
39+
}
40+
41+
[Test, MoqAutoData]
42+
public async Task And_Exception_Then_Returns_Bad_Request(
43+
long accountId,
44+
[Frozen] Mock<IMediator> mockMediator,
45+
[Greedy] SelectAcceptedLevyApplicationsController controller)
46+
{
47+
mockMediator
48+
.Setup(mediator => mediator.Send(
49+
It.IsAny<GetAcceptedEmployerAccountApplicationsQuery>(),
50+
It.IsAny<CancellationToken>()))
51+
.Throws<InvalidOperationException>();
52+
53+
var controllerResult = await controller.Get(accountId) as StatusCodeResult;
54+
55+
controllerResult.StatusCode.Should().Be((int)HttpStatusCode.InternalServerError);
56+
}
57+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
using System;
2+
using System.Net;
3+
using System.Threading.Tasks;
4+
using MediatR;
5+
using Microsoft.AspNetCore.Mvc;
6+
using Microsoft.Extensions.Logging;
7+
using SFA.DAS.Approvals.Application.LevyTransferMatching.Queries.GetApprovedAccountApplication;
8+
9+
namespace SFA.DAS.Approvals.Api.Controllers
10+
{
11+
[ApiController]
12+
public class SelectAcceptedLevyApplicationsController : Controller
13+
{
14+
private readonly IMediator _mediator;
15+
private readonly ILogger<SelectAcceptedLevyApplicationsController> _logger;
16+
17+
public SelectAcceptedLevyApplicationsController(IMediator mediator, ILogger<SelectAcceptedLevyApplicationsController> logger)
18+
{
19+
_mediator = mediator;
20+
_logger = logger;
21+
}
22+
23+
[HttpGet]
24+
[Route("{accountId}/unapproved/add/select-funding/select-accepted-levy-connection")]
25+
public async Task<IActionResult> Get(long accountId)
26+
{
27+
try
28+
{
29+
_logger.LogInformation("Getting Levy Transfer Connections for Account {accountId}", accountId);
30+
var result = await _mediator.Send(new GetAcceptedEmployerAccountApplicationsQuery { EmployerAccountId = accountId});
31+
return Ok(result);
32+
}
33+
catch (Exception e)
34+
{
35+
_logger.LogError(e, "Error when getting Levy Transfer Connections for Account {accountId}", accountId);
36+
return new StatusCodeResult((int) HttpStatusCode.InternalServerError);
37+
}
38+
}
39+
}
40+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
using System.Threading;
2+
using System.Threading.Tasks;
3+
using AutoFixture.NUnit3;
4+
using FluentAssertions;
5+
using Moq;
6+
using NUnit.Framework;
7+
using SFA.DAS.Approvals.Application.LevyTransferMatching.Queries.GetApprovedAccountApplication;
8+
using SFA.DAS.Approvals.InnerApi.Requests;
9+
using SFA.DAS.SharedOuterApi.Configuration;
10+
using SFA.DAS.SharedOuterApi.InnerApi.Responses.LevyTransferMatching;
11+
using SFA.DAS.SharedOuterApi.Interfaces;
12+
using SFA.DAS.Testing.AutoFixture;
13+
14+
namespace SFA.DAS.Approvals.UnitTests.Application.SelectAcceptedEmployerAccountApplications;
15+
16+
public class WhenGettingAcceptedEmployerAccountApplications
17+
{
18+
[Test, MoqAutoData]
19+
public async Task Then_The_Api_To_GetAcceptedEmployerAccountApplications_Returns_ExpectedValues(
20+
GetAcceptedEmployerAccountApplicationsQuery query,
21+
GetApplicationsResponse response,
22+
[Frozen] Mock<ILevyTransferMatchingApiClient<LevyTransferMatchingApiConfiguration>> client,
23+
GetAcceptedEmployerAccountApplicationsQueryHandler handler
24+
)
25+
{
26+
client.Setup(x =>
27+
x.Get<GetApplicationsResponse>(
28+
It.Is<GetAcceptedEmployerAccountPledgeApplicationsRequest>(x =>
29+
x.EmployerAccountId == query.EmployerAccountId)))
30+
.ReturnsAsync(response);
31+
32+
var actual = await handler.Handle(query, CancellationToken.None);
33+
34+
actual.Applications.Should().BeEquivalentTo(response.Applications);
35+
}
36+
}

src/Approvals/SFA.DAS.Approvals.UnitTests/Application/SelectFundingOptions/WhenGettingFundingOption.cs

Lines changed: 21 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
using SFA.DAS.SharedOuterApi.Configuration;
1313
using SFA.DAS.SharedOuterApi.InnerApi.Requests.EmployerFinance;
1414
using SFA.DAS.SharedOuterApi.InnerApi.Responses.EmployerFinance;
15+
using SFA.DAS.SharedOuterApi.InnerApi.Responses.LevyTransferMatching;
1516
using SFA.DAS.SharedOuterApi.Interfaces;
1617
using SFA.DAS.Testing.AutoFixture;
1718

@@ -22,31 +23,16 @@ public class WhenGettingFundingOption
2223
[Test, MoqAutoData]
2324
public async Task ThenDirectTransfersAreAvailable_WhenTransferConnectionsExist(
2425
GetSelectFundingOptionsQuery query,
25-
GetAccountReservationsStatusResponse reservationsResponse,
2626
IEnumerable<GetTransferConnectionsResponse.TransferConnection> directTransfersResponse,
27-
GetAccountResponse accountResponse,
28-
[Frozen] Mock<IReservationApiClient<ReservationApiConfiguration>> reservationsApiClient,
2927
[Frozen] Mock<IFinanceApiClient<FinanceApiConfiguration>> financeApiClient,
30-
[Frozen] Mock<IAccountsApiClient<AccountsConfiguration>> accountsApiClient,
3128
GetSelectFundingOptionsQueryHandler handler
3229
)
3330
{
34-
reservationsApiClient.Setup(x =>
35-
x.Get<GetAccountReservationsStatusResponse>(
36-
It.Is<GetAccountReservationsStatusRequest>(x =>
37-
x.AccountId == query.AccountId && x.TransferSenderId == null)))
38-
.ReturnsAsync(reservationsResponse);
39-
4031
financeApiClient.Setup(x =>
4132
x.Get<IEnumerable<GetTransferConnectionsResponse.TransferConnection>>(
4233
It.Is<GetTransferConnectionsRequest>(x => x.AccountId == query.AccountId)))
4334
.ReturnsAsync(directTransfersResponse);
44-
45-
accountsApiClient.Setup(x =>
46-
x.Get<GetAccountResponse>(
47-
It.Is<GetAccountRequest>(x => x.HashedAccountId == query.AccountId.ToString())))
48-
.ReturnsAsync(accountResponse);
49-
35+
5036
var actual = await handler.Handle(query, CancellationToken.None);
5137

5238
actual.HasDirectTransfersAvailable.Should().BeTrue();
@@ -56,10 +42,7 @@ GetSelectFundingOptionsQueryHandler handler
5642
public async Task ThenHasReservationsAvailable_WhenLimitNotReached(
5743
GetSelectFundingOptionsQuery query,
5844
GetAccountReservationsStatusResponse reservationsResponse,
59-
GetAccountResponse accountResponse,
6045
[Frozen] Mock<IReservationApiClient<ReservationApiConfiguration>> reservationsApiClient,
61-
[Frozen] Mock<IFinanceApiClient<FinanceApiConfiguration>> financeApiClient,
62-
[Frozen] Mock<IAccountsApiClient<AccountsConfiguration>> accountsApiClient,
6346
GetSelectFundingOptionsQueryHandler handler
6447
)
6548
{
@@ -71,16 +54,6 @@ GetSelectFundingOptionsQueryHandler handler
7154
x.AccountId == query.AccountId && x.TransferSenderId == null)))
7255
.ReturnsAsync(reservationsResponse);
7356

74-
financeApiClient.Setup(x =>
75-
x.Get<GetTransferConnectionsResponse>(
76-
It.Is<GetTransferConnectionsRequest>(x => x.AccountId == query.AccountId)))
77-
.ReturnsAsync(new GetTransferConnectionsResponse());
78-
79-
accountsApiClient.Setup(x =>
80-
x.Get<GetAccountResponse>(
81-
It.Is<GetAccountRequest>(x => x.HashedAccountId == query.AccountId.ToString())))
82-
.ReturnsAsync(accountResponse);
83-
8457
var actual = await handler.Handle(query, CancellationToken.None);
8558

8659
actual.HasAdditionalReservationFundsAvailable.Should().BeTrue();
@@ -90,10 +63,7 @@ GetSelectFundingOptionsQueryHandler handler
9063
public async Task ThenHasUnallocatedReservations_WhenPendingReservationsExist(
9164
GetSelectFundingOptionsQuery query,
9265
GetAccountReservationsStatusResponse reservationsResponse,
93-
GetAccountResponse accountResponse,
9466
[Frozen] Mock<IReservationApiClient<ReservationApiConfiguration>> reservationsApiClient,
95-
[Frozen] Mock<IFinanceApiClient<FinanceApiConfiguration>> financeApiClient,
96-
[Frozen] Mock<IAccountsApiClient<AccountsConfiguration>> accountsApiClient,
9767
GetSelectFundingOptionsQueryHandler handler
9868
)
9969
{
@@ -105,16 +75,6 @@ GetSelectFundingOptionsQueryHandler handler
10575
x.AccountId == query.AccountId && x.TransferSenderId == null)))
10676
.ReturnsAsync(reservationsResponse);
10777

108-
financeApiClient.Setup(x =>
109-
x.Get<GetTransferConnectionsResponse>(
110-
It.Is<GetTransferConnectionsRequest>(x => x.AccountId == query.AccountId)))
111-
.ReturnsAsync(new GetTransferConnectionsResponse());
112-
113-
accountsApiClient.Setup(x =>
114-
x.Get<GetAccountResponse>(
115-
It.Is<GetAccountRequest>(x => x.HashedAccountId == query.AccountId.ToString())))
116-
.ReturnsAsync(accountResponse);
117-
11878
var actual = await handler.Handle(query, CancellationToken.None);
11979

12080
actual.HasUnallocatedReservationsAvailable.Should().BeTrue();
@@ -123,27 +83,13 @@ GetSelectFundingOptionsQueryHandler handler
12383
[Test, MoqAutoData]
12484
public async Task ThenIsLevyAccount_WhenEmploymentTypeIsLevy(
12585
GetSelectFundingOptionsQuery query,
126-
GetAccountReservationsStatusResponse reservationsResponse,
12786
GetAccountResponse accountResponse,
128-
[Frozen] Mock<IReservationApiClient<ReservationApiConfiguration>> reservationsApiClient,
129-
[Frozen] Mock<IFinanceApiClient<FinanceApiConfiguration>> financeApiClient,
13087
[Frozen] Mock<IAccountsApiClient<AccountsConfiguration>> accountsApiClient,
13188
GetSelectFundingOptionsQueryHandler handler
13289
)
13390
{
13491
accountResponse.ApprenticeshipEmployerType = "LEVY";
13592

136-
reservationsApiClient.Setup(x =>
137-
x.Get<GetAccountReservationsStatusResponse>(
138-
It.Is<GetAccountReservationsStatusRequest>(x =>
139-
x.AccountId == query.AccountId && x.TransferSenderId == null)))
140-
.ReturnsAsync(reservationsResponse);
141-
142-
financeApiClient.Setup(x =>
143-
x.Get<GetTransferConnectionsResponse>(
144-
It.Is<GetTransferConnectionsRequest>(x => x.AccountId == query.AccountId)))
145-
.ReturnsAsync(new GetTransferConnectionsResponse());
146-
14793
accountsApiClient.Setup(x =>
14894
x.Get<GetAccountResponse>(
14995
It.Is<GetAccountRequest>(x => x.HashedAccountId == query.AccountId.ToString())))
@@ -153,4 +99,23 @@ GetSelectFundingOptionsQueryHandler handler
15399

154100
actual.IsLevyAccount.Should().BeTrue();
155101
}
102+
103+
[Test, MoqAutoData]
104+
public async Task ThenHasLtmTransfers_WhenApprovedApplicationsExist(
105+
GetSelectFundingOptionsQuery query,
106+
GetApplicationsResponse ltmTransfersResponse,
107+
[Frozen] Mock<ILevyTransferMatchingApiClient<LevyTransferMatchingApiConfiguration>> ltmApiClient,
108+
GetSelectFundingOptionsQueryHandler handler
109+
)
110+
{
111+
ltmApiClient.Setup(x =>
112+
x.Get<GetApplicationsResponse>(
113+
It.Is<GetAcceptedEmployerAccountPledgeApplicationsRequest>(x => x.EmployerAccountId == query.AccountId)))
114+
.ReturnsAsync(ltmTransfersResponse);
115+
116+
var actual = await handler.Handle(query, CancellationToken.None);
117+
118+
actual.HasLtmTransfersAvailable.Should().BeTrue();
119+
}
120+
156121
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
using MediatR;
2+
3+
namespace SFA.DAS.Approvals.Application.LevyTransferMatching.Queries.GetApprovedAccountApplication
4+
{
5+
public class GetAcceptedEmployerAccountApplicationsQuery : IRequest<GetAcceptedEmployerAccountApplicationsQueryResult>
6+
{
7+
public long EmployerAccountId { get; set; }
8+
}
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
using System.Threading;
2+
using System.Threading.Tasks;
3+
using MediatR;
4+
using SFA.DAS.Approvals.InnerApi.Requests;
5+
using SFA.DAS.SharedOuterApi.Configuration;
6+
using SFA.DAS.SharedOuterApi.InnerApi.Responses.LevyTransferMatching;
7+
using SFA.DAS.SharedOuterApi.Interfaces;
8+
9+
namespace SFA.DAS.Approvals.Application.LevyTransferMatching.Queries.GetApprovedAccountApplication
10+
{
11+
public class GetAcceptedEmployerAccountApplicationsQueryHandler : IRequestHandler<GetAcceptedEmployerAccountApplicationsQuery, GetAcceptedEmployerAccountApplicationsQueryResult>
12+
{
13+
private readonly ILevyTransferMatchingApiClient<LevyTransferMatchingApiConfiguration> _apiClient;
14+
15+
public GetAcceptedEmployerAccountApplicationsQueryHandler(ILevyTransferMatchingApiClient<LevyTransferMatchingApiConfiguration> apiClient)
16+
{
17+
_apiClient = apiClient;
18+
}
19+
20+
public async Task<GetAcceptedEmployerAccountApplicationsQueryResult> Handle(GetAcceptedEmployerAccountApplicationsQuery request, CancellationToken cancellationToken)
21+
{
22+
var result = await _apiClient.Get<GetApplicationsResponse>(new GetAcceptedEmployerAccountPledgeApplicationsRequest(request.EmployerAccountId));
23+
24+
if (result == null)
25+
return null;
26+
27+
return new GetAcceptedEmployerAccountApplicationsQueryResult
28+
{
29+
Applications = result.Applications
30+
};
31+
}
32+
}
33+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
using System.Collections.Generic;
2+
using SFA.DAS.SharedOuterApi.InnerApi.Responses.LevyTransferMatching;
3+
4+
namespace SFA.DAS.Approvals.Application.LevyTransferMatching.Queries.GetApprovedAccountApplication
5+
{
6+
public class GetAcceptedEmployerAccountApplicationsQueryResult
7+
{
8+
public IEnumerable<GetApplicationsResponse.Application> Applications { get; set; }
9+
}
10+
}

src/Approvals/SFA.DAS.Approvals/Application/SelectFunding/Queries/GetSelectFundingOptionsQueryHandler.cs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
using SFA.DAS.SharedOuterApi.Configuration;
1010
using SFA.DAS.SharedOuterApi.InnerApi.Requests.EmployerFinance;
1111
using SFA.DAS.SharedOuterApi.InnerApi.Responses.EmployerFinance;
12+
using SFA.DAS.SharedOuterApi.InnerApi.Responses.LevyTransferMatching;
1213
using SFA.DAS.SharedOuterApi.Interfaces;
1314

1415
namespace SFA.DAS.Approvals.Application.SelectFunding.Queries;
@@ -17,32 +18,38 @@ public class GetSelectFundingOptionsQueryHandler : IRequestHandler<GetSelectFund
1718
private readonly IFinanceApiClient<FinanceApiConfiguration> _financeApiClient;
1819
private readonly IReservationApiClient<ReservationApiConfiguration> _reservationsApiClient;
1920
private readonly IAccountsApiClient<AccountsConfiguration> _accountsApiClient;
21+
private readonly ILevyTransferMatchingApiClient<LevyTransferMatchingApiConfiguration> _ltmApiClient;
2022

2123
public GetSelectFundingOptionsQueryHandler(IFinanceApiClient<FinanceApiConfiguration> financeApiClient,
2224
IReservationApiClient<ReservationApiConfiguration> reservationsApiClient,
23-
IAccountsApiClient<AccountsConfiguration> accountsApiClient)
25+
IAccountsApiClient<AccountsConfiguration> accountsApiClient,
26+
ILevyTransferMatchingApiClient<LevyTransferMatchingApiConfiguration> ltmApiClient)
2427
{
2528
_financeApiClient = financeApiClient;
2629
_reservationsApiClient = reservationsApiClient;
2730
_accountsApiClient = accountsApiClient;
31+
_ltmApiClient = ltmApiClient;
2832
}
2933

3034
public async Task<GetSelectFundingOptionsQueryResult> Handle(GetSelectFundingOptionsQuery request, CancellationToken cancellationToken)
3135
{
3236
var statusTask = _reservationsApiClient.Get<GetAccountReservationsStatusResponse>(new GetAccountReservationsStatusRequest(request.AccountId, null));
3337
var connectionsTask = _financeApiClient.Get<IEnumerable<GetTransferConnectionsResponse.TransferConnection>>(new GetTransferConnectionsRequest { AccountId = request.AccountId});
3438
var accountTask = _accountsApiClient.Get<GetAccountResponse>(new GetAccountRequest(request.AccountId.ToString()));
39+
var ltmTask = _ltmApiClient.Get<GetApplicationsResponse>(new GetAcceptedEmployerAccountPledgeApplicationsRequest(request.AccountId));
3540

36-
await Task.WhenAll(statusTask, connectionsTask, accountTask);
41+
await Task.WhenAll(statusTask, connectionsTask, accountTask, ltmTask);
3742

3843
var status = await statusTask;
3944
var connections = await connectionsTask;
4045
var account = await accountTask;
46+
var ltm = await ltmTask;
4147

4248
return new GetSelectFundingOptionsQueryResult
4349
{
44-
IsLevyAccount = account.ApprenticeshipEmployerType.Equals("Levy", StringComparison.InvariantCultureIgnoreCase),
50+
IsLevyAccount = account.ApprenticeshipEmployerType?.Equals("Levy", StringComparison.InvariantCultureIgnoreCase) ?? false,
4551
HasDirectTransfersAvailable = connections?.Any() ?? false,
52+
HasLtmTransfersAvailable = ltm.Applications?.Any() ?? false,
4653
HasAdditionalReservationFundsAvailable = !status.HasReachedReservationsLimit,
4754
HasUnallocatedReservationsAvailable = status.HasPendingReservations
4855
};

0 commit comments

Comments
 (0)