diff --git a/DigitalLearningSolutions.Data.Tests/DataServices/GroupsDataServiceTests.cs b/DigitalLearningSolutions.Data.Tests/DataServices/GroupsDataServiceTests.cs index f103f3e693..8aeddae6ba 100644 --- a/DigitalLearningSolutions.Data.Tests/DataServices/GroupsDataServiceTests.cs +++ b/DigitalLearningSolutions.Data.Tests/DataServices/GroupsDataServiceTests.cs @@ -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); @@ -165,6 +168,33 @@ 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() { @@ -172,13 +202,14 @@ public async Task RemoveRelatedProgressRecordsForGroupDelegate_does_not_update_p 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 diff --git a/DigitalLearningSolutions.Data.Tests/Services/GroupServiceTests/GroupsServiceSynchroniseGroupsTests.cs b/DigitalLearningSolutions.Data.Tests/Services/GroupServiceTests/GroupsServiceSynchroniseGroupsTests.cs index 8817323004..04877fe3af 100644 --- a/DigitalLearningSolutions.Data.Tests/Services/GroupServiceTests/GroupsServiceSynchroniseGroupsTests.cs +++ b/DigitalLearningSolutions.Data.Tests/Services/GroupServiceTests/GroupsServiceSynchroniseGroupsTests.cs @@ -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, @@ -144,7 +145,8 @@ public void SynchroniseUserChangesWithGroups_removes_delegate_from_synchronised_ () => groupsDataService.RemoveRelatedProgressRecordsForGroupDelegate( synchronisedGroup.GroupId, reusableDelegateDetails.Id, - testDate + testDate, + removeStartedEnrolments ) ).MustHaveHappened(); } @@ -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( @@ -190,7 +193,8 @@ public void () => groupsDataService.RemoveRelatedProgressRecordsForGroupDelegate( synchronisedGroup.GroupId, reusableDelegateDetails.Id, - testDate + testDate, + removeStartedEnrolments ) ).MustHaveHappened(); } diff --git a/DigitalLearningSolutions.Data.Tests/Services/GroupServiceTests/GroupsServiceTests.cs b/DigitalLearningSolutions.Data.Tests/Services/GroupServiceTests/GroupsServiceTests.cs index 7dbd5d7560..75bb87979e 100644 --- a/DigitalLearningSolutions.Data.Tests/Services/GroupServiceTests/GroupsServiceTests.cs +++ b/DigitalLearningSolutions.Data.Tests/Services/GroupServiceTests/GroupsServiceTests.cs @@ -124,7 +124,7 @@ private void DelegateMustNotHaveBeenRemovedFromAGroup() A.CallTo(() => groupsDataService.DeleteGroupDelegatesRecordForDelegate(A._, A._)) .MustNotHaveHappened(); A.CallTo( - () => groupsDataService.RemoveRelatedProgressRecordsForGroupDelegate(A._, A._, A._) + () => groupsDataService.RemoveRelatedProgressRecordsForGroupDelegate(A._, A._, A._, A._) ).MustNotHaveHappened(); } @@ -170,7 +170,7 @@ private void DatabaseModificationsDoNothing() { A.CallTo(() => groupsDataService.DeleteGroupDelegatesRecordForDelegate(A._, A._)).DoesNothing(); A.CallTo( - () => groupsDataService.RemoveRelatedProgressRecordsForGroupDelegate(A._, A._, A._) + () => groupsDataService.RemoveRelatedProgressRecordsForGroupDelegate(A._, A._, A._, A._) ).DoesNothing(); A.CallTo(() => groupsDataService.AddDelegateToGroup(A._, A._, A._, A._)) .DoesNothing(); diff --git a/DigitalLearningSolutions.Data/DataServices/GroupsDataService.cs b/DigitalLearningSolutions.Data/DataServices/GroupsDataService.cs index 88bf521bdd..f141921d27 100644 --- a/DigitalLearningSolutions.Data/DataServices/GroupsDataService.cs +++ b/DigitalLearningSolutions.Data/DataServices/GroupsDataService.cs @@ -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); @@ -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)) @@ -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 } ); } diff --git a/DigitalLearningSolutions.Data/Services/GroupsService.cs b/DigitalLearningSolutions.Data/Services/GroupsService.cs index fedb69aec6..8191c83267 100644 --- a/DigitalLearningSolutions.Data/Services/GroupsService.cs +++ b/DigitalLearningSolutions.Data/Services/GroupsService.cs @@ -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); } diff --git a/DigitalLearningSolutions.Web.Tests/Controllers/TrackingSystem/Delegates/DelegateGroupsControllerTests.cs b/DigitalLearningSolutions.Web.Tests/Controllers/TrackingSystem/Delegates/DelegateGroupsControllerTests.cs index c19eb7de5b..354c6afd3c 100644 --- a/DigitalLearningSolutions.Web.Tests/Controllers/TrackingSystem/Delegates/DelegateGroupsControllerTests.cs +++ b/DigitalLearningSolutions.Web.Tests/Controllers/TrackingSystem/Delegates/DelegateGroupsControllerTests.cs @@ -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 @@ -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()); @@ -275,23 +275,30 @@ 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 { 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 { new GroupDelegate { DelegateId = delegateId } }); + A.CallTo(() => groupsDataService.DeleteGroupDelegatesRecordForDelegate(groupId, delegateId)).DoesNothing(); + A.CallTo( + () => groupsDataService.RemoveRelatedProgressRecordsForGroupDelegate(groupId, delegateId, A._, model.RemoveStartedEnrolments) + ).DoesNothing(); // When - var result = delegateGroupsController.GroupDelegatesRemove(model, 1, 2); + var result = delegateGroupsController.GroupDelegatesRemove(model, groupId, delegateId); // Then A.CallTo( - () => groupsDataService.RemoveRelatedProgressRecordsForGroupDelegate(A._, A._, A._) - ).MustNotHaveHappened(); - A.CallTo(() => groupsDataService.DeleteGroupDelegatesRecordForDelegate(1, 2)).MustHaveHappened(); + () => groupsDataService.RemoveRelatedProgressRecordsForGroupDelegate(groupId, delegateId, A._, model.RemoveStartedEnrolments) + ).MustHaveHappened(); + A.CallTo(() => groupsDataService.DeleteGroupDelegatesRecordForDelegate(groupId, delegateId)).MustHaveHappened(); result.Should().BeRedirectToActionResult().WithActionName("GroupDelegates"); } @@ -299,19 +306,19 @@ public void GroupDelegatesRemove_should_not_call_remove_progress_if_unchecked() 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 { new GroupDelegate { DelegateId = 2 } }); A.CallTo(() => groupsDataService.DeleteGroupDelegatesRecordForDelegate(1, 2)).DoesNothing(); - A.CallTo(() => groupsDataService.RemoveRelatedProgressRecordsForGroupDelegate(1, 2, A._)) + A.CallTo(() => groupsDataService.RemoveRelatedProgressRecordsForGroupDelegate(1, 2, A._, A._)) .DoesNothing(); // When var result = delegateGroupsController.GroupDelegatesRemove(model, 1, 2); // Then - A.CallTo(() => groupsDataService.RemoveRelatedProgressRecordsForGroupDelegate(1, 2, A._)) + A.CallTo(() => groupsDataService.RemoveRelatedProgressRecordsForGroupDelegate(1, 2, A._, A._)) .MustHaveHappened(); A.CallTo(() => groupsDataService.DeleteGroupDelegatesRecordForDelegate(1, 2)).MustHaveHappened(); result.Should().BeRedirectToActionResult().WithActionName("GroupDelegates"); diff --git a/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Delegates/DelegateGroupsController.cs b/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Delegates/DelegateGroupsController.cs index 6c2a29c5b3..98a5062078 100644 --- a/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Delegates/DelegateGroupsController.cs +++ b/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Delegates/DelegateGroupsController.cs @@ -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(); diff --git a/DigitalLearningSolutions.Web/ViewModels/TrackingSystem/Delegates/DelegateGroups/GroupDelegatesRemoveViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/TrackingSystem/Delegates/DelegateGroups/GroupDelegatesRemoveViewModel.cs index d350513e2b..174145bb6e 100644 --- a/DigitalLearningSolutions.Web/ViewModels/TrackingSystem/Delegates/DelegateGroups/GroupDelegatesRemoveViewModel.cs +++ b/DigitalLearningSolutions.Web/ViewModels/TrackingSystem/Delegates/DelegateGroups/GroupDelegatesRemoveViewModel.cs @@ -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; } @@ -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; } } } diff --git a/DigitalLearningSolutions.Web/Views/TrackingSystem/Delegates/DelegateGroups/GroupDelegatesRemove.cshtml b/DigitalLearningSolutions.Web/Views/TrackingSystem/Delegates/DelegateGroups/GroupDelegatesRemove.cshtml index 20be00bbb6..0a024a0bd7 100644 --- a/DigitalLearningSolutions.Web/Views/TrackingSystem/Delegates/DelegateGroups/GroupDelegatesRemove.cshtml +++ b/DigitalLearningSolutions.Web/Views/TrackingSystem/Delegates/DelegateGroups/GroupDelegatesRemove.cshtml @@ -19,7 +19,8 @@
- @if (errorHasOccurred) { + @if (errorHasOccurred) + { } @@ -27,6 +28,12 @@
+
+
+ All enrolments on courses that have not yet been started and are associated with the delegate’s membership of this group will be removed. +
+
+
Group name: @@ -41,7 +48,8 @@
- @if (confirmError) { + @if (confirmError) + { Error: @ViewData.ModelState[nameof(Model.ConfirmRemovalFromGroup)].Errors[0].ErrorMessage @@ -57,18 +65,18 @@
- @if (Model.RemoveProgressEnabled) { + @if (Model.RemoveStartedEnrolmentsEnabled) + {
-
+
- -