Skip to content

Commit

Permalink
Added View MQ (CRM + UI + tests) (#931)
Browse files Browse the repository at this point in the history
Fixed based on PR comments

Fixed compiler warning

Tweak based on PR comments

Tweaks based on updated designs

Add MQ Backend and UI

Fixed PR issues

Fixed PR issues

Added View MQ (CRM + UI + tests)

Re-ran CRM code generation following rebase

Regenerated CRM code after rebase

Re-added link lost in merge conflict resolution

Fixed failing test
  • Loading branch information
hortha authored Dec 8, 2023
1 parent 7716cd7 commit 6b1b75f
Show file tree
Hide file tree
Showing 25 changed files with 467 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8314,6 +8314,8 @@ public static class Fields
public const string dfeta_npqsl_awarded = "dfeta_npqsl_awarded";
public const string dfeta_npqsl_date = "dfeta_npqsl_date";
public const string dfeta_PersonId = "dfeta_personid";
public const string dfeta_qualificationId = "dfeta_qualificationid";
public const string Id = "dfeta_qualificationid";
public const string dfeta_TrsDeletedEvent = "dfeta_trsdeletedevent";
public const string dfeta_Type = "dfeta_type";
public const string StateCode = "statecode";
Expand Down Expand Up @@ -9096,6 +9098,49 @@ public Microsoft.Xrm.Sdk.EntityReference dfeta_PersonId
}
}

/// <summary>
/// Unique identifier for entity instances
/// </summary>
[Microsoft.Xrm.Sdk.AttributeLogicalNameAttribute("dfeta_qualificationid")]
public System.Nullable<System.Guid> dfeta_qualificationId
{
[System.Diagnostics.DebuggerNonUserCode()]
get
{
return this.GetAttributeValue<System.Nullable<System.Guid>>("dfeta_qualificationid");
}
[System.Diagnostics.DebuggerNonUserCode()]
set
{
this.OnPropertyChanging("dfeta_qualificationId");
this.SetAttributeValue("dfeta_qualificationid", value);
if (value.HasValue)
{
base.Id = value.Value;
}
else
{
base.Id = System.Guid.Empty;
}
this.OnPropertyChanged("dfeta_qualificationId");
}
}

[Microsoft.Xrm.Sdk.AttributeLogicalNameAttribute("dfeta_qualificationid")]
public override System.Guid Id
{
[System.Diagnostics.DebuggerNonUserCode()]
get
{
return base.Id;
}
[System.Diagnostics.DebuggerNonUserCode()]
set
{
this.dfeta_qualificationId = value;
}
}

/// <summary>
///
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
namespace TeachingRecordSystem.Core.Dqt.Queries;

public record GetQualificationsByContactIdQuery(Guid ContactId) : ICrmQuery<dfeta_qualification[]>;
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using Microsoft.PowerPlatform.Dataverse.Client;
using Microsoft.Xrm.Sdk.Messages;
using Microsoft.Xrm.Sdk.Query;
using TeachingRecordSystem.Core.Dqt.Queries;

namespace TeachingRecordSystem.Core.Dqt.QueryHandlers;

