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

Release/v2024.10.10.1 #272

Merged
merged 27 commits into from
Oct 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
839abf7
Updated Participant screen Actions menu to shows Add Consent and Add …
vks333 Sep 18, 2024
27bffcf
Following changes:
PaulCooperWorkJustice Sep 18, 2024
a0f7a93
Update UploadService.cs
carlsixsmith-moj Sep 23, 2024
50925c3
CFODEV-761: Allow colons
carlsixsmith-moj Sep 26, 2024
517251c
Update the build.cake script
carlsixsmith-moj Sep 27, 2024
d6180c4
Remove file from solution folder
carlsixsmith-moj Sep 27, 2024
63f1a5c
Risk due date is now reset to 70 days when user reviews the Risk with…
vks333 Sep 30, 2024
2e8f8f5
CFODEV-752: This line is not required.
carlsixsmith-moj Sep 30, 2024
7716db2
Added warning message on the participant screen that right to work re…
vks333 Sep 18, 2024
e0cb4c7
changed query as suggested in the code review
vks333 Sep 30, 2024
d8a5daa
updated code as suggested, also added Nationalities Exempt for Right …
vks333 Oct 2, 2024
9346590
force fix for the nationality column
carlsixsmith-moj Oct 3, 2024
e5c4404
more removal of broken migrations
carlsixsmith-moj Oct 3, 2024
63fa4a4
CFODEV-764: Display other peoples participants.
carlsixsmith-moj Oct 2, 2024
f809165
Added Ability to reassign a case to new assignee.
carlsixsmith-moj Oct 4, 2024
4387b03
Use display name
carlsixsmith-moj Oct 4, 2024
bb454ac
adjust the reassignment feature to use a dialog, + work with new sear…
carlsixsmith-moj Oct 6, 2024
6830bb1
More reassignment
carlsixsmith-moj Oct 7, 2024
9d42a14
Added ability to add 256 characters to comments. Increased mulitline…
PaulCooperWorkJustice Oct 7, 2024
7be7e07
Extend to 1000 chars
carlsixsmith-moj Oct 7, 2024
a5e80df
CFODEV-767: add a "comment / defer" option that will allow a pqa to keep
carlsixsmith-moj Oct 7, 2024
5882540
CFODEV-771
carlsixsmith-moj Oct 8, 2024
1032b31
add warning to make sure user is aware comments are not private
carlsixsmith-moj Oct 8, 2024
dfdfd4f
Display notes
carlsixsmith-moj Oct 8, 2024
5c9ed90
logging on job
carlsixsmith-moj Oct 9, 2024
457a686
Filter at enrolment and current location
carlsixsmith-moj Oct 9, 2024
edd37a8
Remove co-financing.org internal nuget repository
samgibsonmoj Oct 10, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion NuGet.config
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,5 @@
<configuration>
<packageSources>
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
<add key="co-financing.org" value="https://packages.co-financing.org/nuget" />
</packageSources>
</configuration>
16 changes: 16 additions & 0 deletions build.cake
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

var target = Argument("target", "Test");
var configuration = Argument("configuration", "Release");
var fromVersion = Argument("fromVersion", "");


Task("Clean")
Expand Down Expand Up @@ -63,4 +64,19 @@ Task("Publish")
});
});

Task("Migrate")
.IsDependentOn("Test")
.Does(() =>{
var migrationProject = "src/Migrators/Migrators.MSSQL/Migrators.MSSQL.csproj";
var startupProject = "src/Server.UI/Server.UI.csproj";
var context = "Cfo.Cats.Infrastructure.Persistence.ApplicationDbContext";


var result = StartProcess("dotnet", $"ef migrations script {fromVersion} --no-build --configuration {configuration} --project {migrationProject} --startup-project {startupProject} --context {context} --idempotent -o ./publish/MigrationScript.sql");
if(result != 0)
{
Error("Failed to generate migration script");
}
});

RunTarget(target);
1 change: 0 additions & 1 deletion cats.sln
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "_SolutionItems", "_Solution
docker-compose.yml = docker-compose.yml
.config\dotnet-tools.json = .config\dotnet-tools.json
LICENSE = LICENSE
LocalPublish.ps1 = LocalPublish.ps1
ModifyDatabase.ps1 = ModifyDatabase.ps1
NuGet.config = NuGet.config
README.md = README.md
Expand Down
7 changes: 7 additions & 0 deletions src/Application/Common/Interfaces/IRightToWorkSettings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Cfo.Cats.Application.Common.Interfaces;

