Skip to content

Commit

Permalink
Add endpoint for updating induction status from Welsh induction outco…
Browse files Browse the repository at this point in the history
…me (#1752)
  • Loading branch information
gunndabad authored Dec 17, 2024
1 parent dbc2969 commit 946e8ef
Show file tree
Hide file tree
Showing 11 changed files with 568 additions and 16 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Diagnostics;
using Microsoft.Xrm.Sdk.Query;
using TeachingRecordSystem.Api.Infrastructure.Security;
using TeachingRecordSystem.Core.DataStore.Postgres;
using TeachingRecordSystem.Core.DataStore.Postgres.Models;
using TeachingRecordSystem.Core.Dqt;
Expand All @@ -22,6 +23,7 @@ public class SetCpdInductionStatusHandler(
TrsDbContext dbContext,
ICrmQueryDispatcher crmQueryDispatcher,
TrsDataSyncHelper syncHelper,
ICurrentUserProvider currentUserProvider,
IClock clock)
{
public async Task<ApiResult<SetCpdInductionStatusResult>> HandleAsync(SetCpdInductionStatusCommand command)
Expand Down Expand Up @@ -88,12 +90,14 @@ public async Task<ApiResult<SetCpdInductionStatusResult>> HandleAsync(SetCpdIndu
return ApiError.StaleRequest(cpdInductionCpdModifiedOn);
}

var (currentUserId, _) = currentUserProvider.GetCurrentApplicationUser();

person.SetCpdInductionStatus(
command.Status,
command.StartDate,
command.CompletedDate,
command.CpdModifiedOn,
PostgresModels.SystemUser.SystemUserId,
currentUserId,
clock.UtcNow,
out var updatedEvent);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
using System.Diagnostics;
using Microsoft.Xrm.Sdk.Query;
using TeachingRecordSystem.Api.Infrastructure.Security;
using TeachingRecordSystem.Core.DataStore.Postgres;
using TeachingRecordSystem.Core.DataStore.Postgres.Models;
using TeachingRecordSystem.Core.Dqt;
using TeachingRecordSystem.Core.Dqt.Models;
using TeachingRecordSystem.Core.Dqt.Queries;
using TeachingRecordSystem.Core.Services.TrsDataSync;

namespace TeachingRecordSystem.Api.V3.Implementation.Operations;

public record SetWelshInductionStatusCommand(string Trn, bool Passed, DateOnly StartDate, DateOnly CompletedDate);

public record SetWelshInductionStatusResult;

public class SetWelshInductionStatusHandler(
TrsDbContext dbContext,
ICrmQueryDispatcher crmQueryDispatcher,
TrsDataSyncHelper syncHelper,
ICurrentUserProvider currentUserProvider,
IClock clock)
{
public async Task<ApiResult<SetWelshInductionStatusResult>> HandleAsync(SetWelshInductionStatusCommand command)
{
var dqtContact = await crmQueryDispatcher.ExecuteQueryAsync(
new GetActiveContactByTrnQuery(command.Trn, new ColumnSet(Contact.Fields.dfeta_QTSDate)));

if (dqtContact is null)
{
return ApiError.PersonNotFound(command.Trn);
}

if (dqtContact.dfeta_QTSDate is null)
{
return ApiError.PersonDoesNotHaveQts(command.Trn);
}

await using var txn = await dbContext.Database.BeginTransactionAsync(System.Data.IsolationLevel.ReadCommitted);

var person = await GetPersonAsync();

if (person is null)
{
// The person record hasn't synced to TRS yet - force that to happen so we can assign induction status
var synced = await syncHelper.SyncPersonAsync(dqtContact.Id);
if (!synced)
{
throw new Exception($"Could not sync Person with contact ID: '{dqtContact.Id}'.");
}

person = await GetPersonAsync();
Debug.Assert(person is not null);
}

var (currentUserId, _) = currentUserProvider.GetCurrentApplicationUser();

person.TrySetWelshInductionStatus(
command.Passed,
!command.Passed ? command.StartDate : null,
!command.Passed ? command.CompletedDate : null,
currentUserId,
clock.UtcNow,
out var updatedEvent);

if (updatedEvent is not null)
{
dbContext.AddEvent(updatedEvent);
}

await dbContext.SaveChangesAsync();
await txn.CommitAsync();

return new SetWelshInductionStatusResult();

Task<Person?> GetPersonAsync() => dbContext.Persons.SingleOrDefaultAsync(p => p.Trn == command.Trn);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public class PersonsController(IMapper mapper) : ControllerBase
{
[HttpPut("{trn}/cpd-induction")]
[SwaggerOperation(
OperationId = "SetPersonInductionStatus",
OperationId = "SetPersonCpdInductionStatus",
Summary = "Set person induction status",
Description = "Sets the induction details of the person with the given TRN.")]
[ProducesResponseType(typeof(void), StatusCodes.Status204NoContent)]
Expand All @@ -44,6 +44,32 @@ public async Task<IActionResult> SetCpdInductionStatusAsync(
.MapErrorCode(ApiError.ErrorCodes.StaleRequest, StatusCodes.Status409Conflict);
}

[HttpPut("{trn}/welsh-induction")]
[SwaggerOperation(
OperationId = "SetPersonWelshInductionStatus",
Summary = "Set person induction status",
Description = "Sets the induction details of the person with the given TRN.")]
[ProducesResponseType(typeof(void), StatusCodes.Status204NoContent)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)]
[Authorize(Policy = AuthorizationPolicies.ApiKey, Roles = ApiRoles.SetWelshInduction)]
public async Task<IActionResult> SetWelshInductionStatusAsync(
[FromRoute] string trn,
[FromBody] SetWelshInductionStatusRequest request,
[FromServices] SetWelshInductionStatusHandler handler)
{
var command = new SetWelshInductionStatusCommand(
trn,
request.Passed,
request.StartDate,
request.CompletedDate);

var result = await handler.HandleAsync(command);

return result.ToActionResult(_ => NoContent())
.MapErrorCode(ApiError.ErrorCodes.PersonNotFound, StatusCodes.Status404NotFound);
}

[HttpGet("{trn}")]
[SwaggerOperation(
OperationId = "GetPersonByTrn",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace TeachingRecordSystem.Api.V3.VNext.Requests;

public record SetWelshInductionStatusRequest
{
public required bool Passed { get; init; }
public required DateOnly StartDate { get; init; }
public required DateOnly CompletedDate { get; init; }
}
10 changes: 6 additions & 4 deletions TeachingRecordSystem/src/TeachingRecordSystem.Core/ApiRoles.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ public static class ApiRoles
public const string AppropriateBody = "AppropriateBody";
public const string UpdateRole = "UpdateRole";
public const string SetCpdInduction = "SetCpdInduction";
public const string SetWelshInduction = "SetWelshInduction";

public static IReadOnlyCollection<string> All { get; } = new[]
{
public static IReadOnlyCollection<string> All { get; } =
[
GetPerson,
UpdatePerson,
UpdateNpq,
Expand All @@ -22,6 +23,7 @@ public static class ApiRoles
AssignQtls,
AppropriateBody,
UpdateRole,
SetCpdInduction
};
SetCpdInduction,
SetWelshInduction
];
}
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,95 @@ public void SetInductionStatus(
};
}

public bool TrySetWelshInductionStatus(
bool passed,
DateOnly? startDate,
DateOnly? completedDate,
EventModels.RaisedByUserInfo updatedBy,
DateTime now,
[NotNullWhen(true)] out PersonInductionUpdatedEvent? @event)
{
var newStatus = GetInductionStatusFromWelshOutcome(passed, out var exemptionReasons);

// FUTURE When we have QTS in TRS - assert person has QTS
AssertInductionChangeIsValid(newStatus, startDate, completedDate, exemptionReasons);

if (InductionStatus is InductionStatus.RequiredToComplete)
{
if (newStatus == InductionStatus.Exempt)
{
InductionStatus = newStatus;
InductionExemptionReasons = exemptionReasons;
InductionModifiedOn = now;

@event = new PersonInductionUpdatedEvent
{
PersonId = PersonId,
InductionStatus = newStatus,
InductionStartDate = startDate,
InductionCompletedDate = completedDate,
InductionExemptionReasons = InductionExemptionReasons,
CpdInductionStatus = default,
CpdInductionStartDate = default,
CpdInductionCompletedDate = default,
CpdInductionCpdModifiedOn = default,
ChangeReason = null,
ChangeReasonDetail = null,
EvidenceFile = null,
Changes = PersonInductionUpdatedEventChanges.InductionStatus |
PersonInductionUpdatedEventChanges.InductionStartDate |
PersonInductionUpdatedEventChanges.InductionCompletedDate,
EventId = Guid.NewGuid(),
CreatedUtc = now,
RaisedBy = updatedBy
};

return true;
}
else if (newStatus == InductionStatus.FailedInWales)
{
InductionStatus = newStatus;
InductionModifiedOn = now;

@event = new PersonInductionUpdatedEvent
{
PersonId = PersonId,
InductionStatus = newStatus,
InductionStartDate = null,
InductionCompletedDate = null,
InductionExemptionReasons = exemptionReasons,
CpdInductionStatus = default,
CpdInductionStartDate = default,
CpdInductionCompletedDate = default,
CpdInductionCpdModifiedOn = default,
ChangeReason = null,
ChangeReasonDetail = null,
EvidenceFile = null,
Changes = PersonInductionUpdatedEventChanges.InductionStatus | PersonInductionUpdatedEventChanges.InductionExemptionReasons,
EventId = Guid.NewGuid(),
CreatedUtc = now,
RaisedBy = updatedBy
};

return true;
}
}

@event = null;
return false;
}

public static InductionStatus GetInductionStatusFromWelshOutcome(bool passed, out InductionExemptionReasons exemptionReasons)
{
var status = passed ? InductionStatus.Exempt : InductionStatus.FailedInWales;

exemptionReasons = passed
? InductionExemptionReasons.PassedInductionInWales
: InductionExemptionReasons.None;

return status;
}

public static bool ValidateInductionData(
InductionStatus status,
DateOnly? startDate,
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
namespace TeachingRecordSystem.Core.Models;

[Flags]
public enum InductionExemptionReasons
{
None = 0,
QualifiedBefore07052000 = 1 << 0,
QualifiedBetween07051999And01042003FirstPostWasInWalesAndLastedAMinimumOfTwoTerms = 1 << 1,
QualifiedThroughFurtherEducationRouteBetween01092001And01092004 = 1 << 2,
PassedInductionInGuernsey = 1 << 3,
PassedInductionInIsleOfMan = 1 << 4,
PassedInductionInJersey = 1 << 5,
PassedInductionInNorthernIreland = 1 << 6,
PassedInductionInServiceChildrensEducationSchoolsInGermanyOrCyprus = 1 << 7,
PassedInductionInWales = 1 << 8,
PassedProbationaryPeriodInGibraltar = 1 << 9,
Exempt = 1 << 10,
ExemptDataLossOrErrorCriteria = 1 << 11,
HasOrIsEligibleForFullRegistrationInScotland = 1 << 12,
OverseasTrainedTeacher = 1 << 13,
QualifiedThroughEeaMutualRecognitionRoute = 1 << 14,
RegisteredTeacherWithAtLeast2YearsFullTimeTeachingExperience = 1 << 15,
ExemptThroughQtlsProvidedTheyMaintainMembershipOfTheSocietyOfEducationAndTraining = 1 << 16,
}
Loading

0 comments on commit 946e8ef

Please sign in to comment.