Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change induction status page #1755

Merged
merged 16 commits into from
Dec 27, 2024
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,13 @@ public static bool ValidateInductionData(
return true;
}

public bool InductionStatusManagedByCpd(DateOnly now)
{
var sevenYearsAgo = now.AddYears(-7);
return InductionCompletedDate is not null
&& InductionCompletedDate < sevenYearsAgo;
}

private static void AssertInductionChangeIsValid(
InductionStatus status,
DateOnly? startDate,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ public static class InductionStatusRegistry

public static IReadOnlyCollection<InductionStatusInfo> All => _info.Values.ToArray();

public static IReadOnlyCollection<InductionStatusInfo> ValidStatusChangesWhenManagedByCpd =>
_info
.Where(s => s.Key is InductionStatus.RequiredToComplete or InductionStatus.Exempt or InductionStatus.FailedInWales)
.Select(s => s.Value)
.ToArray();

public static string GetName(this InductionStatus status) => _info[status].Name;

public static string GetTitle(this InductionStatus status) => _info[status].Title;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@ public class EditInductionState : IRegisterJourney
appendUniqueKey: true);

public InductionStatus InductionStatus { get; set; }
public InductionStatus CurrentInductionStatus { get; set; }
public DateOnly? StartDate { get; set; }
public DateOnly? CompletedDate { get; set; }
public InductionExemptionReasons? ExemptionReasons { get; set; }
public string? ChangeReason { get; set; }
public InductionJourneyPage? JourneyStartPage { get; set; }
public bool RecordManagedInCpd { get; set; }

public bool Initialized { get; set; }

Expand All @@ -27,7 +29,8 @@ public async Task EnsureInitializedAsync(TrsDbContext dbContext, Guid personId,
}
var person = await dbContext.Persons
.SingleAsync(q => q.PersonId == personId);
InductionStatus = person!.InductionStatus;

CurrentInductionStatus = person!.InductionStatus;
if (JourneyStartPage == null)
{
JourneyStartPage = startPage;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,31 @@
@page "/persons/{PersonId}/edit-induction/status"
@model TeachingRecordSystem.SupportUi.Pages.Persons.PersonDetail.EditInduction.StatusModel
@{
ViewBag.Title = "Edit status: " + Model.InductionStatus.GetName();
ViewBag.Title = "What is their induction status?";
}

@section BeforeContent {
<govuk-back-link data-testid="back-link" href="@Model.BackLink">Back</govuk-back-link>
}

<h1 data-testid="page-title" class="govuk-heading-l">@ViewBag.Title</h1>

@if (!String.IsNullOrEmpty(Model.StatusWarningMessage))
{
<govuk-warning-text icon-fallback-text="Warning" data-testid="induction-status-warning">@Model.StatusWarningMessage</govuk-warning-text>
}
<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds-from-desktop">
<span data-testid="induction-status-caption" class="govuk-caption-l">Induction - @Model.PersonName</span>
<form data-testid="submit-form" action="@LinkGenerator.InductionEditStatus(Model.PersonId, Model.JourneyInstance!.InstanceId)" method="post">
<govuk-radios asp-for="InductionStatus">
<govuk-radios-fieldset>
<govuk-radios-fieldset-legend data-testid="status-choices-legend" class="govuk-fieldset__legend--m" />
@foreach (var inductionStatus in Model.StatusChoices)
{
<govuk-radios-item value="@inductionStatus.Value" checked="@Model.InductionStatus==inductionStatus.Value">@inductionStatus.Title</govuk-radios-item>
}
</govuk-radios-fieldset>
</govuk-radios>
<input type="hidden" name="InductionStatus" value="@Model.InductionStatus" />
<div class="govuk-button-group">
<govuk-button type="submit" data-testid="continue-button">Continue</govuk-button>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,49 @@
using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using TeachingRecordSystem.Core.DataStore.Postgres;
using TeachingRecordSystem.SupportUi.ValidationAttributes;

namespace TeachingRecordSystem.SupportUi.Pages.Persons.PersonDetail.EditInduction;

[Journey(JourneyNames.EditInduction), ActivatesJourney, RequireJourneyInstance]
public class StatusModel : CommonJourneyPage
{
private const string InductionIsManagedByCpdWarning = "To change this teacher’s induction status to passed, failed, or in progress, use the Record inductions as an appropriate body service.";

protected TrsDbContext _dbContext;
protected IClock _clock;
protected bool InductionStatusManagedByCpd;

[BindProperty]
[Display(Name = "Select a status")]
[NotEqual(InductionStatus.None, ErrorMessage = "Select a status")]
public InductionStatus InductionStatus { get; set; }
public InductionStatus CurrentInductionStatus { get; set; }
public string? PersonName { get; set; }
public IEnumerable<InductionStatusInfo> StatusChoices
{
get
{
return InductionStatusManagedByCpd ?
InductionStatusRegistry.ValidStatusChangesWhenManagedByCpd.Where(i => i.Value != CurrentInductionStatus)
: InductionStatusRegistry.All.ToArray()[1..].Where(i => i.Value != CurrentInductionStatus);
}
}
public string? StatusWarningMessage
{
get
{
if (InductionStatusManagedByCpd)
{
return InductionIsManagedByCpdWarning;
}
else
{
return null;
}
}
}

public InductionJourneyPage NextPage
{
Expand All @@ -24,20 +57,39 @@ _ when InductionStatus.RequiresStartDate() => InductionJourneyPage.StartDate,
};
}
}

