Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -148,12 +148,15 @@ public async Task RemoveRelatedProgressRecordsForGroupDelegate_updates_progress_
try
{
// Given
var removedDate = DateTime.Now;
const int delegateId = 245969;
var removedDate = DateTime.UtcNow;
const int groupId = 60;
const int delegateId = 298304;
const bool removeStartedEnrolments = true;
const int progressId = 282560;

// When
groupsDataService.RemoveRelatedProgressRecordsForGroupDelegate(5, delegateId, removedDate);
var progressFields = await connection.GetProgressRemovedFields(285146);
groupsDataService.RemoveRelatedProgressRecordsForGroupDelegate(groupId, delegateId, removedDate, removeStartedEnrolments);
var progressFields = await connection.GetProgressRemovedFields(progressId);

// Then
progressFields.Item1.Should().Be(3);
Expand All @@ -165,20 +168,48 @@ public async Task RemoveRelatedProgressRecordsForGroupDelegate_updates_progress_
}
}

[Test]
public async Task RemoveRelatedProgressRecordsForGroupDelegate_does_not_remove_started_progress_record_when_specified()
{
using var transaction = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled);
try
{
// Given
var removedDate = DateTime.UtcNow;
const int groupId = 60;
const int delegateId = 298304;
const bool removeStartedEnrolments = false;
const int progressId = 282560;

// When
groupsDataService.RemoveRelatedProgressRecordsForGroupDelegate(groupId, delegateId, removedDate, removeStartedEnrolments);
var progressFields = await connection.GetProgressRemovedFields(progressId);

// Then
progressFields.Item1.Should().Be(0);
progressFields.Item2.Should().BeNull();
}
finally
{
transaction.Dispose();
}
}

[Test]
public async Task RemoveRelatedProgressRecordsForGroupDelegate_does_not_update_progress_record_when_course_is_shared_by_another_group()
{
using var transaction = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled);
try
{
// Given
var removedDate = DateTime.Now;
var removedDate = DateTime.UtcNow;
const int delegateId = 299228;
const bool removeStartedEnrolments = false;
AddDelegateToGroupWithSharedCourse();
AddProgressRecordForGroupWithSharedCourse();

// When
groupsDataService.RemoveRelatedProgressRecordsForGroupDelegate(8, delegateId, removedDate);
groupsDataService.RemoveRelatedProgressRecordsForGroupDelegate(8, delegateId, removedDate, removeStartedEnrolments);
var progressFields = await connection.GetProgressRemovedFields(285172);

// Then
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ public void SynchroniseUserChangesWithGroups_removes_delegate_from_synchronised_
{
// Given
var centreAnswersData = UserTestHelper.GetDefaultCentreAnswersData(answer1: "new answer");
const bool removeStartedEnrolments = false;
A.CallTo(() => clockService.UtcNow).Returns(testDate);
var synchronisedGroup = GroupTestHelper.GetDefaultGroup(
5,
Expand Down Expand Up @@ -144,7 +145,8 @@ public void SynchroniseUserChangesWithGroups_removes_delegate_from_synchronised_
() => groupsDataService.RemoveRelatedProgressRecordsForGroupDelegate(
synchronisedGroup.GroupId,
reusableDelegateDetails.Id,
testDate
testDate,
removeStartedEnrolments
)
).MustHaveHappened();
}
Expand All @@ -155,6 +157,7 @@ public void
{
// Given
var centreAnswersData = UserTestHelper.GetDefaultCentreAnswersData(answer1: "new answer");
const bool removeStartedEnrolments = false;
A.CallTo(() => clockService.UtcNow).Returns(testDate);
A.CallTo(
() => centreCustomPromptsService.GetPromptNameForCentreAndPromptNumber(
Expand Down Expand Up @@ -190,7 +193,8 @@ public void
() => groupsDataService.RemoveRelatedProgressRecordsForGroupDelegate(
synchronisedGroup.GroupId,
reusableDelegateDetails.Id,
testDate
testDate,
removeStartedEnrolments
)
).MustHaveHappened();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ private void DelegateMustNotHaveBeenRemovedFromAGroup()
A.CallTo(() => groupsDataService.DeleteGroupDelegatesRecordForDelegate(A<int>._, A<int>._))
.MustNotHaveHappened();
A.CallTo(
() => groupsDataService.RemoveRelatedProgressRecordsForGroupDelegate(A<int>._, A<int>._, A<DateTime>._)
() => groupsDataService.RemoveRelatedProgressRecordsForGroupDelegate(A<int>._, A<int>._, A<DateTime>._, A<bool>._)
).MustNotHaveHappened();
}

Expand Down Expand Up @@ -170,7 +170,7 @@ private void DatabaseModificationsDoNothing()
{
A.CallTo(() => groupsDataService.DeleteGroupDelegatesRecordForDelegate(A<int>._, A<int>._)).DoesNothing();
A.CallTo(
() => groupsDataService.RemoveRelatedProgressRecordsForGroupDelegate(A<int>._, A<int>._, A<DateTime>._)
() => groupsDataService.RemoveRelatedProgressRecordsForGroupDelegate(A<int>._, A<int>._, A<DateTime>._, A<bool>._)
).DoesNothing();
A.CallTo(() => groupsDataService.AddDelegateToGroup(A<int>._, A<int>._, A<DateTime>._, A<int>._))
.DoesNothing();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public interface IGroupsDataService

string? GetGroupName(int groupId, int centreId);

void RemoveRelatedProgressRecordsForGroupDelegate(int groupId, int delegateId, DateTime removedDate);
void RemoveRelatedProgressRecordsForGroupDelegate(int groupId, int delegateId, DateTime removedDate, bool removeStartedEnrolments);

int? GetRelatedProgressIdForGroupDelegate(int groupId, int delegateId);

Expand Down Expand Up @@ -142,7 +142,7 @@ FROM Groups
).SingleOrDefault();
}

public void RemoveRelatedProgressRecordsForGroupDelegate(int groupId, int delegateId, DateTime removedDate)
public void RemoveRelatedProgressRecordsForGroupDelegate(int groupId, int delegateId, DateTime removedDate, bool removeStartedEnrolments)
{
const string numberOfGroupsWhereDelegateIsEnrolledOnThisCourse =
@"SELECT COUNT(DISTINCT(gd.GroupId))
Expand All @@ -165,9 +165,9 @@ WHERE p.Completed IS NULL
AND GC.GroupID = @groupId
AND p.CandidateID = @delegateId
AND P.RemovedDate IS NULL
AND p.LoginCount = 0)
AND (p.LoginCount = 0 OR @removeStartedEnrolments = 1))
AND ({numberOfGroupsWhereDelegateIsEnrolledOnThisCourse}) = 1",
new { groupId, delegateId, removedDate }
new { groupId, delegateId, removedDate, removeStartedEnrolments }
);
}