public class GetQualificationsByContactIdHandler : ICrmQueryHandler<GetQualificationsByContactIdQuery, dfeta_qualification[]>
{
public async Task<dfeta_qualification[]> Execute(GetQualificationsByContactIdQuery query, IOrganizationServiceAsync organizationService)
{
var filter = new FilterExpression();
filter.AddCondition(dfeta_qualification.Fields.dfeta_PersonId, ConditionOperator.Equal, query.ContactId);
filter.AddCondition(dfeta_qualification.Fields.StateCode, ConditionOperator.Equal, (int)dfeta_qualificationState.Active);

var queryExpression = new QueryExpression(dfeta_qualification.EntityLogicalName)
{
ColumnSet = new ColumnSet(
dfeta_qualification.PrimaryIdAttribute,
dfeta_qualification.Fields.dfeta_Type,
dfeta_qualification.Fields.dfeta_CompletionorAwardDate,
dfeta_qualification.Fields.dfeta_MQ_MQEstablishmentId,
dfeta_qualification.Fields.dfeta_MQStartDate,
dfeta_qualification.Fields.dfeta_MQ_SpecialismId,
dfeta_qualification.Fields.dfeta_MQ_Date,
dfeta_qualification.Fields.dfeta_MQ_Status,
dfeta_qualification.Fields.StateCode),
Criteria = filter
};

var request = new RetrieveMultipleRequest()
{
Query = queryExpression
};

var result = (RetrieveMultipleResponse)await organizationService.ExecuteAsync(request);
return result.EntityCollection.Entities.Select(entity => entity.ToEntity<dfeta_qualification>()).ToArray();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,19 +54,25 @@ public async Task<dfeta_earlyyearsstatus> GetEarlyYearsStatusByValue(string valu
return earlyYearsStatuses.Single(ey => ey.dfeta_Value == value);
}

public async Task<dfeta_specialism[]> GetSpecialisms()
public async Task<dfeta_specialism[]> GetMqSpecialisms()
{
var specialisms = await EnsureSpecialisms();
return specialisms.ToArray();
}

public async Task<dfeta_specialism> GetSpecialismByValue(string value)
public async Task<dfeta_specialism> GetMqSpecialismByValue(string value)
{
var specialisms = await EnsureSpecialisms();
// build environment has some duplicate Specialisms, which prevent us using Single() here
return specialisms.First(s => s.dfeta_Value == value);
}

public async Task<dfeta_specialism> GetMqSpecialismById(Guid specialismId)
{
var specialisms = await EnsureSpecialisms();
return specialisms.Single(s => s.dfeta_specialismId == specialismId);
}

public async Task<dfeta_mqestablishment[]> GetMqEstablishments()
{
var mqEstablishments = await EnsureMqEstablishments();
Expand All @@ -80,6 +86,12 @@ public async Task<dfeta_mqestablishment> GetMqEstablishmentByValue(string value)
return mqEstablishments.First(s => s.dfeta_Value == value);
}

public async Task<dfeta_mqestablishment> GetMqEstablishmentById(Guid mqEstablishmentId)
{
var mqEstablishments = await EnsureMqEstablishments();
return mqEstablishments.Single(s => s.dfeta_mqestablishmentId == mqEstablishmentId);
}

private Task<dfeta_sanctioncode[]> EnsureSanctionCodes() =>
LazyInitializer.EnsureInitialized(
ref _getSanctionCodesTask,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ namespace TeachingRecordSystem.SupportUi.Pages.Common;
public enum PersonSubNavigationTab
{
General,
Qualifications,
Alerts,
ChangeLog
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,7 @@ await _crmQueryDispatcher.ExecuteQuery(
await JourneyInstance!.CompleteAsync();
TempData.SetFlashSuccess("Mandatory qualification added");

// This is temporarily set to PersonDetail rather than PersonQualifications until that page has been created in another trello card
return Redirect(_linkGenerator.PersonDetail(PersonId));
return Redirect(_linkGenerator.PersonQualifications(PersonId));
}

public async Task<IActionResult> OnPostCancel()
Expand All @@ -79,7 +78,7 @@ public override async Task OnPageHandlerExecutionAsync(PageHandlerExecutingConte

PersonName = personDetail!.Contact.ResolveFullName(includeMiddleName: false);
MqEstablishment = await _referenceDataCache.GetMqEstablishmentByValue(JourneyInstance!.State.MqEstablishmentValue!);
Specialism = await _referenceDataCache.GetSpecialismByValue(JourneyInstance!.State.SpecialismValue!);
Specialism = await _referenceDataCache.GetMqSpecialismByValue(JourneyInstance!.State.SpecialismValue!);
StartDate = JourneyInstance!.State.StartDate;
Result = JourneyInstance!.State.Result;
EndDate = JourneyInstance!.State.EndDate;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
}

@section BeforeContent {
<govuk-back-link href="@LinkGenerator.PersonDetail(Model.PersonId)">Back</govuk-back-link>
<govuk-back-link href="@LinkGenerator.PersonQualifications(Model.PersonId)">Back</govuk-back-link>
}

<span class="govuk-caption-l">Add a mandatory qualification - @Model.PersonName</span>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public async Task<IActionResult> OnPost()
public async Task<IActionResult> OnPostCancel()
{
await JourneyInstance!.DeleteAsync();
return Redirect(_linkGenerator.PersonDetail(PersonId));
return Redirect(_linkGenerator.PersonQualifications(PersonId));
}

public override async Task OnPageHandlerExecutionAsync(PageHandlerExecutingContext context, PageHandlerExecutionDelegate next)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ public async Task<IActionResult> OnPost()
public async Task<IActionResult> OnPostCancel()
{
await JourneyInstance!.DeleteAsync();
return Redirect(_linkGenerator.PersonDetail(PersonId));
return Redirect(_linkGenerator.PersonQualifications(PersonId));
}

public override async Task OnPageHandlerExecutionAsync(PageHandlerExecutingContext context, PageHandlerExecutionDelegate next)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,14 @@ public async Task<IActionResult> OnPost()
public async Task<IActionResult> OnPostCancel()
{
await JourneyInstance!.DeleteAsync();
return Redirect(_linkGenerator.PersonDetail(PersonId));
return Redirect(_linkGenerator.PersonQualifications(PersonId));
}

public override async Task OnPageHandlerExecutionAsync(PageHandlerExecutingContext context, PageHandlerExecutionDelegate next)
{
var personDetail = (ContactDetail?)context.HttpContext.Items["CurrentPersonDetail"];

var specialisms = await _referenceDataCache.GetSpecialisms();
var specialisms = await _referenceDataCache.GetMqSpecialisms();
Specialisms = specialisms
.OrderBy(e => e.dfeta_name)
.ToArray();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public async Task<IActionResult> OnPost()
public async Task<IActionResult> OnPostCancel()
{
await JourneyInstance!.DeleteAsync();
return Redirect(_linkGenerator.PersonDetail(PersonId));
return Redirect(_linkGenerator.PersonQualifications(PersonId));
}

public override async Task OnPageHandlerExecutionAsync(PageHandlerExecutingContext context, PageHandlerExecutionDelegate next)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@
<a class="moj-sub-navigation__link" aria-current="@(selectedTab == PersonSubNavigationTab.General ? "page" : null)" href="@LinkGenerator.PersonDetail(personId, search, sortBy, pageNumber)">General</a>
</li>

<li class="moj-sub-navigation__item">
<a class="moj-sub-navigation__link" aria-current="@(selectedTab == PersonSubNavigationTab.Qualifications ? "page" : null)" href="@LinkGenerator.PersonQualifications(personId, search, sortBy, pageNumber)">Qualifications</a>
</li>

<li class="moj-sub-navigation__item">
<a class="moj-sub-navigation__link" aria-current="@(selectedTab == PersonSubNavigationTab.Alerts ? "page" : null)" href="@LinkGenerator.PersonAlerts(personId, search, sortBy, pageNumber)">Alerts</a>
</li>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ else
}
}

<govuk-button-link href="@LinkGenerator.PersonAddAlert(Model.PersonId)" class="govuk-button" data-testid="add-alert">Add an alert</govuk-button-link>
<govuk-button-link href="@LinkGenerator.AlertAdd(Model.PersonId, journeyInstanceId: null)" class="govuk-button" data-testid="add-alert">Add an alert</govuk-button-link>

@if (Model.PreviousAlerts is not null && Model.PreviousAlerts.Length > 0)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
@page "/persons/{personId}/qualifications"
@using TeachingRecordSystem.SupportUi.Pages.Common;
@model TeachingRecordSystem.SupportUi.Pages.Persons.PersonDetail.QualificationsModel
@{
Layout = "Layout";
ViewBag.SelectedTab = PersonSubNavigationTab.Qualifications;
ViewBag.PersonId = Model.PersonId;
ViewBag.Search = Model.Search;
ViewBag.SortBy = Model.SortBy;
ViewBag.PageNumber = Model.PageNumber;
ViewBag.Title = Model.Name;
}

@if (Model.MandatoryQualifications is null || Model.MandatoryQualifications.Length == 0)
{
<p class="govuk-body" data-testid="no-mandatory-qualifications">No mandatory qualifications</p>
}
else
{
@foreach (var mq in Model.MandatoryQualifications)
{
<govuk-summary-card data-testid="[email protected]">
<govuk-summary-card-title>Mandatory qualification@(!string.IsNullOrEmpty(mq.Specialism) ? $" for {mq.Specialism!.ToLowerInvariant()}" : "")</govuk-summary-card-title>
<govuk-summary-card-actions>
<govuk-summary-card-action href="#" visually-hidden-text="remove qualification" data-testid="[email protected]">Remove qualification</govuk-summary-card-action>
</govuk-summary-card-actions>
<govuk-summary-list>
<govuk-summary-list-row>
<govuk-summary-list-row-key>Training provider</govuk-summary-list-row-key>
<govuk-summary-list-row-value data-testid="[email protected]">@(!string.IsNullOrEmpty(mq.Provider) ? mq.Provider : "None")</govuk-summary-list-row-value>
<govuk-summary-list-row-actions>
<govuk-summary-list-row-action href="#" visually-hidden-text="change training provider">Change</govuk-summary-list-row-action>
</govuk-summary-list-row-actions>
</govuk-summary-list-row>
<govuk-summary-list-row>
<govuk-summary-list-row-key>Specialism</govuk-summary-list-row-key>
<govuk-summary-list-row-value data-testid="[email protected]">@(!string.IsNullOrEmpty(mq.Specialism) ? mq.Specialism : "None")</govuk-summary-list-row-value>
<govuk-summary-list-row-actions>
<govuk-summary-list-row-action href="#" visually-hidden-text="change specialism">Change</govuk-summary-list-row-action>
</govuk-summary-list-row-actions>
</govuk-summary-list-row>
<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]">@(mq.StartDate.HasValue ? mq.StartDate!.Value.ToString("d MMMM yyyy") : "None")</govuk-summary-list-row-value>
<govuk-summary-list-row-actions>
<govuk-summary-list-row-action href="#" visually-hidden-text="change start date">Change</govuk-summary-list-row-action>
</govuk-summary-list-row-actions>
</govuk-summary-list-row>
<govuk-summary-list-row>
<govuk-summary-list-row-key>Result</govuk-summary-list-row-key>
<govuk-summary-list-row-value data-testid="[email protected]">@(mq.Result.HasValue ? mq.Result!.ToString() : "None")</govuk-summary-list-row-value>
<govuk-summary-list-row-actions>
<govuk-summary-list-row-action href="#" visually-hidden-text="change result">Change</govuk-summary-list-row-action>
</govuk-summary-list-row-actions>
</govuk-summary-list-row>
<govuk-summary-list-row>
<govuk-summary-list-row-key>End date</govuk-summary-list-row-key>
<govuk-summary-list-row-value data-testid="[email protected]">@(mq.EndDate.HasValue ? mq.EndDate!.Value.ToString("d MMMM yyyy") : "None")</govuk-summary-list-row-value>
<govuk-summary-list-row-actions>
<govuk-summary-list-row-action href="#" visually-hidden-text="change end date">Change</govuk-summary-list-row-action>
</govuk-summary-list-row-actions>
</govuk-summary-list-row>
</govuk-summary-list>
</govuk-summary-card>
}
}

<govuk-button-link href="@LinkGenerator.MqAdd(Model.PersonId)" class="govuk-button" data-testid="add-mandatory-qualification">Add a mandatory qualification</govuk-button-link>
Loading

0 comments on commit 6b1b75f

Please sign in to comment.