public interface IRightToWorkSettings
{
IList<string> NationalitiesExempted { get; set; }
}

Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@ public static class ValidationConstants
public const string DateMustBeInPast = "Date must be in the past.";
public const string DateMustBeInFuture = "Date must be in the future.";

public const string Notes = @"^[A-Za-z0-9 ?.,!""'\/$£&€\r\n\-\(\)@’;%]*$";
public const string Notes = @"^[A-Za-z0-9 ?.,!""'\/$£&€\r\n\-\(\)@’:;%]*$";
public const string NotesMessage = "{0} must contain only letters, numbers, spaces and common punctuation";

public const int NotesLength = 1000;

public const string GuidMessage = "{0} must contain a valid Guid";

Expand Down
20 changes: 12 additions & 8 deletions src/Application/Features/Participants/Commands/AddConsent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ public async Task<Result<string>> Handle(Command request, CancellationToken canc
}

var document = Document.Create(request.Document!.Name,
$"Consent form for {request.ParticipantId}",
DocumentType.PDF);
$"Consent form for {request.ParticipantId}",
DocumentType.PDF);

long maxSizeBytes = Convert.ToInt64(ByteSize.FromMegabytes(Infrastructure.Constants.Documents.Consent.MaximumSizeInMegabytes).Bytes);
await using var stream = request.Document.OpenReadStream(maxSizeBytes);
Expand All @@ -53,12 +53,16 @@ public async Task<Result<string>> Handle(Command request, CancellationToken canc

var result = await uploadService.UploadAsync($"{request.ParticipantId}/consent", uploadRequest);

document.SetURL(result);
document.SetVersion(request.DocumentVersion!);
if (result.Succeeded)
{
document.SetURL(result);
document.SetVersion(request.DocumentVersion!);

participant.AddConsent(request.ConsentDate!.Value, document.Id);

await unitOfWork.DbContext.Documents.AddAsync(document);
}

participant.AddConsent(request.ConsentDate!.Value, document.Id);

unitOfWork.DbContext.Documents.Add(document);
return result;
}
}
Expand Down Expand Up @@ -135,4 +139,4 @@ private async Task<bool> BePdfFile(IBrowserFile? file, CancellationToken cancell
}
}
}
}
}
2 changes: 1 addition & 1 deletion src/Application/Features/Participants/Commands/AddNote.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public Validator(IUnitOfWork unitOfWork)

RuleFor(c => c.Message)
.NotEmpty()
.MaximumLength(255)
.MaximumLength(ValidationConstants.NotesLength)
.Matches(ValidationConstants.Notes)
.WithMessage(string.Format(ValidationConstants.NotesMessage, "Message"));
}
Expand Down
24 changes: 11 additions & 13 deletions src/Application/Features/Participants/Commands/AddRightToWork.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@
using Cfo.Cats.Application.Common.Validators;
using Cfo.Cats.Application.SecurityConstants;
using Cfo.Cats.Domain.Entities.Documents;
using FluentValidation;
using Humanizer.Bytes;
using Microsoft.AspNetCore.Components.Forms;
using System.IO;

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

Expand Down Expand Up @@ -35,7 +33,6 @@ public class Command : IRequest<Result<string>>
public class Handler(IUnitOfWork unitOfWork, IUploadService uploadService)
: IRequestHandler<Command, Result<string>>
{

public async Task<Result<string>> Handle(Command request, CancellationToken cancellationToken)
{
var participant = await unitOfWork.DbContext.Participants.FindAsync(request.ParticipantId);
Expand All @@ -57,17 +54,19 @@ public async Task<Result<string>> Handle(Command request, CancellationToken canc
var uploadRequest = new UploadRequest(request.Document.Name, UploadType.Document, memoryStream.ToArray());

var result = await uploadService.UploadAsync($"{request.ParticipantId}/rtw", uploadRequest);

document.SetURL(result);

if(request.IndefiniteRightToWork)
if (result.Succeeded)
{
request.ValidTo = DateTime.MaxValue;
}
document.SetURL(result);

participant.AddRightToWork(request.ValidFrom!.Value, request.ValidTo!.Value, document.Id);
if (request.IndefiniteRightToWork)
{
request.ValidTo = DateTime.MaxValue;
}

participant.AddRightToWork(request.ValidFrom!.Value, request.ValidTo!.Value, document.Id);

unitOfWork.DbContext.Documents.Add(document);
unitOfWork.DbContext.Documents.Add(document);
}
return result;
}
}
Expand Down Expand Up @@ -145,6 +144,5 @@ private async Task<bool> BePdfFile(IBrowserFile? file, CancellationToken cancell
return header == "%PDF";
}
}

}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ public async Task<Result<string>> Handle(Command request, CancellationToken canc
activeInFeed: candidate.IsActive,
referralSource: request.ReferralSource!,
referralComments: request.ReferralComments,
locationId: candidate.MappedLocationId);
locationId: candidate.MappedLocationId,
nationality: candidate.Nationality);