Expand Down
3 changes: 2 additions & 1 deletion DigitalLearningSolutions.Data/Services/GroupsService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,8 @@ private static bool ProgressShouldBeUpdatedOnEnrolment(Progress progress)

private void RemoveDelegateFromGroup(int delegateId, int groupId)
{
groupsDataService.RemoveRelatedProgressRecordsForGroupDelegate(groupId, delegateId, clockService.UtcNow);
const bool removeStartedEnrolments = false;
groupsDataService.RemoveRelatedProgressRecordsForGroupDelegate(groupId, delegateId, clockService.UtcNow, removeStartedEnrolments);
groupsDataService.DeleteGroupDelegatesRecordForDelegate(groupId, delegateId);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ public void GroupDelegatesRemove_should_return_not_found_with_delegate_not_in_gr
public void GroupDelegatesRemovePost_should_return_not_found_with_invalid_group_for_centre()
{
// Given
var model = new GroupDelegatesRemoveViewModel { ConfirmRemovalFromGroup = true, RemoveProgress = true };
var model = new GroupDelegatesRemoveViewModel { ConfirmRemovalFromGroup = true, RemoveStartedEnrolments = true };
A.CallTo(() => groupsDataService.GetGroupName(1, 2)).Returns(null);

// When
Expand All @@ -246,7 +246,7 @@ public void GroupDelegatesRemovePost_should_return_not_found_with_invalid_group_
public void GroupDelegatesRemovePost_should_return_not_found_with_delegate_not_in_group()
{
// Given
var model = new GroupDelegatesRemoveViewModel { ConfirmRemovalFromGroup = true, RemoveProgress = true };
var model = new GroupDelegatesRemoveViewModel { ConfirmRemovalFromGroup = true, RemoveStartedEnrolments = true };
A.CallTo(() => groupsDataService.GetGroupName(1, 2)).Returns("Group");
A.CallTo(() => groupsDataService.GetGroupDelegates(1)).Returns(new List<GroupDelegate>());

Expand Down Expand Up @@ -275,43 +275,50 @@ public void GroupDelegatesRemove_should_return_view_if_unconfirmed()
}

[Test]
public void GroupDelegatesRemove_should_not_call_remove_progress_if_unchecked()
public void GroupDelegatesRemove_should_call_remove_progress_but_keep_started_enrolments_if_unchecked()
{
// Given
var model = new GroupDelegatesRemoveViewModel { ConfirmRemovalFromGroup = true, RemoveProgress = false };
A.CallTo(() => groupsDataService.GetGroupName(1, 2)).Returns("Group");
A.CallTo(() => groupsDataService.GetGroupDelegates(1))
.Returns(new List<GroupDelegate> { new GroupDelegate { DelegateId = 2 } });
A.CallTo(() => groupsDataService.DeleteGroupDelegatesRecordForDelegate(1, 2)).DoesNothing();
var model = new GroupDelegatesRemoveViewModel { ConfirmRemovalFromGroup = true, RemoveStartedEnrolments = false };

const int groupId = 44;
const int delegateId = 3274;

A.CallTo(() => groupsDataService.GetGroupName(groupId, 2)).Returns("Group");
A.CallTo(() => groupsDataService.GetGroupDelegates(groupId))
.Returns(new List<GroupDelegate> { new GroupDelegate { DelegateId = delegateId } });
A.CallTo(() => groupsDataService.DeleteGroupDelegatesRecordForDelegate(groupId, delegateId)).DoesNothing();
A.CallTo(
() => groupsDataService.RemoveRelatedProgressRecordsForGroupDelegate(groupId, delegateId, A<DateTime>._, model.RemoveStartedEnrolments)
).DoesNothing();

// When
var result = delegateGroupsController.GroupDelegatesRemove(model, 1, 2);
var result = delegateGroupsController.GroupDelegatesRemove(model, groupId, delegateId);

// Then
A.CallTo(
() => groupsDataService.RemoveRelatedProgressRecordsForGroupDelegate(A<int>._, A<int>._, A<DateTime>._)
).MustNotHaveHappened();
A.CallTo(() => groupsDataService.DeleteGroupDelegatesRecordForDelegate(1, 2)).MustHaveHappened();
() => groupsDataService.RemoveRelatedProgressRecordsForGroupDelegate(groupId, delegateId, A<DateTime>._, model.RemoveStartedEnrolments)
).MustHaveHappened();
A.CallTo(() => groupsDataService.DeleteGroupDelegatesRecordForDelegate(groupId, delegateId)).MustHaveHappened();
result.Should().BeRedirectToActionResult().WithActionName("GroupDelegates");
}

[Test]
public void GroupDelegatesRemove_should_call_remove_progress_if_checked()
{
// Given
var model = new GroupDelegatesRemoveViewModel { ConfirmRemovalFromGroup = true, RemoveProgress = true };
var model = new GroupDelegatesRemoveViewModel { ConfirmRemovalFromGroup = true, RemoveStartedEnrolments = true };
A.CallTo(() => groupsDataService.GetGroupName(1, 2)).Returns("Group");
A.CallTo(() => groupsDataService.GetGroupDelegates(1))
.Returns(new List<GroupDelegate> { new GroupDelegate { DelegateId = 2 } });
A.CallTo(() => groupsDataService.DeleteGroupDelegatesRecordForDelegate(1, 2)).DoesNothing();
A.CallTo(() => groupsDataService.RemoveRelatedProgressRecordsForGroupDelegate(1, 2, A<DateTime>._))
A.CallTo(() => groupsDataService.RemoveRelatedProgressRecordsForGroupDelegate(1, 2, A<DateTime>._, A<bool>._))
.DoesNothing();

// When
var result = delegateGroupsController.GroupDelegatesRemove(model, 1, 2);

// Then
A.CallTo(() => groupsDataService.RemoveRelatedProgressRecordsForGroupDelegate(1, 2, A<DateTime>._))
A.CallTo(() => groupsDataService.RemoveRelatedProgressRecordsForGroupDelegate(1, 2, A<DateTime>._, A<bool>._))
.MustHaveHappened();
A.CallTo(() => groupsDataService.DeleteGroupDelegatesRecordForDelegate(1, 2)).MustHaveHappened();
result.Should().BeRedirectToActionResult().WithActionName("GroupDelegates");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,11 +149,9 @@ public IActionResult GroupDelegatesRemove(GroupDelegatesRemoveViewModel model, i
}

using var transaction = new TransactionScope();
if (model.RemoveProgress)
{
var currentDate = clockService.UtcNow;
groupsDataService.RemoveRelatedProgressRecordsForGroupDelegate(groupId, delegateId, currentDate);
}

var currentDate = clockService.UtcNow;
groupsDataService.RemoveRelatedProgressRecordsForGroupDelegate(groupId, delegateId, currentDate, model.RemoveStartedEnrolments);

groupsDataService.DeleteGroupDelegatesRecordForDelegate(groupId, delegateId);
transaction.Complete();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public GroupDelegatesRemoveViewModel(GroupDelegate delegateUser, string groupNam
GroupId = groupId;
GroupName = groupName;
DelegateName = DisplayStringHelper.GetDelegateNameString(delegateUser.FirstName, delegateUser.LastName);
RemoveProgressEnabled = progressId.HasValue;
RemoveStartedEnrolmentsEnabled = progressId.HasValue;
}

public int GroupId { get; set; }
Expand All @@ -23,8 +23,8 @@ public GroupDelegatesRemoveViewModel(GroupDelegate delegateUser, string groupNam

public bool ConfirmRemovalFromGroup { get; set; }

public bool RemoveProgress { get; set; }
public bool RemoveStartedEnrolments { get; set; }

public bool RemoveProgressEnabled { get; set; }
public bool RemoveStartedEnrolmentsEnabled { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,21 @@

<div class="nhsuk-grid-row">
<div class="nhsuk-grid-column-full">
@if (errorHasOccurred) {
@if (errorHasOccurred)
{
<vc:error-summary order-of-property-names="@new []{ nameof(Model.ConfirmRemovalFromGroup) }" />
}

<h1 class="nhsuk-heading-xl">Are you sure you would like to remove @Model.DelegateName from this group?</h1>
</div>
</div>

<div class="nhsuk-grid-row">
<div class="nhsuk-grid-column-full nhsuk-lede-text">
All enrolments on courses that have not yet been started and are associated with the delegate’s membership of this group will be removed.
</div>
</div>

<div class="nhsuk-grid-row">
<div class="nhsuk-grid-column-one-quarter nhsuk-heading-l nhsuk-u-font-weight-bold">
Group name:
Expand All @@ -41,7 +48,8 @@
<form class="nhsuk-u-margin-bottom-5" method="post" asp-action="GroupDelegatesRemove">
<div class="nhsuk-form-group @confirmFormErrorClass">
<fieldset class="nhsuk-fieldset" aria-describedby="@(nameof(Model.ConfirmRemovalFromGroup))-error">
@if (confirmError) {
@if (confirmError)
{
<span class="nhsuk-error-message" id="@(nameof(Model.ConfirmRemovalFromGroup))-error">
<span class="nhsuk-u-visually-hidden">Error:</span> @ViewData.ModelState[nameof(Model.ConfirmRemovalFromGroup)].Errors[0].ErrorMessage
</span>
Expand All @@ -57,18 +65,18 @@
</fieldset>
</div>

@if (Model.RemoveProgressEnabled) {
@if (Model.RemoveStartedEnrolmentsEnabled)
{
<div class="nhsuk-form-group">
<fieldset class="nhsuk-fieldset" aria-describedby="@(nameof(Model.RemoveProgress))-error">
<fieldset class="nhsuk-fieldset" aria-describedby="@(nameof(Model.RemoveStartedEnrolments))-error">
<div class="nhsuk-checkboxes">
<div class="nhsuk-checkboxes__item">
<input class="nhsuk-checkboxes__input" asp-for="RemoveProgress" aria-describedby="@(nameof(Model.RemoveProgress))-item-hint" />
<label class="nhsuk-label nhsuk-checkboxes__label" asp-for="RemoveProgress">
Remove delegate from all the related enrolments where course is not yet complete.
<input class="nhsuk-checkboxes__input" asp-for="RemoveStartedEnrolments" aria-describedby="@(nameof(Model.RemoveStartedEnrolments))-item-hint" />
<label class="nhsuk-label nhsuk-checkboxes__label" asp-for="RemoveStartedEnrolments">
Remove delegate from all the related enrolments where course has been started but is not yet complete
</label>
<div class="nhsuk-hint nhsuk-checkboxes__hint" id="@(nameof(Model.RemoveProgress))-item-hint">
Optionally all enrolments on courses that are incomplete and are associated with the delegate's membership of this group can also be removed.
This facility should not be used if the delegate may have been enrolled on the same course(s) because of membership of another group.
<div class="nhsuk-hint nhsuk-checkboxes__hint" id="@(nameof(Model.RemoveStartedEnrolments))-item-hint">
Optionally all enrolments on courses that have been started, but are incomplete and are associated with the delegate’s membership of this group can also be removed.
</div>
</div>
</div>
Expand Down