Skip to content

Commit

Permalink
Added previous names, nino and gender to person view + CRM query chan…
Browse files Browse the repository at this point in the history
…ges + tests
  • Loading branch information
hortha committed Sep 29, 2023
1 parent ebb8d8b commit 374dddb
Show file tree
Hide file tree
Showing 13 changed files with 222 additions and 35 deletions.
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
namespace TeachingRecordSystem.Core.Dqt.Models;

public record ContactDetail(Contact Contact);
public record ContactDetail(Contact Contact, dfeta_previousname[] PreviousNames);
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public static string ResolveFullName(this Contact contact, bool includeMiddleNam
{
if (fullName.Length > 0)
{
fullName.Append(" ");
fullName.Append(' ');
}

fullName.Append(middleName);
Expand All @@ -35,7 +35,7 @@ public static string ResolveFullName(this Contact contact, bool includeMiddleNam
{
if (fullName.Length > 0)
{
fullName.Append(" ");
fullName.Append(' ');
}

fullName.Append(lastName);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Microsoft.PowerPlatform.Dataverse.Client;
using Microsoft.Xrm.Sdk.Messages;
using Microsoft.Xrm.Sdk.Query;
using TeachingRecordSystem.Core.Dqt.Queries;

Expand All @@ -8,22 +9,53 @@ public class GetContactDetailByIdHandler : ICrmQueryHandler<GetContactDetailById
{
public async Task<ContactDetail?> Execute(GetContactDetailByIdQuery query, IOrganizationServiceAsync organizationService)
{
var filter = new FilterExpression();
filter.AddCondition(Contact.PrimaryIdAttribute, ConditionOperator.Equal, query.ContactId);

var queryExpression = new QueryExpression(Contact.EntityLogicalName)
var contactFilter = new FilterExpression();
contactFilter.AddCondition(Contact.PrimaryIdAttribute, ConditionOperator.Equal, query.ContactId);
var contactQueryExpression = new QueryExpression(Contact.EntityLogicalName)
{
ColumnSet = query.ColumnSet,
Criteria = filter
Criteria = contactFilter
};

var contactRequest = new RetrieveMultipleRequest()
{
Query = contactQueryExpression
};

var previousNameFilter = new FilterExpression();
previousNameFilter.AddCondition(dfeta_previousname.Fields.dfeta_PersonId, ConditionOperator.Equal, query.ContactId);
previousNameFilter.AddCondition(dfeta_previousname.Fields.StateCode, ConditionOperator.Equal, (int)dfeta_documentState.Active);
previousNameFilter.AddCondition(dfeta_previousname.Fields.dfeta_Type, ConditionOperator.NotEqual, (int)dfeta_NameType.Title);
var previousNameQueryExpression = new QueryExpression(dfeta_previousname.EntityLogicalName)
{
ColumnSet = new ColumnSet(
dfeta_previousname.PrimaryIdAttribute,
dfeta_previousname.Fields.CreatedOn,
dfeta_previousname.Fields.dfeta_ChangedOn,
dfeta_previousname.Fields.dfeta_name,
dfeta_previousname.Fields.dfeta_Type),
Criteria = previousNameFilter
};

var response = await organizationService.RetrieveMultipleAsync(queryExpression);
var contact = response.Entities.SingleOrDefault()?.ToEntity<Contact>();
var previousNameRequest = new RetrieveMultipleRequest()
{
Query = previousNameQueryExpression
};

var requestBuilder = RequestBuilder.CreateMultiple(organizationService);
var contactResponse = requestBuilder.AddRequest<RetrieveMultipleResponse>(contactRequest);
var previousNameResponse = requestBuilder.AddRequest<RetrieveMultipleResponse>(previousNameRequest);

await requestBuilder.Execute();

var contact = (await contactResponse.GetResponseAsync()).EntityCollection.Entities.FirstOrDefault()?.ToEntity<Contact>();
var previousNames = (await previousNameResponse.GetResponseAsync()).EntityCollection.Entities.Select(e => e.ToEntity<dfeta_previousname>()).ToArray();

if (contact is null)
{
return null;
}

return new ContactDetail(contact);
return new ContactDetail(contact, previousNames);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,9 @@
{
<tr class="govuk-table__row" data-testid="[email protected]">
<td class="govuk-table__cell" data-testid="name"><a href="@LinkGenerator.PersonDetail(personInfo.PersonId, search: Model.Search, sortBy: Model.SortBy, pageNumber: Model.PageNumber)" class="govuk-link">@personInfo.Name</a></td>
<td class="govuk-table__cell" data-testid="date-of-birth">@(personInfo.DateOfBirth.HasValue ? personInfo.DateOfBirth.Value.ToString("dd/MM/yyyy") : "-")</td>
<td class="govuk-table__cell" data-testid="trn">@(!string.IsNullOrEmpty(personInfo.Trn) ? personInfo.Trn : "-")</td>
<td class="govuk-table__cell" data-testid="nino">@(!string.IsNullOrEmpty(personInfo.NationalInsuranceNumber) ? personInfo.NationalInsuranceNumber : "-")</td>
<td class="govuk-table__cell" data-testid="date-of-birth" use-empty-fallback>@(personInfo.DateOfBirth.HasValue ? personInfo.DateOfBirth.Value.ToString("dd/MM/yyyy") : string.Empty)</td>
<td class="govuk-table__cell" data-testid="trn"><span use-empty-fallback>@personInfo.Trn</span></td>
<td class="govuk-table__cell" data-testid="nino"><span use-empty-fallback>@personInfo.NationalInsuranceNumber</span></td>
</tr>
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ public partial class IndexModel : PageModel

public IndexModel(
TrsLinkGenerator linkGenerator,
ICrmQueryDispatcher crmQueryDispatcher)
ICrmQueryDispatcher crmQueryDispatcher,
IConfiguration configuration)
{
_linkGenerator = linkGenerator;
_crmQueryDispatcher = crmQueryDispatcher;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ else
<govuk-summary-list>
<govuk-summary-list-row>
<govuk-summary-list-row-key>Start date</govuk-summary-list-row-key>
<govuk-summary-list-row-value data-testid="[email protected]">@(alert.StartDate.HasValue ? alert.StartDate.Value.ToString("dd/MM/yyyy") : "-")</govuk-summary-list-row-value>
<govuk-summary-list-row-value data-testid="[email protected]" use-empty-fallback>@(alert.StartDate.HasValue ? alert.StartDate.Value.ToString("dd/MM/yyyy") : string.Empty)</govuk-summary-list-row-value>
</govuk-summary-list-row>
<govuk-summary-list-row>
<govuk-summary-list-row-key>Details</govuk-summary-list-row-key>
Expand Down Expand Up @@ -72,8 +72,8 @@ else
{
<tr class="govuk-table__row" data-testid="[email protected]">
<td class="govuk-table__cell" data-testid="[email protected]"><a href="@LinkGenerator.Alert(alert.AlertId)" class="govuk-link">@alert.Description</a></td>
<td class="govuk-table__cell" data-testid="[email protected]">@(alert.StartDate.HasValue ? alert.StartDate.Value.ToString("dd/MM/yyyy") : "-")</td>
<td class="govuk-table__cell" data-testid="[email protected]">@(alert.EndDate.HasValue ? alert.EndDate.Value.ToString("dd/MM/yyyy") : "-")</td>
<td class="govuk-table__cell" data-testid="[email protected]" use-empty-fallback>@(alert.StartDate.HasValue ? alert.StartDate.Value.ToString("dd/MM/yyyy") : string.Empty)</td>
<td class="govuk-table__cell" data-testid="[email protected]" use-empty-fallback>@(alert.EndDate.HasValue ? alert.EndDate.Value.ToString("dd/MM/yyyy") : string.Empty)</td>
<td class="govuk-table__cell" data-testid="[email protected]">@alert.Status</td>
</tr>
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,32 +7,61 @@
ViewBag.PersonId = Model.PersonId;
ViewBag.Search = Model.Search;
ViewBag.SortBy = Model.SortBy;
ViewBag.PageNumber = Model.PageNumber;
ViewBag.PageNumber = Model.PageNumber;
ViewBag.Title = Model.Person!.Name;
}

@if (Model.Person.HasAlerts)
{
<govuk-notification-banner>
<p class="govuk-notification-banner__heading">
Alert on record.
<a class="govuk-notification-banner__link" href="@LinkGenerator.PersonAlerts(Model.PersonId, Model.Search, Model.SortBy, Model.PageNumber)">View alert</a>.
</p>
</govuk-notification-banner>

}

<govuk-summary-card>
<govuk-summary-card-title>Personal Details</govuk-summary-card-title>
<govuk-summary-list data-testid="personal-details">
<govuk-summary-list-row>
<govuk-summary-list-row-key>Name</govuk-summary-list-row-key>
<govuk-summary-list-row-value data-testid="personal-details-name">@Model.Person.FullName</govuk-summary-list-row-value>
</govuk-summary-list-row>
@if (Model.Person.PreviousNames.Length > 0)
{
@for (int i = 0; i < Model.Person.PreviousNames.Length; i++)
{
<govuk-summary-list-row>
<govuk-summary-list-row-key>@(i == 0 ? "Previous name(s)" : string.Empty)</govuk-summary-list-row-key>
<govuk-summary-list-row-value data-testid="personal-details-previous-names-@i">@Model.Person.PreviousNames[i]</govuk-summary-list-row-value>
</govuk-summary-list-row>
}
}
<govuk-summary-list-row>
<govuk-summary-list-row-key>Date of birth</govuk-summary-list-row-key>
<govuk-summary-list-row-value data-testid="personal-details-date-of-birth">@(Model.Person.DateOfBirth.HasValue ? Model.Person.DateOfBirth.Value.ToString("dd/MM/yyyy") : "Not provided")</govuk-summary-list-row-value>
<govuk-summary-list-row-value data-testid="personal-details-date-of-birth" use-empty-fallback>@(Model.Person.DateOfBirth.HasValue ? Model.Person.DateOfBirth.Value.ToString("dd/MM/yyyy") : string.Empty)</govuk-summary-list-row-value>
</govuk-summary-list-row>
<govuk-summary-list-row>
<govuk-summary-list-row-key>Gender</govuk-summary-list-row-key>
<govuk-summary-list-row-value data-testid="personal-details-gender" use-empty-fallback>@Model.Person.Gender</govuk-summary-list-row-value>
</govuk-summary-list-row>
<govuk-summary-list-row>
<govuk-summary-list-row-key>TRN</govuk-summary-list-row-key>
<govuk-summary-list-row-value data-testid="personal-details-trn">@(!string.IsNullOrEmpty(Model.Person.Trn) ? Model.Person.Trn : "Not provided")</govuk-summary-list-row-value>
<govuk-summary-list-row-value data-testid="personal-details-trn" use-empty-fallback>@Model.Person.Trn</govuk-summary-list-row-value>
</govuk-summary-list-row>
<govuk-summary-list-row>
<govuk-summary-list-row-key>National Insurance number</govuk-summary-list-row-key>
<govuk-summary-list-row-value data-testid="personal-details-nino" use-empty-fallback>@Model.Person.NationalInsuranceNumber</govuk-summary-list-row-value>
</govuk-summary-list-row>
<govuk-summary-list-row>
<govuk-summary-list-row-key>Email</govuk-summary-list-row-key>
<govuk-summary-list-row-value data-testid="personal-details-email">@(!string.IsNullOrEmpty(Model.Person.Email) ? Model.Person.Email : "Not provided")</govuk-summary-list-row-value>
<govuk-summary-list-row-value data-testid="personal-details-email" use-empty-fallback>@Model.Person.Email</govuk-summary-list-row-value>
</govuk-summary-list-row>
<govuk-summary-list-row>
<govuk-summary-list-row-key>Mobile number</govuk-summary-list-row-key>
<govuk-summary-list-row-value data-testid="personal-details-mobile-number">@(!string.IsNullOrEmpty(Model.Person.MobileNumber) ? Model.Person.MobileNumber : "Not provided")</govuk-summary-list-row-value>
<govuk-summary-list-row-value data-testid="personal-details-mobile-number" use-empty-fallback>@Model.Person.MobileNumber</govuk-summary-list-row-value>
</govuk-summary-list-row>
</govuk-summary-list>
</govuk-summary-card>
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Text;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Xrm.Sdk.Query;
Expand All @@ -9,10 +10,14 @@ namespace TeachingRecordSystem.SupportUi.Pages.Persons.PersonDetail;
public class IndexModel : PageModel
{
private readonly ICrmQueryDispatcher _crmQueryDispatcher;
private readonly TimeSpan _concurrentNameChangeWindow;

public IndexModel(ICrmQueryDispatcher crmQueryDispatcher)
public IndexModel(
ICrmQueryDispatcher crmQueryDispatcher,
IConfiguration configuration)
{
_crmQueryDispatcher = crmQueryDispatcher;
_concurrentNameChangeWindow = TimeSpan.FromSeconds(configuration.GetValue<int>("ConcurrentNameChangeWindowSeconds", 5));
}

[FromRoute]
Expand All @@ -36,6 +41,7 @@ public async Task<IActionResult> OnGet()
PersonId,
new ColumnSet(
Contact.Fields.dfeta_TRN,
Contact.Fields.CreatedOn,
Contact.Fields.BirthDate,
Contact.Fields.FirstName,
Contact.Fields.MiddleName,
Expand All @@ -45,15 +51,58 @@ public async Task<IActionResult> OnGet()
Contact.Fields.dfeta_StatedLastName,
Contact.Fields.EMailAddress1,
Contact.Fields.MobilePhone,
Contact.Fields.dfeta_NINumber)));
Contact.Fields.dfeta_NINumber,
Contact.Fields.GenderCode,
Contact.Fields.dfeta_ActiveSanctions)));

Person = MapContact(contactDetail!.Contact);
Person = MapContactDetail(contactDetail!);

return Page();
}

private PersonInfo MapContact(Contact contact)
private PersonInfo MapContactDetail(ContactDetail contactDetail)
{
var currentFirstName = contactDetail.Contact.FirstName;
var currentMiddleName = contactDetail.Contact.MiddleName;
var currentLastName = contactDetail.Contact.LastName;
var previousNames = new List<string>();
DateTime? createdOnBaseline = null;

foreach (var previousName in contactDetail.PreviousNames.OrderByDescending(p => p.CreatedOn))
{
if (createdOnBaseline is null)
{
createdOnBaseline = previousName.CreatedOn;
}
else if (createdOnBaseline - previousName.CreatedOn > _concurrentNameChangeWindow)
{
previousNames.Add(GetFullName(currentFirstName, currentMiddleName, currentLastName));
createdOnBaseline = previousName.CreatedOn;
}

switch (previousName.dfeta_Type)
{
case dfeta_NameType.FirstName:
currentFirstName = previousName.dfeta_name;
break;
case dfeta_NameType.MiddleName:
currentMiddleName = previousName.dfeta_name;
break;
case dfeta_NameType.LastName:
currentLastName = previousName.dfeta_name;
break;
default:
break;
}
}

if (createdOnBaseline is not null)
{
previousNames.Add(GetFullName(currentFirstName, currentMiddleName, currentLastName));
}

var contact = contactDetail.Contact;

return new PersonInfo()
{
Name = contact.ResolveFullName(includeMiddleName: false),
Expand All @@ -62,10 +111,39 @@ private PersonInfo MapContact(Contact contact)
Trn = contact.dfeta_TRN,
NationalInsuranceNumber = contact.dfeta_NINumber,
Email = contact.EMailAddress1,
MobileNumber = contact.MobilePhone
MobileNumber = contact.MobilePhone,
Gender = contact.GenderCode.ToString(),
HasAlerts = contact.dfeta_ActiveSanctions == true,
PreviousNames = previousNames.ToArray()
};
}

private static string GetFullName(string? firstName, string? middleName, string? lastName)
{
var fullName = new StringBuilder(firstName);
if (!string.IsNullOrEmpty(middleName))
{
if (fullName.Length > 0)
{
fullName.Append(' ');
}

fullName.Append(middleName);
}

if (!string.IsNullOrEmpty(lastName))
{
if (fullName.Length > 0)
{
fullName.Append(' ');
}

fullName.Append(lastName);
}

return fullName.ToString();
}

public record PersonInfo
{
public required string Name { get; init; }
Expand All @@ -75,5 +153,8 @@ public record PersonInfo
public required string? NationalInsuranceNumber { get; init; }
public required string? Email { get; init; }
public required string? MobileNumber { get; init; }
public required string? Gender { get; init; }
public required bool HasAlerts { get; init; }
public required string[] PreviousNames { get; init; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"ConcurrentNameChangeWindowSeconds": 1
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@
"CallbackPath": "/signin-oidc",
"SignedOutCallbackPath": "/signout-oidc"
},
"AllowedHosts": "*"
"AllowedHosts": "*",
"ConcurrentNameChangeWindowSeconds": 5
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,7 @@
align-items: flex-end;
display: flex;
}

.trs-subtle-emphasis {
color: govuk-colour("mid-grey");
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,25 @@ public async Task WhenCalled_WithContactIdForExistingContact_ReturnsContactDetai
// Assert
Assert.NotNull(results);
Assert.Equal(results.Contact.Id, person.ContactId);
Assert.Empty(results.PreviousNames);
}

[Fact]
public async Task WhenCalled_WithContactIdForExistingContactWithPreviousName_ReturnsContactDetailIncludingPreviousNames()
{
// Arrange
var updatedFirstName = _dataScope.TestData.GenerateFirstName();
var updatedMiddleName = _dataScope.TestData.GenerateMiddleName();
var updatedLastName = _dataScope.TestData.GenerateLastName();
var person = await _dataScope.TestData.CreatePerson();
await _dataScope.TestData.UpdatePerson(b => b.WithPersonId(person.ContactId).WithUpdatedName(updatedFirstName, updatedMiddleName, updatedLastName));

// Act
var results = await _crmQueryDispatcher.ExecuteQuery(new GetContactDetailByIdQuery(person.ContactId, new ColumnSet()));

// Assert
Assert.NotNull(results);
Assert.Equal(results.Contact.Id, person.ContactId);
Assert.Equal(3, results.PreviousNames.Length);
}
}
Loading

0 comments on commit 374dddb

Please sign in to comment.