Skip to content

Commit

Permalink
TRS console snags (#923)
Browse files Browse the repository at this point in the history
* Fixed a number of snags

* Added paging to cases + exclude non-uploaded file annotations from document queries

* Aligned UI and CRM paging for Cases

* Fixed broken test

* Refactored how we get incident details and documents so that it is testable using fake XRM

* Fixed compiler warning
  • Loading branch information
hortha authored Nov 16, 2023
1 parent 050f0fd commit 7cb4951
Show file tree
Hide file tree
Showing 32 changed files with 265 additions and 124 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -697,6 +697,7 @@ public static class Fields
public const string Id = "annotationid";
public const string DocumentBody = "documentbody";
public const string FileName = "filename";
public const string IsDocument = "isdocument";
public const string MimeType = "mimetype";
public const string ModifiedBy = "modifiedby";
public const string ModifiedOn = "modifiedon";
Expand Down Expand Up @@ -844,6 +845,26 @@ public string FileName
}
}

/// <summary>
/// Specifies whether the note is an attachment.
/// </summary>
[Microsoft.Xrm.Sdk.AttributeLogicalNameAttribute("isdocument")]
public System.Nullable<bool> IsDocument
{
[System.Diagnostics.DebuggerNonUserCode()]
get
{
return this.GetAttributeValue<System.Nullable<bool>>("isdocument");
}
[System.Diagnostics.DebuggerNonUserCode()]
set
{
this.OnPropertyChanging("IsDocument");
this.SetAttributeValue("isdocument", value);
this.OnPropertyChanged("IsDocument");
}
}

/// <summary>
/// MIME type of the note's attachment.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
namespace TeachingRecordSystem.Core.Dqt.Models;

public record GetIncidentsResult(Incident[] Incidents, int TotalRecordCount);

Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
namespace TeachingRecordSystem.Core.Dqt.Models;

public record IncidentDetail(Incident Incident, Contact Contact, Subject Subject, IncidentDocument[] IncidentDocuments);

public record IncidentDocument(dfeta_document Document, Annotation Annotation);
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
namespace TeachingRecordSystem.Core.Dqt.Queries;

public record GetActiveIncidentsQuery : ICrmQuery<Incident[]>;
public record GetActiveIncidentsQuery(int PageNumber, int PageSize) : ICrmQuery<GetIncidentsResult>;
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
namespace TeachingRecordSystem.Core.Dqt.Queries;

public record GetIncidentByTicketNumberQuery(string TicketNumber) : ICrmQuery<(Incident, dfeta_document[])?>;
public record GetIncidentByTicketNumberQuery(string TicketNumber) : ICrmQuery<IncidentDetail?>;
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@

namespace TeachingRecordSystem.Core.Dqt.QueryHandlers;

