Enforced relationships between activities and indicators, fixed actuals recording
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
using PostFundManagement.Application.Abstractions.Services;
|
||||
using PostFundManagement.Application.Abstractions.Services;
|
||||
using PostFundManagement.Application.Services.Shared;
|
||||
using PostFundManagement.Domain;
|
||||
using PostFundManagement.Domain.Abstractions;
|
||||
@@ -123,6 +123,29 @@ public sealed class ProjectService(IDbContextFactory<ApplicationDbContext> conte
|
||||
if (!await context.Milestones.AnyAsync(m => m.Id == request.MilestoneId.Value, cancellationToken))
|
||||
return Result.Fail(NotFoundError.New);
|
||||
|
||||
var approvedBudget = await context.Budgets
|
||||
.Where(b => b.ProjectId == request.ProjectId && (b.Status == ApprovalStatus.Approved || b.Status == ApprovalStatus.Pending))
|
||||
.SumAsync(b => b.Amount, cancellationToken);
|
||||
|
||||
if (approvedBudget == 0)
|
||||
{
|
||||
approvedBudget = await context.Awards
|
||||
.Where(a => a.ProjectId == request.ProjectId)
|
||||
.SumAsync(a => a.Amount, cancellationToken);
|
||||
}
|
||||
|
||||
if (approvedBudget > 0)
|
||||
{
|
||||
var existingTotal = await context.Activities
|
||||
.Where(a => a.ProjectId == request.ProjectId)
|
||||
.SumAsync(a => a.Amount, cancellationToken);
|
||||
|
||||
if (existingTotal + request.Amount > approvedBudget)
|
||||
{
|
||||
return Result.Fail(CreateError.Create($"Activity budget R {request.Amount:N2} exceeds the available project budget. Approved budget: R {approvedBudget:N2}, already allocated: R {existingTotal:N2}, remaining: R {(approvedBudget - existingTotal):N2}."));
|
||||
}
|
||||
}
|
||||
|
||||
context.Activities.Add(new Domain.Entities.Activity
|
||||
{
|
||||
ProjectId = request.ProjectId,
|
||||
@@ -141,6 +164,54 @@ public sealed class ProjectService(IDbContextFactory<ApplicationDbContext> conte
|
||||
CreateError.Create("No record was created."));
|
||||
}));
|
||||
|
||||
public async Task<Result> UpdateActivityAsync(UpdateActivityRequest request, Guid userId, CancellationToken cancellationToken = default) =>
|
||||
await Result.Try((Func<Task<Result>>)(async () =>
|
||||
{
|
||||
await using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
var activity = await context.Activities.FirstOrDefaultAsync(a => a.Id == request.ActivityId, cancellationToken);
|
||||
if (activity is null)
|
||||
return Result.Fail(NotFoundError.Create("Activity not found"));
|
||||
|
||||
if (request.MilestoneId.HasValue && !await context.Milestones.AnyAsync(m => m.Id == request.MilestoneId.Value, cancellationToken))
|
||||
return Result.Fail(NotFoundError.Create("Milestone not found"));
|
||||
|
||||
var approvedBudget = await context.Budgets
|
||||
.Where(b => b.ProjectId == request.ProjectId && (b.Status == ApprovalStatus.Approved || b.Status == ApprovalStatus.Pending))
|
||||
.SumAsync(b => b.Amount, cancellationToken);
|
||||
|
||||
if (approvedBudget == 0)
|
||||
{
|
||||
approvedBudget = await context.Awards
|
||||
.Where(a => a.ProjectId == request.ProjectId)
|
||||
.SumAsync(a => a.Amount, cancellationToken);
|
||||
}
|
||||
|
||||
if (approvedBudget > 0)
|
||||
{
|
||||
var otherTotal = await context.Activities
|
||||
.Where(a => a.ProjectId == request.ProjectId && a.Id != request.ActivityId)
|
||||
.SumAsync(a => a.Amount, cancellationToken);
|
||||
|
||||
if (otherTotal + request.Amount > approvedBudget)
|
||||
{
|
||||
return Result.Fail(UpdateError.Create($"Updated activity budget R {request.Amount:N2} exceeds the available project budget. Approved budget: R {approvedBudget:N2}, other allocated: R {otherTotal:N2}, remaining: R {(approvedBudget - otherTotal):N2}."));
|
||||
}
|
||||
}
|
||||
|
||||
activity.Title = request.Title.Humanize(LetterCasing.Title).Trim();
|
||||
activity.Description = request.Description;
|
||||
activity.Category = request.Category.Humanize(LetterCasing.Title).Trim();
|
||||
activity.Amount = request.Amount;
|
||||
activity.MilestoneId = request.MilestoneId;
|
||||
activity.PerformedAt = request.PerformedAt;
|
||||
activity.UpdatedBy = userId;
|
||||
activity.UpdatedAt = DateTime.UtcNow;
|
||||
|
||||
return Result.OkIf(await context.SaveChangesAsync(cancellationToken) > 0,
|
||||
UpdateError.Create("No changes detected, update cancelled."));
|
||||
}));
|
||||
|
||||
public async Task<Result<Milestone[]>> GetProjectMilestonesAsync(string projectIdHash, CancellationToken cancellationToken = default) =>
|
||||
await Result.Try((Func<Task<Result<Milestone[]>>>)(async () =>
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user