if(candidate.Crn is not null)
{
Expand Down
103 changes: 103 additions & 0 deletions src/Application/Features/Participants/Commands/ReassignParticipants.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
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 ReassignParticipants
{
[RequestAuthorize(Policy = SecurityPolicies.UserHasAdditionalRoles)]
public class Command : IRequest<Result<bool>>
{
public string[] ParticipantIdsToReassign { get; set; } = [];

public UserProfile? CurrentUser { get; set; }

public string? AssigneeId { get; set; }
}

public class Handler(IUnitOfWork unitOfWork) : IRequestHandler<Command, Result<bool>>
{
public async Task<Result<bool>> Handle(Command request, CancellationToken cancellationToken)
{
foreach (var participantId in request.ParticipantIdsToReassign)
{
var participant = await unitOfWork.DbContext.Participants
.FirstOrDefaultAsync(x => x.Id == participantId);

if (participant is not null)
{
participant.AssignTo(request.AssigneeId);
}
else
{
return Result<bool>.Failure("Participant not found");
}
}

return Result<bool>.Success(true);
}
}

public class A_ : AbstractValidator<Command>
{
public A_()
{
RuleFor(x => x.AssigneeId)
.NotNull()
.MinimumLength(36);

RuleFor(x => x.ParticipantIdsToReassign)
.NotEmpty();

RuleFor(x => x.CurrentUser)
.NotNull();
}
}

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

public A_ParticipantExists(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;

// Validate each ParticipantId in the list
RuleForEach(p => p.ParticipantIdsToReassign)
.NotNull()
.Length(9)
.WithMessage("Invalid Participant Id")
.MustAsync(Exist)
.WithMessage("Participant does not exist")
.Matches(ValidationConstants.AlphaNumeric)
.WithMessage(string.Format(ValidationConstants.AlphaNumericMessage, "Participant Id"));
}

// Check if the participant exists in the database
private async Task<bool> Exist(string participantId, CancellationToken cancellationToken)
=> await _unitOfWork.DbContext.Participants.AnyAsync(p => p.Id == participantId, cancellationToken);
}

public class B_AssignerHasAccessToAssignee : AbstractValidator<Command>
{
private IUnitOfWork _unitOfWork;
public B_AssignerHasAccessToAssignee(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;

RuleFor(c => c)
.MustAsync(ValidAssignee)
.WithMessage("This assessment is created by you hence must not be processed at PQA stage by you");
}

private async Task<bool> ValidAssignee(Command c, CancellationToken cancellationToken)
{
return await _unitOfWork.DbContext.Users.Where(x => x.TenantId!.StartsWith(c.CurrentUser!.TenantId!)
&& x.Id == c.CurrentUser.UserId
&& x.IsActive == true
).AnyAsync(cancellationToken);
}
}
}
}
4 changes: 3 additions & 1 deletion src/Application/Features/Participants/DTOs/ParticipantDto.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public class ParticipantDto
public DateOnly? DateOfBirth { get; set; }
public DateTime? RiskDue { get; set; }
public int? RiskDueInDays { get; set; }
public string? Nationality { get; set; }