public class GetActiveIncidentsHandler : ICrmQueryHandler<GetActiveIncidentsQuery, Incident[]>
public class GetActiveIncidentsHandler : ICrmQueryHandler<GetActiveIncidentsQuery, GetIncidentsResult>
{
public async Task<Incident[]> Execute(GetActiveIncidentsQuery query, IOrganizationServiceAsync organizationService)
public async Task<GetIncidentsResult> Execute(GetActiveIncidentsQuery query, IOrganizationServiceAsync organizationService)
{
var filter = new FilterExpression(LogicalOperator.And);
filter.AddCondition(Incident.Fields.StateCode, ConditionOperator.Equal, (int)IncidentState.Active);
Expand All @@ -20,15 +20,23 @@ public async Task<Incident[]> Execute(GetActiveIncidentsQuery query, IOrganizati
Criteria = filter,
Orders =
{
new OrderExpression(Incident.Fields.CreatedOn, OrderType.Descending)
new OrderExpression(Incident.Fields.CreatedOn, OrderType.Ascending)
},
PageInfo = new()
{
PageNumber = query.PageNumber,
Count = query.PageSize,
ReturnTotalRecordCount = true
}
};

AddContactLink(queryExpression);
AddSubjectLink(queryExpression);

var result = await organizationService.RetrieveMultipleAsync(queryExpression);
return result.Entities.Select(entity => entity.ToEntity<Incident>()).ToArray();
var incidents = result.Entities.Select(entity => entity.ToEntity<Incident>()).ToArray();

return new GetIncidentsResult(incidents, result.TotalRecordCount);

static void AddContactLink(QueryExpression query)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public async Task<Contact[]> Execute(GetContactsByNameQuery query, IOrganization
{
ColumnSet = query.ColumnSet,
Criteria = filter,
TopCount = query.MaxRecordCount,
TopCount = query.MaxRecordCount
};

switch (query.SortBy)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,14 @@ static void AddAnnotationLink(QueryExpression queryExpression)
Annotation.Fields.Subject,
Annotation.Fields.DocumentBody,
Annotation.Fields.MimeType,
Annotation.Fields.FileName);
Annotation.Fields.FileName,
Annotation.Fields.IsDocument);

annotationLink.EntityAlias = Annotation.EntityLogicalName;

var annotationFilter = new FilterExpression();
annotationFilter.AddCondition(Annotation.Fields.IsDocument, ConditionOperator.Equal, true);
annotationLink.LinkCriteria = annotationFilter;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@

namespace TeachingRecordSystem.Core.Dqt.QueryHandlers;

public class GetIncidentByTicketNumberHandler : ICrmQueryHandler<GetIncidentByTicketNumberQuery, (Incident, dfeta_document[])?>
public class GetIncidentByTicketNumberHandler : ICrmQueryHandler<GetIncidentByTicketNumberQuery, IncidentDetail?>
{
public async Task<(Incident, dfeta_document[])?> Execute(GetIncidentByTicketNumberQuery query, IOrganizationServiceAsync organizationService)
public async Task<IncidentDetail?> Execute(GetIncidentByTicketNumberQuery query, IOrganizationServiceAsync organizationService)
{
var filter = new FilterExpression(LogicalOperator.And);
filter.AddCondition(Incident.Fields.TicketNumber, ConditionOperator.Equal, query.TicketNumber);
Expand Down Expand Up @@ -39,11 +39,20 @@ public class GetIncidentByTicketNumberHandler : ICrmQueryHandler<GetIncidentByTi
}

var incidentAndDocuments = result.Entities.Select(entity => entity.ToEntity<Incident>())
.Select(i => (Incident: i, Document: i.Extract<dfeta_document>(dfeta_document.EntityLogicalName, dfeta_document.PrimaryIdAttribute)));
.Select(i =>
(Incident: i,
Document: i.Extract<dfeta_document>(dfeta_document.EntityLogicalName, dfeta_document.PrimaryIdAttribute),
Annotation: i.Extract<Annotation>(Annotation.EntityLogicalName, Annotation.PrimaryIdAttribute)));

var returnValue = incidentAndDocuments
.GroupBy(t => t.Incident.TicketNumber)
.Select(g => (g.First().Incident, g.Where(i => i.Document != null).Select(i => i.Document).ToArray()))
.Select(g => (g.First().Incident, DocumentsAndAnnotations: g.Where(i => i.Document != null).Select(i => (Document: i.Document, Annotation: i.Annotation))))
.Select(i =>
new IncidentDetail(
i.Incident,
i.Incident.Extract<Contact>("contact", Contact.PrimaryIdAttribute),
i.Incident.Extract<Subject>("subject", Subject.PrimaryIdAttribute),
i.DocumentsAndAnnotations.Select(da => new IncidentDocument(da.Document, da.Annotation)).ToArray()))
.FirstOrDefault();

return returnValue;
Expand Down Expand Up @@ -119,9 +128,14 @@ static void AddAnnotationLink(LinkEntity documentLink)
Annotation.Fields.ObjectId,
Annotation.Fields.Subject,
Annotation.Fields.MimeType,
Annotation.Fields.FileName);
Annotation.Fields.FileName,
Annotation.Fields.IsDocument);

annotationLink.EntityAlias = $"{dfeta_document.EntityLogicalName}.{Annotation.EntityLogicalName}";
annotationLink.EntityAlias = Annotation.EntityLogicalName;

var annotationFilter = new FilterExpression();
annotationFilter.AddCondition(Annotation.Fields.IsDocument, ConditionOperator.Equal, true);
annotationLink.LinkCriteria = annotationFilter;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
}
</govuk-select>

<govuk-textarea asp-for="Details" label-class="govuk-label--m"/>
<govuk-character-count asp-for="Details" label-class="govuk-label--m" max-length="4000" />

