Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Develop #218

Merged
merged 9 commits into from
Sep 2, 2024
6 changes: 6 additions & 0 deletions src/Application/Features/Dashboard/Queries/GetDashboard.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ into grp
dto.PendingCases = result.Count;
continue;
}
if(result.Status == EnrolmentStatus.EnrolmentConfirmedStatus)
{
dto.ConfirmedCases = result.Count;
continue;
}
if (result.Status == EnrolmentStatus.SubmittedToProviderStatus)
{
dto.CasesAtPqa = result.Count;
Expand Down Expand Up @@ -72,6 +77,7 @@ public Validator()
public class DashboardDto
{
public int PendingCases { get; set; }
public int ConfirmedCases { get; set; }
public int CasesAtPqa { get; set; }
public int CasesAtCfo { get; set; }
public int ApprovedCases { get; set; }
Expand Down
54 changes: 54 additions & 0 deletions src/Application/Features/Participants/Commands/ConfirmEnrolment.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
using Cfo.Cats.Application.Common.Security;
using Cfo.Cats.Application.Common.Validators;
using Cfo.Cats.Application.SecurityConstants;

namespace Cfo.Cats.Application.Features.Participants.Commands;

public static class ConfirmEnrolment
{
[RequestAuthorize(Policy = SecurityPolicies.Enrol)]
public class Command : IRequest<Result>
{
public required string ParticipantId { get; set; }
}

class Handler(IUnitOfWork unitOfWork) : IRequestHandler<Command, Result>
{
public async Task<Result> Handle(Command request, CancellationToken cancellationToken)
{
var participant = await unitOfWork.DbContext.Participants.FindAsync(request.ParticipantId);

if(participant is null)
{
return Result.Failure("Participant not found");
}

participant.TransitionTo(EnrolmentStatus.EnrolmentConfirmedStatus);

return Result.Success();
}
}

class Validator : AbstractValidator<Command>
{
private readonly IUnitOfWork _unitOfWork;

public Validator(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;

RuleFor(c => c.ParticipantId)
.NotNull()
.Length(9)
.WithMessage("Invalid Participant Id")
.Matches(ValidationConstants.AlphaNumeric)
.WithMessage(string.Format(ValidationConstants.AlphaNumericMessage, "Participant Id"))
.MustAsync(Exist)
.WithMessage("Participant does not exist");
}

private async Task<bool> Exist(string identifier, CancellationToken cancellationToken)
=> await _unitOfWork.DbContext.Participants.AnyAsync(e => e.Id == identifier, cancellationToken);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ public async Task<Result<string>> Handle(Command request, CancellationToken canc
lastName: candidate.LastName,
gender: candidate.Gender,
dateOfBirth: candidate.DateOfBirth,
activeInFeed: candidate.IsActive,
referralSource: request.ReferralSource!,
referralComments: request.ReferralComments,
locationId: candidate.MappedLocationId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ public enum ParticipantListView
{
[Description("Default")] Default,
[Description("Pending")] Pending,
[Description("Enrolment Confirmed")] EnrolmentConfirmed,
[Description("Submitted To Provider")] SubmittedToProvider,
[Description("Submitted To QA")] SubmittedToQa,
[Description("Any QA")] SubmittedToAny,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public ParticipantAdvancedSpecification(ParticipantAdvancedFilter filter)
filter.ListView == ParticipantListView.Default);

Query.Where( p => p.EnrolmentStatus == EnrolmentStatus.PendingStatus.Value, filter.ListView == ParticipantListView.Pending);

Query.Where(p => p.EnrolmentStatus == EnrolmentStatus.EnrolmentConfirmedStatus.Value, filter.ListView == ParticipantListView.EnrolmentConfirmed);
Query.Where( p => p.EnrolmentStatus == EnrolmentStatus.SubmittedToProviderStatus.Value, filter.ListView == ParticipantListView.SubmittedToProvider);
Query.Where( p => p.EnrolmentStatus == EnrolmentStatus.SubmittedToAuthorityStatus.Value, filter.ListView == ParticipantListView.SubmittedToQa);
Query.Where( p => p.EnrolmentStatus == EnrolmentStatus.SubmittedToProviderStatus.Value || p.EnrolmentStatus == EnrolmentStatus.SubmittedToAuthorityStatus.Value, filter.ListView == ParticipantListView.SubmittedToAny);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public async Task<Result> Handle(Command request, CancellationToken cancellation
}

entry.Complete(request.Accept.GetValueOrDefault(), request.Message);
EnrolmentStatus transitionTo = request.Accept.GetValueOrDefault() ? EnrolmentStatus.SubmittedToAuthorityStatus : EnrolmentStatus.PendingStatus;
EnrolmentStatus transitionTo = request.Accept.GetValueOrDefault() ? EnrolmentStatus.SubmittedToAuthorityStatus : EnrolmentStatus.EnrolmentConfirmedStatus;
entry.Participant!.TransitionTo(transitionTo);

return Result.Success();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,4 +145,24 @@ private async Task<bool> MustBeJustified(Command command, CancellationToken canc
return false;
}
}

public class E_ParticipantStatusShouldBeConfirmed : AbstractValidator<Command>
{
private IUnitOfWork _unitOfWork;

public E_ParticipantStatusShouldBeConfirmed(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;

RuleFor(c => c.ParticipantId)
.MustAsync(MustBeConfirmed)
.WithMessage("Enrolment status must be confirmed");
}

private async Task<bool> MustBeConfirmed(string participantId, CancellationToken cancellationToken)
{
var result = await _unitOfWork.DbContext.Participants.SingleOrDefaultAsync(p => p.Id == participantId);
return result?.EnrolmentStatus == EnrolmentStatus.EnrolmentConfirmedStatus;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using Cfo.Cats.Domain.Events;

namespace Cfo.Cats.Application.Features.Timelines.EventHandlers;

public class ParticipantActiveStatusChangedDomainEventHandler(
ICurrentUserService currentUserService,
IUnitOfWork unitOfWork) : TimelineNotificationHandler<ParticipantActiveStatusChangedDomainEvent>(currentUserService, unitOfWork)
{
protected override TimelineEventType GetEventType() => TimelineEventType.Dms;

protected override string GetLine1(ParticipantActiveStatusChangedDomainEvent notification) => "Participant active status updated";

protected override string? GetLine2(ParticipantActiveStatusChangedDomainEvent notification) => string.Format("To {0}", notification.To ? "Active" : "Inactive");
protected override string? GetLine3(ParticipantActiveStatusChangedDomainEvent notification) => string.Format("From {0}", notification.From ? "Active" : "Inactive");

protected override string GetParticipantId(ParticipantActiveStatusChangedDomainEvent notification) => notification.Item.Id;
}
12 changes: 11 additions & 1 deletion src/Domain/Common/Enums/EnrolmentStatus.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ public abstract class EnrolmentStatus : SmartEnum<EnrolmentStatus>
{

public static readonly EnrolmentStatus PendingStatus = new Pending();
public static readonly EnrolmentStatus EnrolmentConfirmedStatus = new EnrolmentConfirmed();
public static readonly EnrolmentStatus SubmittedToProviderStatus = new SubmittedToProvider();
public static readonly EnrolmentStatus SubmittedToAuthorityStatus = new SubmittedToAuthority();
public static readonly EnrolmentStatus ApprovedStatus = new Approved();
Expand Down Expand Up @@ -43,7 +44,7 @@ public Pending()
: base("Pending", 0) { }

protected override EnrolmentStatus[] GetAllowedTransitions()
=> [ ArchivedStatus, SubmittedToProviderStatus];
=> [ ArchivedStatus, EnrolmentConfirmedStatus];
}

private sealed class SubmittedToProvider : EnrolmentStatus
Expand Down Expand Up @@ -102,6 +103,15 @@ protected override EnrolmentStatus[] GetAllowedTransitions() =>
[ ArchivedStatus, ApprovedStatus ];
}

private sealed class EnrolmentConfirmed : EnrolmentStatus
{
public EnrolmentConfirmed()
: base("Enrolment Confirmed", 6) { }

protected override EnrolmentStatus[] GetAllowedTransitions()
=> [ArchivedStatus, SubmittedToProviderStatus];
}

/// <summary>
/// Indicates that a participant at this enrolment stage is allowed to have a new assessment created
/// </summary>
Expand Down
27 changes: 23 additions & 4 deletions src/Domain/Entities/Participants/Participant.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ private Participant()
}
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.

public static Participant CreateFrom(string id, string firstName, string? middleName, string lastName, string? gender, DateTime dateOfBirth, string referralSource, string? referralComments, int locationId)
public static Participant CreateFrom(string id, string firstName, string? middleName, string lastName, string? gender, DateTime dateOfBirth, bool activeInFeed, string referralSource, string? referralComments, int locationId)
{
Participant p = new Participant
{
Expand All @@ -35,6 +35,7 @@ public static Participant CreateFrom(string id, string firstName, string? middle
MiddleName = middleName,
LastName = lastName,
Gender = gender,
ActiveInFeed = activeInFeed,
ReferralSource = referralSource,
ReferralComments = referralComments,
_currentLocationId = locationId
Expand All @@ -50,6 +51,11 @@ public static Participant CreateFrom(string id, string firstName, string? middle
public string? Gender { get; private set; }
public DateOnly DateOfBirth { get; private set; }

/// <summary>
/// Whether the participant is active in the DMS feed.
/// </summary>
public bool ActiveInFeed { get; private set; }

public string ReferralSource { get; private set; }

public string? ReferralComments { get; private set; }
Expand Down Expand Up @@ -169,8 +175,8 @@ public Participant AddOrUpdateDateOfBirth(DateOnly dateOfBirth)
{
if(DateOfBirth != dateOfBirth)
{
DateOfBirth = dateOfBirth;
AddDomainEvent(new ParticipantDateOfBirthChangedDomainEvent(this, DateOfBirth, dateOfBirth));
DateOfBirth = dateOfBirth;
}

return this;
Expand All @@ -187,8 +193,8 @@ public Participant AddOrUpdateExternalIdentifier(ExternalIdentifier newIdentifie

if(identifier is { Type.IsExclusive: true } )
{
_externalIdentifiers.Remove(identifier);
AddDomainEvent(new ParticipantIdentifierChangedDomainEvent(this, identifier, newIdentifier));
_externalIdentifiers.Remove(identifier);
}

_externalIdentifiers.Add(newIdentifier);
Expand All @@ -200,8 +206,8 @@ public Participant AddOrUpdateGender(string? gender)
{
if (string.Equals(Gender, gender, StringComparison.OrdinalIgnoreCase) is false)
{
Gender = gender;
AddDomainEvent(new ParticipantGenderChangedDomainEvent(this, Gender, gender));
Gender = gender;
}

return this;
Expand Down Expand Up @@ -254,4 +260,17 @@ public Participant ApproveConsent()
return this;
}


public Participant UpdateActiveStatus(bool activeInFeed)
{
if(ActiveInFeed != activeInFeed)
{
AddDomainEvent(new ParticipantActiveStatusChangedDomainEvent(this, ActiveInFeed, activeInFeed));
ActiveInFeed = activeInFeed;
}

return this;
}


}
8 changes: 8 additions & 0 deletions src/Domain/Events/ParticipantEvents.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,12 @@ public sealed class ParticipantNameChangedDomainEvent(Participant participant, s
public Participant Item { get; } = participant;
public string? From { get; } = from;
public string? To { get; } = to;
}

public sealed class ParticipantActiveStatusChangedDomainEvent(Participant item, bool from, bool to)
: DomainEvent
{
public Participant Item { get; } = item;
public bool From { get; } = from;
public bool To { get; } = to;
}
3 changes: 3 additions & 0 deletions src/Infrastructure/Jobs/SyncParticipantsJob.cs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@ public async Task Execute(IJobExecutionContext context)
participant.AddOrUpdateGender(candidate.Gender);
}

// Update active in feed status
participant.UpdateActiveStatus(candidate.IsActive);

// Dispatch events and commit transaction
await domainEventDispatcher.DispatchEventsAsync(unitOfWork.DbContext, CancellationToken.None);
await unitOfWork.CommitTransactionAsync();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ public void Configure(EntityTypeBuilder<Participant> builder)
builder.Property(p => p.Gender)
.HasMaxLength(6);

builder.Property(p => p.ActiveInFeed)
.IsRequired();

builder.Property(p => p.ReferralSource)
.IsRequired()
.HasMaxLength(100);
Expand Down
Loading
Loading