Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,23 @@
using Unity.Modules.Shared;
using Volo.Abp.Application.Dtos;

namespace Unity.GrantManager.GrantApplications
namespace Unity.GrantManager.GrantApplications;

public interface IGrantApplicationAppService
{
public interface IGrantApplicationAppService
{
Task<ApplicationStatusDto> GetApplicationStatusAsync(Guid id);
Task<ListResultDto<ApplicationActionDto>> GetActions(Guid applicationId, bool includeInternal = false);
Task<GrantApplicationDto> UpdateProjectInfoAsync(Guid id, CreateUpdateProjectInfoDto input);
Task<GrantApplicationDto> UpdatePartialProjectInfoAsync(Guid id, PartialUpdateDto<UpdateProjectInfoDto> input);
Task<GrantApplicationDto> UpdateAssessmentResultsAsync(Guid id, CreateUpdateAssessmentResultsDto input);
Task UpdateSupplierNumberAsync(Guid applicationId, string supplierNumber);
Task<List<GrantApplicationLiteDto>> GetAllApplicationsAsync();
Task<IList<GrantApplicationDto>> GetApplicationDetailsListAsync(List<Guid> applicationIds);
Task<GrantApplicationDto> GetAsync(Guid id);
Task<GrantApplicationDto> TriggerAction(Guid applicationId, GrantApplicationAction triggerAction);
Task<Guid?> GetAccountCodingIdFromFormIdAsync(Guid formId);
Task<string> HideAIAnalysisItemAsync(Guid applicationId, string itemId);
Task<string> ShowAIAnalysisItemAsync(Guid applicationId, string itemId);
Task<PagedResultDto<GrantApplicationDto>> GetListAsync(GrantApplicationListInputDto input);
}
Task<ApplicationStatusDto> GetApplicationStatusAsync(Guid id);
Task<ListResultDto<ApplicationActionDto>> GetActions(Guid applicationId, bool includeInternal = false);
Task<GrantApplicationDto> UpdateProjectInfoAsync(Guid id, CreateUpdateProjectInfoDto input);
Task<GrantApplicationDto> UpdatePartialProjectInfoAsync(Guid id, PartialUpdateDto<UpdateProjectInfoDto> input);
Task<GrantApplicationDto> UpdateAssessmentResultsAsync(Guid id, CreateUpdateAssessmentResultsDto input);
Task UpdateSupplierNumberAsync(Guid applicationId, string supplierNumber);
Task<List<GrantApplicationLiteDto>> GetAllApplicationsAsync();
Task<IList<GrantApplicationDto>> GetApplicationDetailsListAsync(List<Guid> applicationIds);
Task<GrantApplicationDto> GetAsync(Guid id);
Task<GrantApplicationDto> TriggerAction(Guid applicationId, GrantApplicationAction triggerAction);
Task<Guid?> GetAccountCodingIdFromFormIdAsync(Guid formId);
Task<string> HideAIAnalysisItemAsync(Guid applicationId, string itemId);
Task<string> ShowAIAnalysisItemAsync(Guid applicationId, string itemId);
Task<PagedResultDto<GrantApplicationDto>> GetListAsync(GrantApplicationListInputDto input);
Task<bool> IsApplicantRedStopAsync(Guid applicationId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@
using System.Threading.Tasks;
using Unity.Flex.WorksheetInstances;
using Unity.Flex.Worksheets;
using Unity.GrantManager.AI.Models;
using Unity.GrantManager.AI.Responses;
using Unity.GrantManager.Applicants;
using Unity.GrantManager.ApplicationForms;
using Unity.GrantManager.Applications;
using Unity.GrantManager.AI.Models;
using Unity.GrantManager.AI.Responses;
using Unity.GrantManager.Events;
using Unity.GrantManager.Flex;
using Unity.GrantManager.Identity;
Expand Down Expand Up @@ -955,6 +955,14 @@ public async Task<ApplicationStatusDto> GetApplicationStatusAsync(Guid id)
return form.AccountCodingId;
}



public async Task<bool> IsApplicantRedStopAsync(Guid applicationId)
{
var application = await applicationRepository.GetAsync(applicationId, true);
return application.Applicant != null && application.Applicant.RedStop == true;
}

#region APPLICATION WORKFLOW
/// <summary>
/// Fetches the list of actions and their status context for a given application.
Expand All @@ -974,9 +982,10 @@ public async Task<ListResultDto<ApplicationActionDto>> GetActions(Guid applicati

// NOTE: Authorization is applied on the AppService layer and is false by default
// AUTHORIZATION HANDLING
bool isRedStop = application.Applicant != null && application.Applicant.RedStop == true;
foreach (var item in actionDtos)
{
item.IsPermitted = item.IsPermitted && (await AuthorizationService.IsGrantedAsync(application, GetActionAuthorizationRequirement(item.ApplicationAction)));
item.IsPermitted = !isRedStop && item.IsPermitted && (await AuthorizationService.IsGrantedAsync(application, GetActionAuthorizationRequirement(item.ApplicationAction)));
item.IsAuthorized = true;
Comment thread
plavoie-BC marked this conversation as resolved.
}

Expand All @@ -1002,6 +1011,12 @@ public async Task<GrantApplicationDto> TriggerAction(Guid applicationId, GrantAp
throw new UnauthorizedAccessException();
}

// RED STOP CHECK: Block all status actions when the applicant has RedStop = true
if (application.Applicant != null && application.Applicant.RedStop == true)
{
throw new UserFriendlyException(L["GrantApplication:ActionButton.RedStopWarning"]);
}
Comment thread
plavoie-BC marked this conversation as resolved.

application = await applicationManager.TriggerAction(applicationId, triggerAction);

await LocalEventBus.PublishAsync(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,7 @@
"AssessmentResultsView:AssessmentResultsForm.RiskRanking": "Risk Ranking",

"GrantApplication:ActionButtonName": "Status Actions",
"GrantApplication:ActionButton.RedStopWarning": "This application is from an applicant with a Red-Stop. Status actions are not permitted.",
"Enum:GrantApplicationAction.Open": "Open",
"Enum:GrantApplicationAction.Submit": "Submit",
"Enum:GrantApplicationAction.Internal_Assign": "Assign",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ public async Task<IViewComponentResult> InvokeAsync(Guid applicationId)
var viewModel = new ApplicationActionWidgetViewModel()
{
ApplicationId = applicationId,
ApplicationActions = await _applicationAppService.GetActions(applicationId)
ApplicationActions = await _applicationAppService.GetActions(applicationId),
IsRedStop = await _applicationAppService.IsApplicantRedStopAsync(applicationId)
};
Comment thread
plavoie-BC marked this conversation as resolved.

return View(viewModel);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ public class ApplicationActionWidgetViewModel
{
public Guid ApplicationId { get; set; }
public ListResultDto<ApplicationActionDto> ApplicationActions { get; set; } = new();
public bool IsRedStop { get; set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,23 @@
@using Unity.GrantManager.Localization;
@using Microsoft.Extensions.Localization;
@using Unity.GrantManager.Web.Views.Shared.Components.ApplicationActionWidget;
@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Button;

@model ApplicationActionWidgetViewModel;

@inject IStringLocalizer<GrantManagerResource> L
@inject IAuthorizationService AuthorizationService

@{
var actionButtonType = Model.IsRedStop ? AbpButtonType.Danger : AbpButtonType.Light;
var actionButtonTitleText = Model.IsRedStop ? L["GrantApplication:ActionButton.RedStopWarning"].Value : string.Empty;
}

<input type="hidden" id="actionWidget_ApplicantIsRedStop" value="@Model.IsRedStop.ToString().ToLower()" />
<abp-dropdown id="ApplicationActionDropdown">
<abp-dropdown-button
button-type="Light"
text="@L["GrantApplication:ActionButtonName"].Value" />
<abp-dropdown-button button-type=@(actionButtonType)
text="@L["GrantApplication:ActionButtonName"].Value"
title="@actionButtonTitleText" />
<ul class="dropdown-menu">
@foreach (var item in Model.ApplicationActions.Items.Where(x => x.IsAuthorized))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,10 @@

function customConfirmation(triggerActionEnum) {
let confirmationDetails = getConfirmationText(triggerActionEnum);
let isRedStop = $('#redStop').prop("checked");
let isRedStop = document.getElementById('actionWidget_ApplicantIsRedStop')?.value === 'true';

if (isRedStop && triggerActionEnum === 'Approve') {
return handleRedStopApproval();
if (isRedStop) {
return handleRedStopAction();
}
Comment thread
plavoie-BC marked this conversation as resolved.

if (triggerActionEnum === 'CompleteAssessment') {
Expand All @@ -58,10 +58,10 @@
}
}

function handleRedStopApproval() {
function handleRedStopAction() {
return Swal.fire({
icon: "error",
text: "This application is currently flagged as high risk. Approval is not permitted at this time",
text: l("GrantApplication:ActionButton.RedStopWarning"),
confirmButtonText: 'Ok',
customClass: {
confirmButton: 'btn btn-primary'
Expand Down
Loading