<govuk-input asp-for="Link" label-class="govuk-label--m"/>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ public IndexModel(
public Guid? AlertTypeId { get; set; }

[BindProperty]
[Display(Description = "You can enter up to 4000 characters")]
public string? Details { get; set; }

[BindProperty]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,20 +30,23 @@
</table>
</div>
</div>

<hr class="govuk-section-break govuk-section-break--l govuk-section-break--visible">

<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds-from-desktop">
<h3 class="govuk-heading-s">Details</h3>
@if (!string.IsNullOrEmpty(Model.Alert.Details))
@if (!string.IsNullOrEmpty(Model.Alert.Details) || !string.IsNullOrEmpty(Model.Alert.DetailsLink))
{
<multi-line-text data-testid="alert-details" text="@Model.Alert.Details"/>
}
<h3 class="govuk-heading-s">Details</h3>
@if (!string.IsNullOrEmpty(Model.Alert.Details))
{
<multi-line-text data-testid="alert-details" text="@Model.Alert.Details" />
}

@if (!string.IsNullOrEmpty(Model.Alert.DetailsLink))
{
<a href="@Model.Alert.DetailsLink" class="govuk-link" rel="noreferrer noopener" target="_blank" data-testid="full-case-details-link">See full case details (opens in new tab)</a>
@if (!string.IsNullOrEmpty(Model.Alert.DetailsLink))
{
<a href="@Model.Alert.DetailsLink" class="govuk-link" rel="noreferrer noopener" target="_blank" data-testid="full-case-details-link">See full case details (opens in new tab)</a>
}
}

@if (Model.IsActive)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,27 +30,27 @@ public AcceptModel(

public async Task<IActionResult> OnGet()
{
(Incident Incident, dfeta_document[] Documents)? incidentAndDocuments = await GetIncidentAndDocuments();
if (incidentAndDocuments is null)
var incidentDetail = await GetIncidentDetail();
if (incidentDetail is null)
{
return NotFound();
}

if (incidentAndDocuments.Value.Incident.StateCode != IncidentState.Active)
if (incidentDetail.Incident.StateCode != IncidentState.Active)
{
return BadRequest();
}

SetModelFromIncident(incidentAndDocuments.Value.Incident);
SetModelFromIncidentDetail(incidentDetail);

return Page();
}

public async Task<IActionResult> OnPost()
{
(Incident Incident, dfeta_document[] Documents)? incidentAndDocuments = await GetIncidentAndDocuments();
var incidentDetail = await GetIncidentDetail();

_ = await _crmQueryDispatcher.ExecuteQuery(new ApproveIncidentQuery(incidentAndDocuments.Value.Incident.Id));
_ = await _crmQueryDispatcher.ExecuteQuery(new ApproveIncidentQuery(incidentDetail!.Incident.Id));

TempData.SetFlashSuccess(
$"The request has been accepted",
Expand All @@ -59,10 +59,11 @@ public async Task<IActionResult> OnPost()
return Redirect(_linkGenerator.Cases());
}

private void SetModelFromIncident(Incident incident)
private void SetModelFromIncidentDetail(IncidentDetail incidentDetail)
{
var customer = incident.Extract<Contact>("contact", Contact.PrimaryIdAttribute);
var subject = incident.Extract<Subject>("subject", Subject.PrimaryIdAttribute);
var incident = incidentDetail.Incident;
var customer = incidentDetail.Contact;
var subject = incidentDetail.Subject;

if (subject.Title == DqtConstants.NameChangeSubjectTitle)
{
Expand All @@ -77,6 +78,6 @@ private void SetModelFromIncident(Incident incident)
}
}

private Task<(Incident Incident, dfeta_document[] Documents)?> GetIncidentAndDocuments() =>
private Task<IncidentDetail?> GetIncidentDetail() =>
_crmQueryDispatcher.ExecuteQuery(new GetIncidentByTicketNumberQuery(TicketNumber));
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,16 @@
<caption class="govuk-table__caption govuk-table__caption--xl" data-testid="page-title">@ViewBag.Title</caption>
<thead class="govuk-table__head">
<tr class="govuk-table__row">
<th scope="col" class="govuk-table__header trs-table__header--no-border govuk-!-font-size-16 govuk-!-width-one-quarter">Case reference</th>
<th scope="col" class="govuk-table__header trs-table__header--no-border govuk-!-font-size-16 govuk-!-width-one-third">Name</th>
<th scope="col" class="govuk-table__header trs-table__header--no-border govuk-!-font-size-16">Created on</th>
<th scope="col" class="govuk-table__header trs-table__header--no-border govuk-!-width-one-quarter">Case reference</th>
<th scope="col" class="govuk-table__header trs-table__header--no-border govuk-!-width-one-third">Name</th>
<th scope="col" class="govuk-table__header trs-table__header--no-border">Created on</th>
</tr>
</thead>
<tbody class="govuk-table__body">
<tr class="govuk-table__row" data-testid="case-header">
<td class="govuk-table__cell trs-table__cell--no-border govuk-!-font-size-16" data-testid="case-header-case-reference">@Model.CaseHeader.CaseReference</td>
<td class="govuk-table__cell trs-table__cell--no-border govuk-!-font-size-16" data-testid="case-header-name">@Model.CaseHeader.Customer</td>
<td class="govuk-table__cell trs-table__cell--no-border govuk-!-font-size-16" data-testid="case-header-created-on">@Model.CaseHeader.CreatedOn.ToString("dd/MM/yyyy")</td>
<td class="govuk-table__cell trs-table__cell--no-border" data-testid="case-header-case-reference">@Model.CaseHeader.CaseReference</td>
<td class="govuk-table__cell trs-table__cell--no-border" data-testid="case-header-name">@Model.CaseHeader.Customer</td>
<td class="govuk-table__cell trs-table__cell--no-border" data-testid="case-header-created-on">@Model.CaseHeader.CreatedOn.ToString("dd/MM/yyyy")</td>
</tr>
</tbody>
</table>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,18 +30,18 @@ public IndexModel(ICrmQueryDispatcher crmQueryDispatcher)

public async Task<IActionResult> OnGet()
{
(Incident Incident, dfeta_document[] Documents)? incidentAndDocuments = await _crmQueryDispatcher.ExecuteQuery(new GetIncidentByTicketNumberQuery(TicketNumber));
if (incidentAndDocuments is null)
var incidentDetail = await _crmQueryDispatcher.ExecuteQuery(new GetIncidentByTicketNumberQuery(TicketNumber));
if (incidentDetail is null)
{
return NotFound();
}

if (incidentAndDocuments.Value.Incident.StateCode != IncidentState.Active)
if (incidentDetail.Incident.StateCode != IncidentState.Active)
{
return BadRequest();
}

SetModelFromIncidentAndDocuments(incidentAndDocuments.Value.Incident, incidentAndDocuments.Value.Documents);
SetModelFromIncidentDetail(incidentDetail);

return Page();
}
Expand All @@ -64,14 +64,16 @@ public async Task<IActionResult> OnGetDocuments(Guid id)
return File(bytes, annotation.MimeType);
}

private void SetModelFromIncidentAndDocuments(Incident incident, dfeta_document[] documents)
private void SetModelFromIncidentDetail(IncidentDetail incidentDetail)
{
var customer = incident.Extract<Contact>("contact", Contact.PrimaryIdAttribute);
var subject = incident.Extract<Subject>("subject", Subject.PrimaryIdAttribute);
var incident = incidentDetail.Incident;
var customer = incidentDetail.Contact;
var subject = incidentDetail.Subject;
var incidentDocuments = incidentDetail.IncidentDocuments;

CaseHeader = new CaseInfo()
{
CaseReference = incident.TicketNumber,
CaseReference = incidentDetail.Incident.TicketNumber,
Customer = customer.dfeta_StatedFirstName is not null ? $"{customer.dfeta_StatedFirstName} {customer.dfeta_StatedLastName}" : $"{customer.FirstName} {customer.LastName}",
CaseType = subject.Title,
CreatedOn = incident.CreatedOn!.Value
Expand Down Expand Up @@ -99,17 +101,16 @@ private void SetModelFromIncidentAndDocuments(Incident incident, dfeta_document[
};
}

if (documents is not null)
if (incidentDocuments.Length > 0)
{
var evidence = new List<EvidenceInfo>();
foreach (var document in documents)
foreach (var incidentDocument in incidentDocuments)
{
var annotation = document.Extract<Annotation>(Annotation.EntityLogicalName, Annotation.PrimaryIdAttribute);
evidence.Add(new EvidenceInfo()
{
DocumentId = document!.dfeta_documentId!.Value,
FileName = annotation.FileName,
MimeType = annotation.MimeType
DocumentId = incidentDocument.Document.dfeta_documentId!.Value,
FileName = incidentDocument.Annotation.FileName,
MimeType = incidentDocument.Annotation.MimeType
});
}

Expand Down
Loading

0 comments on commit 7cb4951

Please sign in to comment.