Enforced approval rights
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
using PostFundManagement.Application.Services.Grants;
|
||||
using PostFundManagement.Application.Services.Grants;
|
||||
using PostFundManagement.Domain;
|
||||
using PostFundManagement.Domain.Attributes;
|
||||
using PostFundManagement.Domain.Models;
|
||||
@@ -45,6 +45,10 @@ public interface IGrantsService
|
||||
[Audit]
|
||||
Task<Result> CreateAwardAsync(CreateAwardRequest request, Guid userId, CancellationToken cancellationToken = default);
|
||||
|
||||
[Log]
|
||||
[Audit]
|
||||
Task<Result> UpdateAwardAsync(UpdateAwardRequest request, Guid userId, CancellationToken cancellationToken = default);
|
||||
|
||||
[Log]
|
||||
[Audit]
|
||||
Task<Result<string>> GetContractUrlAsync(string contractIdHash, CancellationToken cancellationToken = default);
|
||||
|
||||
@@ -12,6 +12,9 @@ public sealed class GrantsCacheService(GrantsService service, IDistributedCache
|
||||
public async Task<Result> CreateAwardAsync(CreateAwardRequest request, Guid userId, CancellationToken cancellationToken = default) =>
|
||||
await service.CreateAwardAsync(request, userId, cancellationToken);
|
||||
|
||||
public async Task<Result> UpdateAwardAsync(UpdateAwardRequest request, Guid userId, CancellationToken cancellationToken = default) =>
|
||||
await service.UpdateAwardAsync(request, userId, cancellationToken);
|
||||
|
||||
public async Task<Result> CreateBudgetAsync(CreateBudgetRequest request, Guid userId, CancellationToken cancellationToken = default) =>
|
||||
await service.CreateBudgetAsync(request, userId, cancellationToken);
|
||||
|
||||
|
||||
@@ -206,6 +206,44 @@ public sealed class GrantsService(IDbContextFactory<ApplicationDbContext> contex
|
||||
return Result.OkIf(await context.SaveChangesAsync(cancellationToken) > 0, CreateError.Create("Failed to create award"));
|
||||
}));
|
||||
|
||||
public async Task<Result> UpdateAwardAsync(UpdateAwardRequest request, Guid userId, CancellationToken cancellationToken = default) =>
|
||||
await Result.Try((Func<Task<Result>>)(async () =>
|
||||
{
|
||||
if (request.Amount <= 0)
|
||||
return Result.Fail(ValidationError.Create("Approved amount must be greater than zero."));
|
||||
|
||||
await using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
var existingAward = await context.Awards.FirstOrDefaultAsync(a => a.Id == request.AwardId, cancellationToken);
|
||||
if (existingAward is null)
|
||||
return Result.Fail(NotFoundError.Create("Award not found"));
|
||||
|
||||
if (existingAward.Status == ApprovalStatus.Approved)
|
||||
return Result.Fail(ValidationError.Create("Approved awards are immutable and cannot be updated."));
|
||||
|
||||
if (!await context.Organisations.AnyAsync(o => o.Id == request.OrganisationId, cancellationToken))
|
||||
return Result.Fail(NotFoundError.Create("Organisation not found"));
|
||||
|
||||
if (!await context.Programmes.AnyAsync(p => p.Id == request.ProgrammeId, cancellationToken))
|
||||
return Result.Fail(NotFoundError.Create("Programme not found"));
|
||||
|
||||
existingAward.OrganisationId = request.OrganisationId;
|
||||
existingAward.ProgrammeId = request.ProgrammeId;
|
||||
existingAward.Amount = request.Amount;
|
||||
if (request.Status.HasValue)
|
||||
{
|
||||
existingAward.Status = request.Status.Value;
|
||||
if (request.Status.Value == ApprovalStatus.Approved)
|
||||
{
|
||||
existingAward.ApprovedBy = userId;
|
||||
}
|
||||
}
|
||||
existingAward.UpdatedBy = userId;
|
||||
existingAward.UpdatedAt = DateTime.UtcNow;
|
||||
|
||||
return Result.OkIf(await context.SaveChangesAsync(cancellationToken) > 0, UpdateError.Create("Failed to update award"));
|
||||
}));
|
||||
|
||||
public async Task<Result<string>> GetContractUrlAsync(string contractIdHash, CancellationToken cancellationToken = default) =>
|
||||
await Result.Try((Func<Task<Result<string>>>)(async () =>
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using PostFundManagement.Domain;
|
||||
using PostFundManagement.Domain;
|
||||
|
||||
namespace PostFundManagement.Application.Services.Grants;
|
||||
|
||||
@@ -8,6 +8,8 @@ public sealed record UpdateContractRequest([Required] long ContractId, [Required
|
||||
|
||||
public sealed record CreateAwardRequest([Required] long OrganisationId, [Required] long ProgrammeId, [Required] decimal Amount);
|
||||
|
||||
public sealed record UpdateAwardRequest([Required] long AwardId, [Required] long OrganisationId, [Required] long ProgrammeId, [Required] decimal Amount, ApprovalStatus? Status);
|
||||
|
||||
public sealed record CreateBudgetRequest([Required] long ProjectId,
|
||||
[Required][MaxLength(LabelLength)] string Name,
|
||||
[Required] decimal Amount);
|
||||
|
||||
Reference in New Issue
Block a user