public string BackLink => LinkGenerator.PersonInduction(PersonId);

public StatusModel(TrsLinkGenerator linkGenerator, TrsDbContext dbContext) : base(linkGenerator)
public StatusModel(TrsLinkGenerator linkGenerator, TrsDbContext dbContext, IClock clock) : base(linkGenerator)
{
_dbContext = dbContext;
_clock = clock;
}

public void OnGet()
public async Task OnGetAsync()
{
var person = await _dbContext.Persons.SingleAsync(q => q.PersonId == PersonId);
InductionStatusManagedByCpd = person.InductionStatusManagedByCpd(_clock.Today);
InductionStatus = JourneyInstance!.State.InductionStatus;
CurrentInductionStatus = JourneyInstance!.State.CurrentInductionStatus;
await JourneyInstance!.UpdateStateAsync(state =>
{
if (state.CurrentInductionStatus == InductionStatus.None)
{
state.CurrentInductionStatus = CurrentInductionStatus;
}
});
}

public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
var person = await _dbContext.Persons.SingleAsync(q => q.PersonId == PersonId);
InductionStatusManagedByCpd = person.InductionStatusManagedByCpd(_clock.Today);
CurrentInductionStatus = JourneyInstance!.State.CurrentInductionStatus;
return this.PageWithErrors();
}
await JourneyInstance!.UpdateStateAsync(state =>
{
state.InductionStatus = InductionStatus;
Expand All @@ -54,6 +106,9 @@ public override async Task OnPageHandlerExecutionAsync(PageHandlerExecutingConte
{
await JourneyInstance!.State.EnsureInitializedAsync(_dbContext, PersonId, InductionJourneyPage.Status);

var personInfo = context.HttpContext.GetCurrentPersonFeature();
PersonId = personInfo.PersonId;
PersonName = personInfo.Name;
await next();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace TeachingRecordSystem.SupportUi.Pages.Persons.PersonDetail;
public class InductionModel(TrsDbContext dbContext, ICrmQueryDispatcher crmQueryDispatcher, IClock clock) : PageModel
{
private const string NoQualifiedTeacherStatusWarning = "This teacher has not been awarded QTS and is therefore ineligible for induction.";
private const string InductionIsManagedByCpdWarning = "To change a teacher\u2019s induction status to passed, failed, or in progress, use the Record inductions as an appropriate body service.";
private const string InductionIsManagedByCpdWarning = "To change this teacher’s induction status to passed, failed, or in progress, use the Record inductions as an appropriate body service.";
private bool _statusIsManagedByCpd;
private bool _teacherHoldsQualifiedTeacherStatus;

Expand Down Expand Up @@ -68,20 +68,12 @@ public async Task OnGetAsync()
StartDate = person!.InductionStartDate;
CompletionDate = person!.InductionCompletedDate;
ExemptionReasons = person!.InductionExemptionReasons;
_statusIsManagedByCpd = StatusManagedByCpdRule(person!.CpdInductionStatus, person.CpdInductionCompletedDate);
_statusIsManagedByCpd = person.InductionStatusManagedByCpd(clock.Today);
_teacherHoldsQualifiedTeacherStatus = TeacherHoldsQualifiedTeacherStatusRule(result?.Contact.dfeta_QTSDate);
}

private bool TeacherHoldsQualifiedTeacherStatusRule(DateTime? qtsDate)
{
return qtsDate is null;
}

private bool StatusManagedByCpdRule(InductionStatus? status, DateOnly? inductionCompletedDate)
{
var sevenYearsAgo = clock.Today.AddYears(-7);
return status is not null
&& inductionCompletedDate is not null
&& inductionCompletedDate < sevenYearsAgo;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
namespace TeachingRecordSystem.SupportUi.ValidationAttributes;

using System.ComponentModel.DataAnnotations;

public class NotEqualAttribute : ValidationAttribute
{
private readonly object _notAllowedValue;

public NotEqualAttribute(object notAllowedValue)
{
_notAllowedValue = notAllowedValue;
}

protected override ValidationResult? IsValid(object? value, ValidationContext validationContext)
{
if (value != null && value.Equals(_notAllowedValue))
{
return new ValidationResult(ErrorMessage ?? $"The value cannot be {_notAllowedValue}.");
}

return ValidationResult.Success;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -228,4 +228,32 @@ public void TrySetWelshInductionStatus_StatusIsAtLowerPriorityStatus_UpdatesStat
Assert.Equal(expectedStatus, person.InductionStatus);
Assert.Equal(expectedExemptionReasons, person.InductionExemptionReasons);
}

[Theory]
[InlineData(-3, false)]
[InlineData(-7, true)]
public void InductionManagedByCpd_ReturnsTrue(int yearsSinceCompleted, bool expected)
{
// Arrange
var dateTimeCompleted = Clock.UtcNow.AddYears(yearsSinceCompleted).AddDays(-1);
var dateCompleted = Clock.Today.AddYears(yearsSinceCompleted).AddDays(-1);
var person = new Person
{
PersonId = Guid.NewGuid(),
CreatedOn = dateTimeCompleted,
UpdatedOn = dateTimeCompleted,
Trn = "1234567",
FirstName = "Joe",
MiddleName = "",
LastName = "Bloggs",
DateOfBirth = new(1990, 1, 1),
};
person.SetInductionStatus(InductionStatus.Passed, dateCompleted, dateCompleted, InductionExemptionReasons.None, SystemUser.SystemUserId, Clock.UtcNow, out _);

// Act
var result = person.InductionStatusManagedByCpd(Clock.Today);

// Assert
Assert.Equal(expected, result);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
using TeachingRecordSystem.SupportUi.Pages.Persons.PersonDetail.EditInduction;

namespace TeachingRecordSystem.SupportUi.Tests.PageTests.Persons.PersonDetail.EditInduction;

public class EditInductionStateBuilder
{
private InductionStatus InductionStatus { get; set; }
private InductionStatus CurrentInductionStatus { get; set; }
private DateOnly? StartDate { get; set; }
private DateOnly? CompletedDate { get; set; }
private InductionExemptionReasons? ExemptionReasons { get; set; }
private string? ChangeReason { get; set; }
private InductionJourneyPage? JourneyStartPage { get; set; }
private bool RecordManagedInCpd { get; set; }
private bool Initialized { get; set; }

public EditInductionStateBuilder WithInitialisedState(InductionStatus? currentInductionStatus, InductionJourneyPage startPage)
{
this.Initialized = true;
JourneyStartPage = startPage;
CurrentInductionStatus = currentInductionStatus ?? InductionStatus.None;
return this;
}

public EditInductionStateBuilder WithUpdatedState(InductionStatus inductionStatus)
{
if (CurrentInductionStatus == InductionStatus.None)
{
throw new NotSupportedException("Initialised state must be set using WithInitialisedState");
}
InductionStatus = inductionStatus;
return this;
}

public EditInductionState Create()
{
return new EditInductionState()
{
InductionStatus = InductionStatus,
CurrentInductionStatus = CurrentInductionStatus,
StartDate = StartDate,
CompletedDate = CompletedDate,
ExemptionReasons = ExemptionReasons,
ChangeReason = ChangeReason,
JourneyStartPage = JourneyStartPage,
RecordManagedInCpd = RecordManagedInCpd,
Initialized = Initialized
};
}
}
Loading
Loading