[Description("Enrolment Status")]
public EnrolmentStatus? EnrolmentStatus { get; set; }
Expand Down Expand Up @@ -63,7 +64,8 @@ public Mapping()
#nullable disable
.ForMember(target => target.SupportWorker, options => options.MapFrom(source => source.Owner.DisplayName))
.ForMember(dest => dest.RiskDue, opt => opt.MapFrom(src => src.RiskDue))
.ForMember(dest => dest.RiskDueInDays, opt => opt.MapFrom(src => src.RiskDueInDays()));
.ForMember(dest => dest.RiskDueInDays, opt => opt.MapFrom(src => src.RiskDueInDays()))
.ForMember(dest => dest.Nationality, opt => opt.MapFrom(src => src.Nationality));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,18 @@ public class ParticipantSummaryDto
public required DateOnly DateOfBirth { get; set; }
public DateTime? RiskDue { get; set; }
public int? RiskDueInDays { get; set; }
public string? Nationality { get; set; }
/// <summary>
/// The current enrolment status of the participant
/// </summary>
public EnrolmentStatus EnrolmentStatus { get; set; } = EnrolmentStatus.IdentifiedStatus;


/// <summary>
/// The current enrolment status of the participant
/// </summary>
public ConsentStatus ConsentStatus { get; set; } = ConsentStatus.PendingStatus;


/// <summary>
/// The person who "owns" this participant's case. Usually the support worker.
/// </summary>
Expand All @@ -49,6 +56,9 @@ public class ParticipantSummaryDto

public PathwayPlanSummaryDto? PathwayPlan { get; set; }

public bool HasActiveRightToWork { get; set; }
public bool IsRightToWorkRequired{ get; set; }

private class Mapping : Profile
{
public Mapping()
Expand All @@ -62,7 +72,8 @@ public Mapping()
.ForMember(target => target.OwnerName, options => options.MapFrom(source => source.Owner!.DisplayName))
.ForMember(target => target.ParticipantName, options => options.MapFrom(source => source.FirstName + ' ' + source.LastName))
.ForMember(dest => dest.RiskDue, opt => opt.MapFrom(src => src.RiskDue))
.ForMember(dest => dest.RiskDueInDays, opt => opt.MapFrom(src => src.RiskDueInDays()));
.ForMember(dest => dest.RiskDueInDays, opt => opt.MapFrom(src => src.RiskDueInDays()))
.ForMember(dest => dest.Nationality, opt => opt.MapFrom(src => src.Nationality));

CreateMap<ParticipantAssessment, AssessmentSummaryDto>()
.ForMember(target => target.AssessmentId, options => options.MapFrom(source => source.Id))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using Cfo.Cats.Domain.Events;

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

public class RiskInformationReviewed(IUnitOfWork unitOfWork) : INotificationHandler<RiskInformationReviewedDomainEvent>
{
public async Task Handle(RiskInformationReviewedDomainEvent notification, CancellationToken cancellationToken)
{
var participant = await unitOfWork.DbContext.Participants
.Where(p => p.Id == notification.Item.ParticipantId)
.FirstOrDefaultAsync(cancellationToken);

if (participant is not null)
{
participant.SetRiskDue(DateTime.UtcNow.AddDays(70));
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,9 @@ public class Query : IAuditableRequest<Result<ParticipantSummaryDto>>
public string Identifier() => ParticipantId;

}

public class Handler(IUnitOfWork unitOfWork, IMapper mapper) : IRequestHandler<Query, Result<ParticipantSummaryDto>>
public class Handler(IUnitOfWork unitOfWork, IMapper mapper, IRightToWorkSettings rtwSettings) : IRequestHandler<Query, Result<ParticipantSummaryDto>>
{

public async Task<Result<ParticipantSummaryDto>> Handle(Query request, CancellationToken cancellationToken)
{
var query = from c in unitOfWork.DbContext.Participants
Expand Down Expand Up @@ -59,6 +58,13 @@ public async Task<Result<ParticipantSummaryDto>> Handle(Query request, Cancellat

summary.BioSummary = mapper.Map<BioSummaryDto>(bio);

summary.HasActiveRightToWork = await unitOfWork.DbContext.Participants
.Where(x => x.Id == request.ParticipantId)
.SelectMany(p => p.RightToWorks)
.AnyAsync(x => x.Lifetime.EndDate >= DateTime.Now.Date, cancellationToken);

summary.IsRightToWorkRequired = rtwSettings.NationalitiesExempted.Any(s => s.Equals(summary.Nationality!, StringComparison.OrdinalIgnoreCase)) == false;

return Result<ParticipantSummaryDto>.Success(summary);
}
}
Expand Down
Loading
Loading