diff --git a/.terraformignore b/.terraformignore index ea97880c..2cee3633 100644 --- a/.terraformignore +++ b/.terraformignore @@ -1,12 +1,14 @@ **/.terraform/ -*.tfstate -*.tfstate.* -*.tfvars -.terraform.lock.hcl **/.terraform.lock.hcl -crash.log -crash.*.log -override.tf -override.tf.json -*_override.tf -*_override.tf.json +**/*.tfstate +**/*.tfstate.* +**/*.tfvars +**/*.tfvars.json +**/crash.log +**/crash.*.log +**/override.tf +**/override.tf.json +**/*_override.tf +**/*_override.tf.json +**/.terraformrc +**/terraform.rc \ No newline at end of file diff --git a/docs/request-journey.md b/docs/request-journey.md index 01ceff4e..f33c0e14 100644 --- a/docs/request-journey.md +++ b/docs/request-journey.md @@ -293,7 +293,7 @@ The summary page renders a GOV.UK summary list of all answers. It can only be re - **Remove / Include** — a single "Pupil name" row shows `SelectedPupil` with a Change link to the primary `PupilSearch` page. - **Merge** — two rows replace the single "Pupil name" row: - **"First record to merge"** — `"{Firstname} {Surname}, {d MMMM yyyy}"` (e.g. `"Jane Smith, 27 July 2010"`) with a Change link to the primary `PupilSearch` page. - - **"Second record to merge"** — `"{Cypmd_Id}, {Firstname} {Surname}"` (e.g. `"CYPMD456, John Doe"`) with a Change link to the match `PupilSearch` page. + - **"Second record to merge"** — `"{Firstname} {Surname} {d MMMM yyyy} ({Cypmd_Id})"` (e.g. `"John Doe 2 February 2010 (CYPMD456)"`) with a Change link to the match `PupilSearch` page. If the DOB cannot be parsed the raw stored value is shown; if it is missing entirely the DOB segment is omitted (`"{name} ({id})"`). Change links for `PupilSearch` pages use the `PupilSearchPage` action rather than the `Page` action. The back link on the summary page also uses `PupilSearchPage` when the last page in `QuestionHistory` is a `PupilSearch` page. diff --git a/src/DfE.CheckPerformanceData.Application/AmendmentRequests/SubmittedRequestService.cs b/src/DfE.CheckPerformanceData.Application/AmendmentRequests/SubmittedRequestService.cs index 4fb2e4f2..fed37a9d 100644 --- a/src/DfE.CheckPerformanceData.Application/AmendmentRequests/SubmittedRequestService.cs +++ b/src/DfE.CheckPerformanceData.Application/AmendmentRequests/SubmittedRequestService.cs @@ -1,4 +1,3 @@ -using System.Globalization; using DfE.CheckPerformanceData.Application.CurrentUser; using DfE.CheckPerformanceData.Application.Journey; using DfE.CheckPerformanceData.Application.RequestSubmission; @@ -120,12 +119,6 @@ private static (string? first, string? second) BuildMergeDisplays(RequestState j if (journey.MatchedPupil is not { } mp || journey.SelectedPupil is not { } sp) return (null, null); - var dob = DateTime.TryParseExact(sp.DateOfBirth, "dd/MM/yyyy", - CultureInfo.InvariantCulture, DateTimeStyles.None, out var parsed) - ? parsed.ToString("d MMMM yyyy") - : sp.DateOfBirth; - var first = $"{sp.Firstname} {sp.Surname}, {dob}".Trim(); - var second = $"{mp.Cypmd_Id}, {mp.Firstname} {mp.Surname}".Trim(); - return (first, second); + return (MergeRecordDisplays.First(sp), MergeRecordDisplays.Second(mp)); } } diff --git a/src/DfE.CheckPerformanceData.Application/Journey/MergeRecordDisplays.cs b/src/DfE.CheckPerformanceData.Application/Journey/MergeRecordDisplays.cs new file mode 100644 index 00000000..ef73ab5d --- /dev/null +++ b/src/DfE.CheckPerformanceData.Application/Journey/MergeRecordDisplays.cs @@ -0,0 +1,37 @@ +using System.Globalization; +using DfE.CheckPerformanceData.Application.CheckYourPupilData; + +namespace DfE.CheckPerformanceData.Application.Journey; + +/// +/// Single source of truth for the two record displays shown when merging duplicate +/// pupils. Both the in-journey summary and the submitted-request view delegate here so +/// the format can never drift between surfaces. +/// +public static class MergeRecordDisplays +{ + public static string First(PupilDto pupil) + { + var dob = FormatDob(pupil.DateOfBirth); + return $"{pupil.Firstname} {pupil.Surname}, {dob}".Trim(); + } + + public static string Second(PupilDto pupil) + { + var dob = FormatDob(pupil.DateOfBirth); + var name = $"{pupil.Firstname} {pupil.Surname}".Trim(); + var display = dob.Length > 0 ? $"{name} {dob} ({pupil.Cypmd_Id})" : $"{name} ({pupil.Cypmd_Id})"; + return display.Trim(); + } + + private static string FormatDob(string raw) + { + if (string.IsNullOrEmpty(raw)) + return string.Empty; + + return DateTime.TryParseExact(raw, "dd/MM/yyyy", + CultureInfo.InvariantCulture, DateTimeStyles.None, out var d) + ? d.ToString("d MMMM yyyy", CultureInfo.InvariantCulture) + : raw; + } +} \ No newline at end of file diff --git a/src/DfE.CheckPerformanceData.Web/Controllers/Journey/JourneyViewModelBuilder.cs b/src/DfE.CheckPerformanceData.Web/Controllers/Journey/JourneyViewModelBuilder.cs index e71a5dbc..9ba34b42 100644 --- a/src/DfE.CheckPerformanceData.Web/Controllers/Journey/JourneyViewModelBuilder.cs +++ b/src/DfE.CheckPerformanceData.Web/Controllers/Journey/JourneyViewModelBuilder.cs @@ -50,13 +50,8 @@ public SummaryViewModel BuildSummaryVm( string? secondRecordDisplay = null; if (journey.MatchedPupil is { } mp && journey.SelectedPupil is { } sp) { - var dob = DateTime.TryParseExact(sp.DateOfBirth, "dd/MM/yyyy", - System.Globalization.CultureInfo.InvariantCulture, - System.Globalization.DateTimeStyles.None, out var d) - ? d.ToString("d MMMM yyyy") - : sp.DateOfBirth; - firstRecordDisplay = $"{sp.Firstname} {sp.Surname}, {dob}".Trim(); - secondRecordDisplay = $"{mp.Cypmd_Id}, {mp.Firstname} {mp.Surname}".Trim(); + firstRecordDisplay = MergeRecordDisplays.First(sp); + secondRecordDisplay = MergeRecordDisplays.Second(mp); } return new SummaryViewModel diff --git a/tests/DfE.CheckPerformanceData.UnitTests/AmendmentRequests/SubmittedRequestServiceTests.cs b/tests/DfE.CheckPerformanceData.UnitTests/AmendmentRequests/SubmittedRequestServiceTests.cs index 671dad72..9dbe81c1 100644 --- a/tests/DfE.CheckPerformanceData.UnitTests/AmendmentRequests/SubmittedRequestServiceTests.cs +++ b/tests/DfE.CheckPerformanceData.UnitTests/AmendmentRequests/SubmittedRequestServiceTests.cs @@ -254,6 +254,35 @@ public async Task GetConfirmDataCorrectAsync_WhenConfirmCorrect_MapsView() Assert.Equal(Reference, result.ReferenceNumber); } + [Fact] + public async Task GetAsync_MergeJourney_PopulatesRecordDisplays() + { + var journey = Journey(); + journey.MatchedPupil = new PupilDto + { + Id = Guid.NewGuid(), + Firstname = "John", + Surname = "Doe", + Sex = "M", + DateOfBirth = "02/02/2010", + Age = 16, + Cypmd_Id = "CYPMD456", + Identifier = "456456" + }; + var page = new JourneyPage + { + Id = "reason", + Questions = [new Question { Id = "q1", Type = QuestionType.Radio, Title = "Why?" }] + }; + Setup(journey, page); + + var result = await _sut.GetAsync(WindowId, Reference); + + Assert.NotNull(result); + Assert.Equal("Jane Smith, 1 January 2010", result!.FirstRecordDisplay); + Assert.Equal("John Doe 2 February 2010 (CYPMD456)", result.SecondRecordDisplay); + } + private void Setup(RequestState journey, JourneyPage page) { _blob.GetAsync(WindowId, Reference).Returns(journey); diff --git a/tests/DfE.CheckPerformanceData.UnitTests/Journey/JourneyViewModelBuilderTests.cs b/tests/DfE.CheckPerformanceData.UnitTests/Journey/JourneyViewModelBuilderTests.cs index a7e7f136..7ca8b2e7 100644 --- a/tests/DfE.CheckPerformanceData.UnitTests/Journey/JourneyViewModelBuilderTests.cs +++ b/tests/DfE.CheckPerformanceData.UnitTests/Journey/JourneyViewModelBuilderTests.cs @@ -165,7 +165,7 @@ public void BuildSummaryVm_WhenMatchedPupil_PopulatesRecordDisplays() var vm = _sut.BuildSummaryVm(WindowId, journey, mergeConfig); Assert.Equal("Jane Smith, 27 July 2010", vm.FirstRecordDisplay); - Assert.Equal("CYPMD456, John Doe", vm.SecondRecordDisplay); + Assert.Equal("John Doe 2 February 2010 (CYPMD456)", vm.SecondRecordDisplay); } [Fact] diff --git a/tests/DfE.CheckPerformanceData.UnitTests/Journey/MergeRecordDisplaysTests.cs b/tests/DfE.CheckPerformanceData.UnitTests/Journey/MergeRecordDisplaysTests.cs new file mode 100644 index 00000000..0d031c5d --- /dev/null +++ b/tests/DfE.CheckPerformanceData.UnitTests/Journey/MergeRecordDisplaysTests.cs @@ -0,0 +1,75 @@ +using DfE.CheckPerformanceData.Application.CheckYourPupilData; +using DfE.CheckPerformanceData.Application.Journey; + +namespace DfE.CheckPerformanceData.Application.UnitTests.Journey; + +public class MergeRecordDisplaysTests +{ + private static PupilDto Pupil(string? dob = "02/02/2010", string cypmdId = "CYPMD456") => new() + { + Id = Guid.NewGuid(), + Firstname = "John", + Surname = "Doe", + Sex = "M", + DateOfBirth = dob!, + Age = 16, + Cypmd_Id = cypmdId, + Identifier = "UPN002" + }; + + [Fact] + public void First_ParseableDob_RendersNameCommaDate() + { + var result = MergeRecordDisplays.First(Pupil()); + + Assert.Equal("John Doe, 2 February 2010", result); + } + + [Fact] + public void Second_ParseableDob_RendersNameDateInParenthesisedId() + { + var result = MergeRecordDisplays.Second(Pupil()); + + Assert.Equal("John Doe 2 February 2010 (CYPMD456)", result); + } + + [Fact] + public void First_UnparseableDob_EchoesRawValue() + { + var result = MergeRecordDisplays.First(Pupil(dob: "unknown")); + + Assert.Equal("John Doe, unknown", result); + } + + [Fact] + public void Second_UnparseableDob_EchoesRawValue() + { + var result = MergeRecordDisplays.Second(Pupil(dob: "unknown")); + + Assert.Equal("John Doe unknown (CYPMD456)", result); + } + + [Fact] + public void Second_EmptyDob_OmitsDobSegmentWithoutArtifacts() + { + var result = MergeRecordDisplays.Second(Pupil(dob: "")); + + Assert.Equal("John Doe (CYPMD456)", result); + } + + [Fact] + public void Second_EmptyCypmdId_StillRendersNameAndDob() + { + var result = MergeRecordDisplays.Second(Pupil(cypmdId: "")); + + Assert.Equal("John Doe 2 February 2010 ()", result); + } + + [Fact] + public void Second_SingleDigitDayAndMonth_FormatsWithoutLeadingZeros() + { + var result = MergeRecordDisplays.Second(Pupil(dob: "05/06/2010")); + + Assert.Equal("John Doe 5 June 2010 (CYPMD456)", result); + } +} \ No newline at end of file diff --git a/tests/DfE.CheckPerformanceData.UnitTests/Journey/PupilSearchJourneyTests.cs b/tests/DfE.CheckPerformanceData.UnitTests/Journey/PupilSearchJourneyTests.cs index 9f57fe01..da8b44ba 100644 --- a/tests/DfE.CheckPerformanceData.UnitTests/Journey/PupilSearchJourneyTests.cs +++ b/tests/DfE.CheckPerformanceData.UnitTests/Journey/PupilSearchJourneyTests.cs @@ -578,7 +578,7 @@ public async Task Summary_ForMerge_PopulatesSecondRecordDisplay() var view = Assert.IsType(result); var vm = Assert.IsType(view.Model); - Assert.Equal("CYPMD456, John Doe", vm.SecondRecordDisplay); + Assert.Equal("John Doe 2 February 2010 (CYPMD456)", vm.SecondRecordDisplay); } [Fact]