diff --git a/ess/src/API/EMBC.ESS/Resources/Tasks/Contract.cs b/ess/src/API/EMBC.ESS/Resources/Tasks/Contract.cs index 58409ae88..7a985a616 100644 --- a/ess/src/API/EMBC.ESS/Resources/Tasks/Contract.cs +++ b/ess/src/API/EMBC.ESS/Resources/Tasks/Contract.cs @@ -47,11 +47,15 @@ public record EssTask : Task public bool RemoteExtensionsEnabled { get; set; } public bool SelfServeEnabled { get; set; } public IEnumerable EnabledSupports { get; set; } = []; + public IEnumerable SupportLimits { get; set; } = []; } public record SupportConfiguration { public SupportType SupportType { get; set; } + public DateTime SupportLimitStartDate { get; set; } + public DateTime SupportLimitEndDate { get; set; } + public bool ExtensionAvailable { get; set; } } public enum SupportType diff --git a/ess/src/API/EMBC.ESS/Resources/Tasks/Mappings.cs b/ess/src/API/EMBC.ESS/Resources/Tasks/Mappings.cs index e384e6cae..ce9ebd7d4 100644 --- a/ess/src/API/EMBC.ESS/Resources/Tasks/Mappings.cs +++ b/ess/src/API/EMBC.ESS/Resources/Tasks/Mappings.cs @@ -20,10 +20,28 @@ public Mappings() .ForMember(d => d.SelfServeEnabled, opts => opts.MapFrom(s => s.era_selfservetoggle)) .ForMember(d => d.AutoApprovedEnabled, opts => opts.Ignore()) .ForMember(d => d.EnabledSupports, opts => opts.MapFrom(s => s.era_era_task_era_selfservesupportlimits_Task.Where(sl => sl.statuscode == 1))) + .ForMember(d => d.SupportLimits, opts => opts.MapFrom(s => s.era_era_task_era_supportlimit_Task.Where(sl => sl.statuscode == 1))) ; CreateMap() .ForMember(d => d.SupportType, opts => opts.MapFrom(s => s.era_supporttypeoption)) + .ForMember(d => d.ExtensionAvailable, opts => opts.MapFrom(s => s.era_extensionavailable)) + .ForMember(d => d.SupportLimitStartDate, opts => opts.MapFrom(s => s.era_supportlimitstartdate.HasValue ? s.era_supportlimitstartdate.Value.UtcDateTime : (DateTime?)null)) + .ForMember(d => d.SupportLimitEndDate, opts => opts.MapFrom(s => s.era_supportlimitenddate.HasValue ? s.era_supportlimitenddate.Value.UtcDateTime : (DateTime?)null)) ; + + CreateMap() + .ForMember(d => d.SupportType, opts => opts.MapFrom(s => s.era_supporttypeoption)) + .ForMember(d => d.ExtensionAvailable, opts => opts.MapFrom(s => s.era_extensionavailable)) + .ForMember(d => d.SupportLimitStartDate, opts => opts.MapFrom(s => s.era_supportlimitstartdate.HasValue ? s.era_supportlimitstartdate.Value.UtcDateTime : (DateTime?)null)) + .ForMember(d => d.SupportLimitEndDate, opts => opts.MapFrom(s => s.era_supportlimitenddate.HasValue ? s.era_supportlimitenddate.Value.UtcDateTime : (DateTime?)null)) + ; + + CreateMap() + .ForMember(dest => dest.SupportType, opts => opts.MapFrom(src => src.SupportType)) + .ForMember(dest => dest.SupportLimitStartDate, opts => opts.MapFrom(src => src.SupportLimitStartDate)) + .ForMember(dest => dest.SupportLimitEndDate, opts => opts.MapFrom(src => src.SupportLimitEndDate)) + .ForMember(dest => dest.ExtensionAvailable, opts => opts.MapFrom(src => src.ExtensionAvailable)) + ; } } diff --git a/ess/src/API/EMBC.ESS/Resources/Tasks/TaskRepository.cs b/ess/src/API/EMBC.ESS/Resources/Tasks/TaskRepository.cs index 85ec512ba..553599e45 100644 --- a/ess/src/API/EMBC.ESS/Resources/Tasks/TaskRepository.cs +++ b/ess/src/API/EMBC.ESS/Resources/Tasks/TaskRepository.cs @@ -65,6 +65,7 @@ private async Task> QueryTasks(TaskQuery query, Cancellati essContext.era_tasks .Expand(t => t.era_JurisdictionID) .Expand(t => t.era_era_task_era_selfservesupportlimits_Task) + .Expand(t => t.era_era_task_era_supportlimit_Task) .Where(t => t.era_name == query.ById) .GetAllPagesAsync(ct)).ToList(); @@ -72,6 +73,9 @@ await Parallel.ForEachAsync(tasks, ct, async (t, ct1) => { var selfServeSupports = (await essContext.era_selfservesupportlimitses.Expand(sl => sl.era_SupportType).Where(sl => sl._era_task_value == t.era_taskid).GetAllPagesAsync(ct1)).ToList(); t.era_era_task_era_selfservesupportlimits_Task = new System.Collections.ObjectModel.Collection(selfServeSupports); + + var supportLimits = (await essContext.era_supportlimits.Expand(sl => sl.era_SupportType).Where(sl => sl._era_task_value == t.era_taskid).GetAllPagesAsync(ct1)).ToList(); + t.era_era_task_era_supportlimit_Task = new System.Collections.ObjectModel.Collection(supportLimits); }); return tasks; diff --git a/responders/src/API/EMBC.Responders.API/Controllers/TasksController.cs b/responders/src/API/EMBC.Responders.API/Controllers/TasksController.cs index 1217755b8..3d6a9e913 100644 --- a/responders/src/API/EMBC.Responders.API/Controllers/TasksController.cs +++ b/responders/src/API/EMBC.Responders.API/Controllers/TasksController.cs @@ -45,6 +45,7 @@ public async Task> GetTask(string taskId) if (task == null) return NotFound(taskId); var mappedTask = mapper.Map(task); mappedTask.Workflows = GetTaskWorkflows(task); + mappedTask.SupportLimits = GetTaskSupportLimits(task); return Ok(mappedTask); } @@ -60,6 +61,24 @@ private static IEnumerable GetTaskWorkflows(IncidentTask incidentT return workflows; } + private static IEnumerable GetTaskSupportLimits(IncidentTask incidentTask) + { + if (incidentTask.SupportLimits == null || !incidentTask.SupportLimits.Any()) + { + return Enumerable.Empty(); + } + + var supportLimits = incidentTask.SupportLimits.Select(sl => new SupportLimits + { + SupportType = sl.SupportType, + SupportLimitStartDate = sl.SupportLimitStartDate, + SupportLimitEndDate = sl.SupportLimitEndDate, + ExtensionAvailable = sl.ExtensionAvailable + }); + + return supportLimits; + } + [HttpGet("{taskId}/suppliers")] [ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status200OK)] @@ -96,6 +115,7 @@ public class ESSTask public string Description { get; set; } public string Status { get; set; } public IEnumerable Workflows { get; set; } = Array.Empty(); + public IEnumerable SupportLimits { get; set; } = Array.Empty(); } public class TaskWorkflow @@ -125,7 +145,8 @@ public class TaskMapping : Profile public TaskMapping() { CreateMap() - .ForMember(d => d.Workflows, opts => opts.Ignore()); + .ForMember(d => d.Workflows, opts => opts.Ignore()) + .ForMember(d => d.SupportLimits, opts => opts.MapFrom(s => s.SupportLimits)); CreateMap(); } } diff --git a/responders/src/UI/embc-responder/src/app/core/api/models/ess-task.ts b/responders/src/UI/embc-responder/src/app/core/api/models/ess-task.ts index f7ee7e4a3..b6beb7399 100644 --- a/responders/src/UI/embc-responder/src/app/core/api/models/ess-task.ts +++ b/responders/src/UI/embc-responder/src/app/core/api/models/ess-task.ts @@ -1,6 +1,7 @@ /* tslint:disable */ /* eslint-disable */ import { TaskWorkflow } from '../models/task-workflow'; +import { SupportLimit } from './support-limit'; export interface EssTask { communityCode?: string | null; description?: string | null; @@ -9,4 +10,5 @@ export interface EssTask { startDate?: string; status?: string | null; workflows?: Array | null; + supportLimits?: Array | null; } diff --git a/responders/src/UI/embc-responder/src/app/core/api/models/support-limit.ts b/responders/src/UI/embc-responder/src/app/core/api/models/support-limit.ts new file mode 100644 index 000000000..5cebd3e6d --- /dev/null +++ b/responders/src/UI/embc-responder/src/app/core/api/models/support-limit.ts @@ -0,0 +1,9 @@ +/* tslint:disable */ +/* eslint-disable */ +import { SupportSubCategory } from './support-sub-category'; +export interface SupportLimit { + supportLimitStartDate: Date; + supportLimitEndDate: Date; + extensionAvailable: boolean; + supportType: SupportSubCategory +} diff --git a/responders/src/UI/embc-responder/src/app/feature-components/wizard/step-supports/step-supports.service.ts b/responders/src/UI/embc-responder/src/app/feature-components/wizard/step-supports/step-supports.service.ts index 18a0dc8fe..72a59e594 100644 --- a/responders/src/UI/embc-responder/src/app/feature-components/wizard/step-supports/step-supports.service.ts +++ b/responders/src/UI/embc-responder/src/app/feature-components/wizard/step-supports/step-supports.service.ts @@ -31,6 +31,7 @@ import { DateConversionService } from 'src/app/core/services/utility/dateConvers import { ComputeRulesService } from 'src/app/core/services/computeRules.service'; import { AppBaseService } from 'src/app/core/services/helper/appBase.service'; import { EvacueeSessionService } from 'src/app/core/services/evacuee-session.service'; +import { SupportLimit } from 'src/app/core/api/models/support-limit'; @Injectable({ providedIn: 'root' }) export class StepSupportsService { @@ -41,6 +42,8 @@ export class StepSupportsService { private supportDetailsVal: SupportDetailsModel; private supportDeliveryVal: SupportDeliveryModel; private selectedSupportDetailVal: Support; + private supportLimitsVal: BehaviorSubject = new BehaviorSubject([]); + private supportLimitsVal$: Observable = this.supportLimitsVal.asObservable(); constructor( private essFileService: EssFileService, @@ -89,6 +92,36 @@ export class StepSupportsService { return this.existingSupportListVal$; } + setStoredSupportLimits(supportLimits: SupportLimit[]): void { + this.supportLimitsVal.next(supportLimits); + } + + getStoredSupportLimits(): Observable { + return this.supportLimitsVal$; + } + + fetchSupportLimits(): Observable { + return this.taskService + .tasksGetTask({ + taskId: this.userService?.currentProfile?.taskNumber + }) + .pipe( + map((task) => { + const supportLimits: SupportLimit[] = task.supportLimits.map((supportLimit) => { + return { + supportLimitStartDate: supportLimit.supportLimitStartDate, + supportLimitEndDate: supportLimit.supportLimitEndDate, + extensionAvailable: supportLimit.extensionAvailable, + supportType: supportLimit.supportType + }; + }); + + this.setStoredSupportLimits(supportLimits); + return supportLimits; + }) + ); + } + set supportTypeToAdd(supportTypeToAddVal: Code) { this.supportTypeToAddVal = supportTypeToAddVal; this.cacheService.set('supportType', JSON.stringify(supportTypeToAddVal)); diff --git a/responders/src/UI/embc-responder/src/app/feature-components/wizard/support-components/support-details/support-details.component.html b/responders/src/UI/embc-responder/src/app/feature-components/wizard/support-components/support-details/support-details.component.html index 51717bcf0..c968abcf2 100644 --- a/responders/src/UI/embc-responder/src/app/feature-components/wizard/support-components/support-details/support-details.component.html +++ b/responders/src/UI/embc-responder/src/app/feature-components/wizard/support-components/support-details/support-details.component.html @@ -377,38 +377,50 @@

- -
-
- All household members -
+
+ +
- @for (member of evacueeSessionService?.evacFile?.needsAssessment?.householdMembers; track member) { +
- {{ member.lastName | uppercase }}, {{ member.firstName | titlecase }}All household members
- } - @if ( - supportDetailsFormControl.members.invalid && - supportDetailsFormControl.members.hasError('noSelection') - ) { - Required - } - + @for (member of evacueeSessionService?.evacFile?.needsAssessment?.householdMembers; track member) { +
+
+ + {{ member.lastName | uppercase }}, {{ member.firstName | titlecase }} + + + @if (!isHouseholdMemberEligibleForSupport(member)) { + + {{ member.firstName | titlecase }} {{ member.lastName | titlecase }} has already received + this support. Extensions for this support are not available at this time. + + } +
+
+ } + @if ( + supportDetailsFormControl.members.invalid && + supportDetailsFormControl.members.hasError('noSelection') + ) { + Required + } +
@switch (stepSupportsService?.supportTypeToAdd?.value) { diff --git a/responders/src/UI/embc-responder/src/app/feature-components/wizard/support-components/support-details/support-details.component.ts b/responders/src/UI/embc-responder/src/app/feature-components/wizard/support-components/support-details/support-details.component.ts index 08f44b828..0df027385 100644 --- a/responders/src/UI/embc-responder/src/app/feature-components/wizard/support-components/support-details/support-details.component.ts +++ b/responders/src/UI/embc-responder/src/app/feature-components/wizard/support-components/support-details/support-details.component.ts @@ -1,5 +1,5 @@ -import { DatePipe, NgStyle, UpperCasePipe, TitleCasePipe } from '@angular/common'; -import { ChangeDetectionStrategy, Component, OnDestroy, OnInit } from '@angular/core'; +import { DatePipe, NgStyle, UpperCasePipe, TitleCasePipe, CommonModule } from '@angular/common'; +import { ChangeDetectionStrategy, Component, OnDestroy, OnInit, ChangeDetectorRef } from '@angular/core'; import { AbstractControl, UntypedFormArray, @@ -54,6 +54,7 @@ import { AppLoaderComponent } from '../../../../shared/components/app-loader/app import { MatInput } from '@angular/material/input'; import { MatFormField, MatPrefix, MatError, MatLabel, MatSuffix } from '@angular/material/form-field'; import { MatCard, MatCardContent } from '@angular/material/card'; +import { MatTooltipModule } from '@angular/material/tooltip'; @Component({ selector: 'app-support-details', @@ -80,6 +81,7 @@ import { MatCard, MatCardContent } from '@angular/material/card'; MatSelect, MatOption, MatCheckbox, + MatTooltipModule, ShelterAllowanceGroupComponent, FoodMealsComponent, FoodGroceriesComponent, @@ -93,7 +95,8 @@ import { MatCard, MatCardContent } from '@angular/material/card'; MatButton, UpperCasePipe, TitleCasePipe, - DatePipe + DatePipe, + CommonModule ] }) export class SupportDetailsComponent implements OnInit, OnDestroy { @@ -112,6 +115,8 @@ export class SupportDetailsComponent implements OnInit, OnDestroy { originalSupport: Support; existingSupports: Support[]; supportListSubscription: Subscription; + supportLimits: any; + supportLimitsSubscription: Subscription; constructor( private router: Router, @@ -126,6 +131,7 @@ export class SupportDetailsComponent implements OnInit, OnDestroy { private referralCreationService: ReferralCreationService, private dateConversionService: DateConversionService, private computeState: ComputeRulesService, + private cdr: ChangeDetectorRef, private loadEvacueeListService: LoadEvacueeListService ) { if (this.router.getCurrentNavigation() !== null) { @@ -235,6 +241,22 @@ export class SupportDetailsComponent implements OnInit, OnDestroy { }; ngOnInit(): void { + this.showLoader = true; + this.supportLimitsSubscription = this.stepSupportsService.fetchSupportLimits().subscribe({ + next: (supportLimits) => { + this.supportLimits = supportLimits; + this.showLoader = false; + this.cdr.detectChanges(); + }, + error: (error) => { + this.showLoader = false; + console.error('Error fetching support limits: ', error); + this.alertService.clearAlert(); + this.alertService.setAlert('danger', globalConst.supportListerror); + this.cdr.detectChanges(); + } + }); + this.supportListSubscription = this.stepSupportsService.getExistingSupportList().subscribe({ next: (supports) => { this.existingSupports = supports; @@ -277,6 +299,42 @@ export class SupportDetailsComponent implements OnInit, OnDestroy { ngOnDestroy(): void { this.supportListSubscription.unsubscribe(); + this.supportLimitsSubscription.unsubscribe(); + } + + isHouseholdMemberEligibleForSupport(member: EvacuationFileHouseholdMember): boolean { + if (!this.supportLimits || this.supportLimits.length === 0) { + return true; + } + const currentSupportType = this.stepSupportsService.supportTypeToAdd.description; + const matchingSupportLimit = this.supportLimits.find( + (limit) => this.mapSupportType(limit.supportType) === currentSupportType + ); + if (!matchingSupportLimit) { + return true; + } + if (matchingSupportLimit.extensionAvailable) { + return true; + } + const supportLimitStartDate = moment(matchingSupportLimit.supportLimitStartDate); + const supportLimitEndDate = moment(matchingSupportLimit.supportLimitEndDate); + const hasReceivedSupport = this.existingSupports.some((support) => { + const supportDate = moment(support.from); + return ( + support.category === currentSupportType && + support.includedHouseholdMembers?.some((m) => m === member.id) && + support.status !== SupportStatus.Cancelled.toString() && + support.status !== SupportStatus.Void.toString() && + supportDate.isBetween(supportLimitStartDate, supportLimitEndDate, 'days', '[]') + ); + }); + return !hasReceivedSupport; + } + + allMembersEligible(): boolean { + return this.evacueeSessionService?.evacFile?.needsAssessment?.householdMembers.every((member) => + this.isHouseholdMemberEligibleForSupport(member) + ); } checkDateRange(): boolean { @@ -378,7 +436,9 @@ export class SupportDetailsComponent implements OnInit, OnDestroy { if ($event.checked) { members.clear(); this.evacueeSessionService?.evacFile?.needsAssessment?.householdMembers.forEach((member) => { - members.push(new UntypedFormControl(member)); + if (this.isHouseholdMemberEligibleForSupport(member)) { + members.push(new UntypedFormControl(member)); + } }); } else { members.clear(); @@ -687,6 +747,33 @@ export class SupportDetailsComponent implements OnInit, OnDestroy { return largestToTime; } + private mapSupportType(supportType: number): SupportSubCategory | SupportCategory { + switch (supportType) { + case 174360000: + return SupportSubCategory.Food_Groceries; + case 174360001: + return SupportSubCategory.Food_Restaurant; + case 174360002: + return SupportSubCategory.Lodging_Hotel; + case 174360003: + return SupportSubCategory.Lodging_Billeting; + case 174360004: + return SupportSubCategory.Lodging_Group; + case 174360005: + return SupportCategory.Incidentals; + case 174360006: + return SupportCategory.Clothing; + case 174360007: + return SupportSubCategory.Transportation_Taxi; + case 174360008: + return SupportSubCategory.Transportation_Other; + case 174360009: + return SupportSubCategory.Lodging_Allowance; + default: + return SupportCategory.Unknown; + } + } + private setToTime() { if (this.evacueeSessionService.isPaperBased) { return this.stepSupportsService?.supportDetails?.toTime ? this.stepSupportsService?.supportDetails?.toTime : ''; diff --git a/responders/src/UI/embc-responder/src/styles/styles.scss b/responders/src/UI/embc-responder/src/styles/styles.scss index 7634b27d1..f1e172595 100644 --- a/responders/src/UI/embc-responder/src/styles/styles.scss +++ b/responders/src/UI/embc-responder/src/styles/styles.scss @@ -371,6 +371,11 @@ mat-form-field { font-size: 75%; } +.custom-mat-info { + font-size: 75%; + color: #000 !important; +} + .mat-mdc-dialog-container { border-radius: 0px !important; } diff --git a/shared/src/EMBC.ESS.Shared.Contracts/Events/Tasks.cs b/shared/src/EMBC.ESS.Shared.Contracts/Events/Tasks.cs index 791f619bc..6f5f83c37 100644 --- a/shared/src/EMBC.ESS.Shared.Contracts/Events/Tasks.cs +++ b/shared/src/EMBC.ESS.Shared.Contracts/Events/Tasks.cs @@ -39,6 +39,7 @@ public class IncidentTask public IncidentTaskStatus Status { get; set; } public bool RemoteExtensionsEnabled { get; set; } public bool SelfServeEnabled { get; set; } + public IEnumerable SupportLimits { get; set; } } public enum IncidentTaskStatus @@ -46,4 +47,26 @@ public enum IncidentTaskStatus Active, Expired } + + public record SupportLimits + { + public SupportType SupportType { get; set; } + public DateTime SupportLimitStartDate { get; set; } + public DateTime SupportLimitEndDate { get; set; } + public bool ExtensionAvailable { get; set; } + } + + public enum SupportType + { + FoodGroceries = 174360000, + FoodRestaurant = 174360001, + ShelterHotel = 174360002, + ShelterBilleting = 174360003, + ShelterGroup = 174360004, + Incidentals = 174360005, + Clothing = 174360006, + TransporationTaxi = 174360007, + TransportationOther = 174360008, + ShelterAllowance = 174360009 + } }