From 593219db8c48e86639392b9405a445d401175908 Mon Sep 17 00:00:00 2001 From: Hermes Agent Date: Thu, 24 Sep 2026 15:57:33 +0200 Subject: [PATCH 1/2] Stable run, Added remaining service implementations (stubs) --- .../Services/ICommunicationsService.cs | 14 ++ .../Services/IGovernanceService.cs | 32 +++++ .../Abstractions/Services/IProjectService.cs | 84 ++++++++++++ .../CommunicationsCacheService.cs | 56 +++++++- .../Communications/CommunicationsService.cs | 20 +++ .../Services/Communications/Records.cs | 12 ++ .../Governance/GovernanceCacheService.cs | 35 +++++ .../Services/Governance/GovernanceService.cs | 45 +++++++ .../Services/Governance/Records.cs | 22 +++- .../Services/Projects/ProjectCacheService.cs | 96 ++++++++++++++ .../Services/Projects/ProjectService.cs | 120 ++++++++++++++++++ .../Services/Projects/Records.cs | 69 ++++++++++ .../Models/AuditChanges.cs | 6 - 13 files changed, 598 insertions(+), 13 deletions(-) delete mode 100644 PostFundManagement.Domain/Models/AuditChanges.cs diff --git a/PostFundManagement.Application/Abstractions/Services/ICommunicationsService.cs b/PostFundManagement.Application/Abstractions/Services/ICommunicationsService.cs index 68adcc9..f1a1821 100644 --- a/PostFundManagement.Application/Abstractions/Services/ICommunicationsService.cs +++ b/PostFundManagement.Application/Abstractions/Services/ICommunicationsService.cs @@ -19,4 +19,18 @@ public interface ICommunicationsService [Log] Task> GetApplicantInvitesAsync(Guid applicantUserId, CancellationToken cancellationToken = default); + + [Log] + [Audit] + Task CreateNotificationAsync(CreateNotificationRequest request, Guid userId, CancellationToken cancellationToken = default); + + [Log] + [Audit] + Task UpdateNotificationAsync(UpdateNotificationRequest request, Guid userId, CancellationToken cancellationToken = default); + + [Log] + Task> GetNotificationsAsync(string organisationIdHash, CancellationToken cancellationToken = default); + + [Log] + Task> GetNotificationAsync(string notificationIdHash, CancellationToken cancellationToken = default); } diff --git a/PostFundManagement.Application/Abstractions/Services/IGovernanceService.cs b/PostFundManagement.Application/Abstractions/Services/IGovernanceService.cs index 4ea0399..98b9624 100644 --- a/PostFundManagement.Application/Abstractions/Services/IGovernanceService.cs +++ b/PostFundManagement.Application/Abstractions/Services/IGovernanceService.cs @@ -46,4 +46,36 @@ public interface IGovernanceService [Log] Task> GetPortfolioAsync(string portfolioIdHash, CancellationToken cancellationToken = default); + + [Log] + [Audit] + Task CreateTenantAsync(CreateTenantRequest request, Guid userId, CancellationToken cancellationToken = default); + + [Log] + Task> GetTenantsAsync(CancellationToken cancellationToken = default); + + [Log] + Task> GetTenantAsync(string tenantIdHash, CancellationToken cancellationToken = default); + + [Log] + [Audit] + Task UpdateTenantAsync(UpdateTenantRequest request, Guid userId, CancellationToken cancellationToken = default); + + [Log] + [Audit] + Task UpdateTenantUserAsync(UpdateTenantUserRequest request, Guid userId, CancellationToken cancellationToken = default); + + [Log] + [Audit] + Task CreateTenantOrganisationAsync(string organisationIdHash, string tenantIdHash, Guid userId, CancellationToken cancellationToken = default); + + [Log] + [Audit] + Task UpdateTenantOrganisationAsync(string tenantOrganisationIdHash, ActivityStatus status, Guid userId, CancellationToken cancellationToken = default); + + [Log] + Task> GetTenantOrganisationsAsync(CancellationToken cancellationToken = default); + + [Log] + Task> GetTenantOrganisationAsync(string tenantOrganisationIdHash, CancellationToken cancellationToken = default); } diff --git a/PostFundManagement.Application/Abstractions/Services/IProjectService.cs b/PostFundManagement.Application/Abstractions/Services/IProjectService.cs index 3e7846d..4f6e73e 100644 --- a/PostFundManagement.Application/Abstractions/Services/IProjectService.cs +++ b/PostFundManagement.Application/Abstractions/Services/IProjectService.cs @@ -70,4 +70,88 @@ public interface IProjectService [Log] Task> GetProgrammeProjectsAsync(string programmeIdHash, CancellationToken cancellationToken = default); + + [Log] + [Audit] + Task CreateChangeRequestAsync(CreateChangeRequest request, Guid userId, CancellationToken cancellationToken = default); + + [Log] + [Audit] + Task UpdateChangeRequestAsync(UpdatehangeRequest request, Guid userId, CancellationToken cancellationToken = default); + + [Log] + Task> GetChangeRequestsAsync(string projectIdHash, CancellationToken cancellationToken = default); + + [Log] + Task> GetChangeRequestAsync(string changeRequestIdHash, CancellationToken cancellationToken = default); + + [Log] + [Audit] + Task CreateComplianceItemAsync(CreateComplianceItemRequest request, Guid userId, CancellationToken cancellationToken = default); + + [Log] + [Audit] + Task UpdateComplianceItemAsync(UpdateComplianceItemRequest request, Guid userId, CancellationToken cancellationToken = default); + + [Log] + Task> GetComplianceItemsAsync(string projectIdHash, CancellationToken cancellationToken = default); + + [Log] + Task> GetComplianceItemAsync(string complianceItemIdHash, CancellationToken cancellationToken = default); + + [Log] + [Audit] + Task CreateBeneficiaryAsync(CreateBeneficiaryRequest request, Guid userId, CancellationToken cancellationToken = default); + + [Log] + [Audit] + Task UpdateBeneficiaryItemAsync(UpdateBeneficiaryRequest request, Guid userId, CancellationToken cancellationToken = default); + + [Log] + Task> GetBeneficiariesAsync(string projectIdHash, CancellationToken cancellationToken = default); + + [Log] + Task> GetBeneficiaryAsync(string beneficiaryIdHash, CancellationToken cancellationToken = default); + + [Log] + [Audit] + Task CreateIssueAsync(CreateIssueRequest request, Guid userId, CancellationToken cancellationToken = default); + + [Log] + [Audit] + Task UpdateIssueAsync(UpdateIssueRequest request, Guid userId, CancellationToken cancellationToken = default); + + [Log] + Task> GetIssuesAsync(string projectIdHash, CancellationToken cancellationToken = default); + + [Log] + Task> GetIssueAsync(string issueIdHash, CancellationToken cancellationToken = default); + + [Log] + [Audit] + Task CreateOutcomeAsync(CreateOutcomeRequest request, Guid userId, CancellationToken cancellationToken = default); + + [Log] + [Audit] + Task UpdateOutcomeAsync(UpdateOutcomeRequest request, Guid userId, CancellationToken cancellationToken = default); + + [Log] + Task> GetOutcomesAsync(string beneficiaryIdHash, CancellationToken cancellationToken = default); + + [Log] + Task> GetOutcomeAsync(string outcomeIdHash, CancellationToken cancellationToken = default); + + [Log] + [Audit] + Task CreateSiteVisitAsync(CreateSiteVisitRequest request, Guid userId, CancellationToken cancellationToken = default); + + [Log] + [Audit] + Task UpdateSiteVisitAsync(UpdateSiteVisitRequest request, Guid userId, CancellationToken cancellationToken = default); + + [Log] + Task> GetSiteVisitsAsync(string projectIdHash, CancellationToken cancellationToken = default); + + [Log] + Task> GetSiteVisitAsync(string SiteVisitIdHash, CancellationToken cancellationToken = default); } diff --git a/PostFundManagement.Application/Services/Communications/CommunicationsCacheService.cs b/PostFundManagement.Application/Services/Communications/CommunicationsCacheService.cs index a3a4a60..a12bbb1 100644 --- a/PostFundManagement.Application/Services/Communications/CommunicationsCacheService.cs +++ b/PostFundManagement.Application/Services/Communications/CommunicationsCacheService.cs @@ -11,6 +11,9 @@ namespace PostFundManagement.Application.Services.Communications; public sealed class CommunicationsCacheService(CommunicationsService service, IDistributedCache cache, IFeatureManager featureManager) : ServiceCacheBase, ICommunicationsService { + public async Task CreateNotificationAsync(CreateNotificationRequest request, Guid userId, CancellationToken cancellationToken = default) => + await service.CreateNotificationAsync(request, userId, cancellationToken); + public async Task> GetApplicantInviteAsync(Guid applicantUserId, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { @@ -80,7 +83,58 @@ public sealed class CommunicationsCacheService(CommunicationsService service, ID return Result.Ok(cached.FromJson()); })); - + + public async Task> GetNotificationAsync(string notificationIdHash, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => + { + if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) + return await service.GetNotificationAsync(notificationIdHash, cancellationToken); + + var cacheKey = HashService.ToMd5Hash($"getnotification-{notificationIdHash}").Value; + + var cached = await cache.GetStringAsync(cacheKey, cancellationToken); + + if (string.IsNullOrWhiteSpace(cached)) + { + var result = await service.GetNotificationAsync(notificationIdHash, cancellationToken); + + if (result.IsFailed) return result; + + await cache.SetStringAsync(cacheKey, result.Value.ToJson(), cacheOptions, cancellationToken); + + return Result.Ok(result.Value); + } + + return Result.Ok(cached.FromJson()); + })); + + public async Task> GetNotificationsAsync(string organisationIdHash, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => + { + if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) + return await service.GetNotificationsAsync(organisationIdHash, cancellationToken); + + var cacheKey = HashService.ToMd5Hash($"getnotifications-{organisationIdHash}").Value; + + var cached = await cache.GetStringAsync(cacheKey, cancellationToken); + + if (string.IsNullOrWhiteSpace(cached)) + { + var result = await service.GetNotificationsAsync(organisationIdHash, cancellationToken); + + if (result.IsFailed) return result; + + await cache.SetStringAsync(cacheKey, result.Value.ToJson(), cacheOptions, cancellationToken); + + return Result.Ok(result.Value); + } + + return Result.Ok(cached.FromJson()); + })); + public async Task> SendInviteAsync(InviteRequest request, Guid userId, CancellationToken cancellationToken = default) => await service.SendInviteAsync(request, userId, cancellationToken); + + public async Task UpdateNotificationAsync(UpdateNotificationRequest request, Guid userId, CancellationToken cancellationToken = default) => + await service.UpdateNotificationAsync(request, userId, cancellationToken); } diff --git a/PostFundManagement.Application/Services/Communications/CommunicationsService.cs b/PostFundManagement.Application/Services/Communications/CommunicationsService.cs index a446400..824d5ff 100644 --- a/PostFundManagement.Application/Services/Communications/CommunicationsService.cs +++ b/PostFundManagement.Application/Services/Communications/CommunicationsService.cs @@ -81,4 +81,24 @@ public sealed class CommunicationsService(IDbContextFactory i.Map()).ToArray()); })); + + public Task CreateNotificationAsync(CreateNotificationRequest request, Guid userId, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task UpdateNotificationAsync(UpdateNotificationRequest request, Guid userId, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task> GetNotificationsAsync(string organisationIdHash, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task> GetNotificationAsync(string notificationIdHash, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } } diff --git a/PostFundManagement.Application/Services/Communications/Records.cs b/PostFundManagement.Application/Services/Communications/Records.cs index 3d7ca5d..b315be1 100644 --- a/PostFundManagement.Application/Services/Communications/Records.cs +++ b/PostFundManagement.Application/Services/Communications/Records.cs @@ -3,6 +3,18 @@ using static PostFundManagement.Domain.Extensions.Constants; namespace PostFundManagement.Application.Services.Communications; +public sealed record UpdateNotificationRequest([Required] long NotificationId, + [Required] NotificationStatus Status, + [Required] DateTime SentAt, + [Required][MaxLength(TextLength)] string Error); + +public sealed record CreateNotificationRequest([Required] long OrganisationId, + [Required] Guid Recipient, + [Required] NotificationPlatform Platform, + [Required][MaxLength(LabelLength)] string? Subject, + [Required][MaxLength(LabelLength)] string? Destination, + [Required][MaxLength(TextLength)] string Message); + public sealed record InviteRequest([Required] long OrganisationId, [Required] long InvitedOrganisationId, [Required] long AwardId, diff --git a/PostFundManagement.Application/Services/Governance/GovernanceCacheService.cs b/PostFundManagement.Application/Services/Governance/GovernanceCacheService.cs index cc47ae3..9f630d8 100644 --- a/PostFundManagement.Application/Services/Governance/GovernanceCacheService.cs +++ b/PostFundManagement.Application/Services/Governance/GovernanceCacheService.cs @@ -17,6 +17,12 @@ public sealed class GovernanceCacheService(GovernanceService service, IDistribut public async Task CreatePortfolioAsync(CreatePortfolioRequest request, Guid userId, CancellationToken cancellationToken = default) => await service.CreatePortfolioAsync(request, userId, cancellationToken); + public async Task CreateTenantAsync(CreateTenantRequest request, Guid userId, CancellationToken cancellationToken = default) => + await service.CreateTenantAsync(request, userId, cancellationToken); + + public async Task CreateTenantOrganisationAsync(string organisationIdHash, string tenantIdHash, Guid userId, CancellationToken cancellationToken = default) => + await service.CreateTenantOrganisationAsync(organisationIdHash, tenantIdHash, userId, cancellationToken); + public async Task> GetDashboardAsync(CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { @@ -209,9 +215,38 @@ public sealed class GovernanceCacheService(GovernanceService service, IDistribut return Result.Ok(cached.FromJson()); })); + public Task> GetTenantAsync(string tenantIdHash, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task> GetTenantOrganisationAsync(string tenantOrganisationIdHash, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task> GetTenantOrganisationsAsync(CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task> GetTenantsAsync(CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + public async Task> UpdateOrganisationAsync(UpdateOrganisationRequest request, CancellationToken cancellationToken = default) => await service.UpdateOrganisationAsync(request, cancellationToken); public async Task UpdatePortfolioAsync(UpdatePortfolioRequest request, Guid userId, CancellationToken cancellationToken = default) => await service.UpdatePortfolioAsync(request, userId, cancellationToken); + + public async Task UpdateTenantAsync(UpdateTenantRequest request, Guid userId, CancellationToken cancellationToken = default) => + await service.UpdateTenantAsync(request, userId, cancellationToken); + + public async Task UpdateTenantOrganisationAsync(string tenantOrganisationIdHash, ActivityStatus status, Guid userId, CancellationToken cancellationToken = default) => + await service.UpdateTenantOrganisationAsync(tenantOrganisationIdHash, status, userId, cancellationToken); + + public async Task UpdateTenantUserAsync(UpdateTenantUserRequest request, Guid userId, CancellationToken cancellationToken = default) => + await service.UpdateTenantUserAsync(request, userId, cancellationToken); } diff --git a/PostFundManagement.Application/Services/Governance/GovernanceService.cs b/PostFundManagement.Application/Services/Governance/GovernanceService.cs index 5bff270..e118760 100644 --- a/PostFundManagement.Application/Services/Governance/GovernanceService.cs +++ b/PostFundManagement.Application/Services/Governance/GovernanceService.cs @@ -252,4 +252,49 @@ public sealed class GovernanceService(IDbContextFactory co return Result.FailIf(portfolio is null, NotFoundError.New) .ToResult(portfolio!.Map()); })); + + public Task CreateTenantAsync(CreateTenantRequest request, Guid userId, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task> GetTenantsAsync(CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task> GetTenantAsync(string tenantIdHash, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task UpdateTenantAsync(UpdateTenantRequest request, Guid userId, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task UpdateTenantUserAsync(UpdateTenantUserRequest request, Guid userId, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task CreateTenantOrganisationAsync(string organisationIdHash, string tenantIdHash, Guid userId, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task UpdateTenantOrganisationAsync(string tenantOrganisationIdHash, ActivityStatus status, Guid userId, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task> GetTenantOrganisationsAsync(CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task> GetTenantOrganisationAsync(string tenantOrganisationIdHash, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } } diff --git a/PostFundManagement.Application/Services/Governance/Records.cs b/PostFundManagement.Application/Services/Governance/Records.cs index 5a70f0b..949e824 100644 --- a/PostFundManagement.Application/Services/Governance/Records.cs +++ b/PostFundManagement.Application/Services/Governance/Records.cs @@ -3,25 +3,35 @@ using static PostFundManagement.Domain.Extensions.Constants; namespace PostFundManagement.Application.Services.Governance; +public sealed record UpdateTenantUserRequest([Required] Guid UserId, [Required] ActivityStatus Status); + +public sealed record CreateTenantUserRequest([Required] long TenantId, [Required] Guid UserId); + +public sealed record UpdateTenantRequest([Required] long TenantId, + [Required][MaxLength(LabelLength)] string Name, + [Required] ActivityStatus Status); + +public sealed record CreateTenantRequest([Required] long TenantId, [Required][MaxLength(LabelLength)] string Name); + public sealed record UpdatePortfolioRequest([Required] long PortfolioId, [Required][MaxLength(LabelLength)] string Name, - [Required][MaxLength(TextLength)] string Description, + [Required][MaxLength(TextLength)] string Description, [Required] Guid OwnerId); public sealed record CreatePortfolioRequest([Required][MaxLength(LabelLength)] string Name, - [Required][MaxLength(TextLength)] string Description, + [Required][MaxLength(TextLength)] string Description, [Required] long OrganisationId, [Required] Guid OwnerId); public sealed record UpdateOrganisationRequest([Required] long OrganisationId, [Required][MaxLength(LabelLength)] string Name, [Required][MaxLength(LabelLength)] string RegistrationNo, - [Required][EmailAddress][MaxLength(LabelLength)] string Email, + [Required][EmailAddress][MaxLength(LabelLength)] string Email, [Required] OrganisationType Type); -public sealed record CreateOrganisationRequest([Required][MaxLength(LabelLength)] string Name, - [Required][MaxLength(LabelLength)] string RegistrationNo, - [Required][EmailAddress][MaxLength(LabelLength)] string Email, +public sealed record CreateOrganisationRequest([Required][MaxLength(LabelLength)] string Name, + [Required][MaxLength(LabelLength)] string RegistrationNo, + [Required][EmailAddress][MaxLength(LabelLength)] string Email, [Required] OrganisationType Type = OrganisationType.PrivateLimitedCompany); public sealed record Dashboard([Required] decimal PortfolioValue, diff --git a/PostFundManagement.Application/Services/Projects/ProjectCacheService.cs b/PostFundManagement.Application/Services/Projects/ProjectCacheService.cs index 85e05b9..4c952e3 100644 --- a/PostFundManagement.Application/Services/Projects/ProjectCacheService.cs +++ b/PostFundManagement.Application/Services/Projects/ProjectCacheService.cs @@ -14,12 +14,30 @@ public sealed class ProjectCacheService(ProjectService service, IDistributedCach public async Task CreateActivityAsync(CreateActivityRequest request, Guid userId, CancellationToken cancellationToken = default) => await service.CreateActivityAsync(request, userId, cancellationToken); + public async Task CreateBeneficiaryAsync(CreateBeneficiaryRequest request, Guid userId, CancellationToken cancellationToken = default) => + await service.CreateBeneficiaryAsync(request, userId, cancellationToken); + + public async Task CreateChangeRequestAsync(CreateChangeRequest request, Guid userId, CancellationToken cancellationToken = default) => + await service.CreateChangeRequestAsync(request, userId, cancellationToken); + + public async Task CreateComplianceItemAsync(CreateComplianceItemRequest request, Guid userId, CancellationToken cancellationToken = default) => + await service.CreateComplianceItemAsync(request, userId, cancellationToken); + + public async Task CreateIssueAsync(CreateIssueRequest request, Guid userId, CancellationToken cancellationToken = default) => + await service.CreateIssueAsync(request, userId, cancellationToken); + + public async Task CreateOutcomeAsync(CreateOutcomeRequest request, Guid userId, CancellationToken cancellationToken = default) => + await service.CreateOutcomeAsync(request, userId, cancellationToken); + public async Task CreateProgrammeAsync(CreateProgrammeRequest request, Guid userId, CancellationToken cancellationToken = default) => await service.CreateProgrammeAsync(request, userId, cancellationToken); public async Task CreateProjectAsync(CreateProjectRequest request, Guid userId, CancellationToken cancellationToken = default) => await service.CreateProjectAsync(request, userId, cancellationToken); + public async Task CreateSiteVisitAsync(CreateSiteVisitRequest request, Guid userId, CancellationToken cancellationToken = default) => + await service.CreateSiteVisitAsync(request, userId, cancellationToken); + public async Task> GetActivitiesAsync(string projectIdHash, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { @@ -68,6 +86,46 @@ public sealed class ProjectCacheService(ProjectService service, IDistributedCach return Result.Ok(cached.FromJson()); })); + public Task> GetBeneficiariesAsync(string projectIdHash, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task> GetBeneficiaryAsync(string beneficiaryIdHash, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task> GetChangeRequestAsync(string changeRequestIdHash, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task> GetChangeRequestsAsync(string projectIdHash, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task> GetComplianceItemAsync(string complianceItemIdHash, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task> GetComplianceItemsAsync(string projectIdHash, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task> GetIssueAsync(string issueIdHash, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task> GetIssuesAsync(string projectIdHash, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + public async Task> GetMilestoneAsync(string milestoneIdHash, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { @@ -116,6 +174,16 @@ public sealed class ProjectCacheService(ProjectService service, IDistributedCach return Result.Ok(cached.FromJson()); })); + public Task> GetOutcomeAsync(string outcomeIdHash, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task> GetOutcomesAsync(string beneficiaryIdHash, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + public async Task> GetProgrammeAsync(string programmeIdHash, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { @@ -308,9 +376,34 @@ public sealed class ProjectCacheService(ProjectService service, IDistributedCach return Result.Ok(cached.FromJson()); })); + public Task> GetSiteVisitAsync(string SiteVisitIdHash, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task> GetSiteVisitsAsync(string projectIdHash, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public async Task UpdateBeneficiaryItemAsync(UpdateBeneficiaryRequest request, Guid userId, CancellationToken cancellationToken = default) => + await service.UpdateBeneficiaryItemAsync(request, userId, cancellationToken); + + public async Task UpdateChangeRequestAsync(UpdatehangeRequest request, Guid userId, CancellationToken cancellationToken = default) => + await service.UpdateChangeRequestAsync(request, userId, cancellationToken); + + public async Task UpdateComplianceItemAsync(UpdateComplianceItemRequest request, Guid userId, CancellationToken cancellationToken = default) => + await service.UpdateComplianceItemAsync(request, userId, cancellationToken); + + public async Task UpdateIssueAsync(UpdateIssueRequest request, Guid userId, CancellationToken cancellationToken = default) => + await service.UpdateIssueAsync(request, userId, cancellationToken); + public async Task UpdateMilestoneAsync(UpdateMilestoneRequest request, Guid userId, CancellationToken cancellationToken = default) => await service.UpdateMilestoneAsync(request, userId, cancellationToken); + public async Task UpdateOutcomeAsync(UpdateOutcomeRequest request, Guid userId, CancellationToken cancellationToken = default) => + await service.UpdateOutcomeAsync(request, userId, cancellationToken); + public async Task UpdateProgrammeAsync(UpdateProgrammeRequest request, Guid userId, CancellationToken cancellationToken = default) => await service.UpdateProgrammeAsync(request, userId, cancellationToken); @@ -319,4 +412,7 @@ public sealed class ProjectCacheService(ProjectService service, IDistributedCach public async Task UpdateProjectStatusAsync(UpdateProjectStatusRequest request, Guid userId, CancellationToken cancellationToken = default) => await service.UpdateProjectStatusAsync(request, userId, cancellationToken); + + public async Task UpdateSiteVisitAsync(UpdateSiteVisitRequest request, Guid userId, CancellationToken cancellationToken = default) => + await service.UpdateSiteVisitAsync(request, userId, cancellationToken); } diff --git a/PostFundManagement.Application/Services/Projects/ProjectService.cs b/PostFundManagement.Application/Services/Projects/ProjectService.cs index 22cc715..f7fef6d 100644 --- a/PostFundManagement.Application/Services/Projects/ProjectService.cs +++ b/PostFundManagement.Application/Services/Projects/ProjectService.cs @@ -382,4 +382,124 @@ public sealed class ProjectService(IDbContextFactory conte return Result.Ok(projects.Select(i => i.Map()).ToArray()); })); + + public Task CreateChangeRequestAsync(CreateChangeRequest request, Guid userId, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task UpdateChangeRequestAsync(UpdatehangeRequest request, Guid userId, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task> GetChangeRequestsAsync(string projectIdHash, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task> GetChangeRequestAsync(string changeRequestIdHash, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task CreateComplianceItemAsync(CreateComplianceItemRequest request, Guid userId, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task UpdateComplianceItemAsync(UpdateComplianceItemRequest request, Guid userId, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task> GetComplianceItemsAsync(string projectIdHash, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task> GetComplianceItemAsync(string complianceItemIdHash, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task CreateBeneficiaryAsync(CreateBeneficiaryRequest request, Guid userId, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task UpdateBeneficiaryItemAsync(UpdateBeneficiaryRequest request, Guid userId, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task> GetBeneficiariesAsync(string projectIdHash, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task> GetBeneficiaryAsync(string beneficiaryIdHash, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task CreateIssueAsync(CreateIssueRequest request, Guid userId, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task UpdateIssueAsync(UpdateIssueRequest request, Guid userId, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task> GetIssuesAsync(string projectIdHash, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task> GetIssueAsync(string issueIdHash, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task CreateOutcomeAsync(CreateOutcomeRequest request, Guid userId, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task UpdateOutcomeAsync(UpdateOutcomeRequest request, Guid userId, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task> GetOutcomesAsync(string beneficiaryIdHash, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task> GetOutcomeAsync(string outcomeIdHash, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task CreateSiteVisitAsync(CreateSiteVisitRequest request, Guid userId, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task UpdateSiteVisitAsync(UpdateSiteVisitRequest request, Guid userId, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task> GetSiteVisitsAsync(string projectIdHash, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task> GetSiteVisitAsync(string SiteVisitIdHash, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } } diff --git a/PostFundManagement.Application/Services/Projects/Records.cs b/PostFundManagement.Application/Services/Projects/Records.cs index 28562a2..5934724 100644 --- a/PostFundManagement.Application/Services/Projects/Records.cs +++ b/PostFundManagement.Application/Services/Projects/Records.cs @@ -3,6 +3,75 @@ using static PostFundManagement.Domain.Extensions.Constants; namespace PostFundManagement.Application.Services.Projects; +public sealed record UpdateSiteVisitRequest([Required] long SiteVisitId, + [Required][MaxLength(LabelLength)] string InspectorName, + [Required][MaxLength(TextLength)] string Findings, + [Required] ApprovalStatus Status, + [Required] DateTime VisitedAt); + +public sealed record CreateSiteVisitRequest([Required] long ProjectId, + [Required][MaxLength(LabelLength)] string InspectorName, + [Required][MaxLength(TextLength)] string Findings, + [Required] DateTime VisitedAt); + +public sealed record UpdateOutcomeRequest([Required] long OutcomeId, + [Required][MaxLength(LabelLength)] string Name, + [Required][MaxLength(TextLength)] string Description); + +public sealed record CreateOutcomeRequest([Required] long BeneficiaryId, + [Required][MaxLength(LabelLength)] string Name, + [Required][MaxLength(TextLength)] string Description); + +public sealed record UpdateIssueRequest([Required] long IssueId, + [Required][MaxLength(LabelLength)] string Title, + [Required][MaxLength(TextLength)] string Description, + [Required][MaxLength(TextLength)] string Resolution, + [Required] DateTime? ResolvedAt, + [Required] Priority Priority, + [Required] ActivityStatus Status, + [Required] Guid AssignedTo); + +public sealed record CreateIssueRequest([Required] long ProjectId, + [Required][MaxLength(LabelLength)] string Title, + [Required][MaxLength(TextLength)] string Description, + [Required] Priority Priority, + [Required] Guid AssignedTo); + +public sealed record UpdateBeneficiaryRequest([Required] long BeneficiaryId, + [Required][MaxLength(LabelLength)] string Name, + [Required][MaxLength(LabelLength)] string Category, + [Required][MaxLength(LabelLength)] string Location, + [Required] int TargetCount, + [Required] int ReachedCount); + +public sealed record CreateBeneficiaryRequest([Required] long ProjectId, + [Required][MaxLength(LabelLength)] string Name, + [Required][MaxLength(LabelLength)] string Category, + [Required][MaxLength(LabelLength)] string Location, + [Required] int TargetCount, + [Required] int ReachedCount); + +public sealed record UpdateComplianceItemRequest([Required] long ComplianceItemId, + [Required][MaxLength(LabelLength)] string Title, + [Required][MaxLength(TextLength)] string Requirements, + [Required] ApprovalStatus Status); + +public sealed record CreateComplianceItemRequest([Required] long ProjectId, + [Required][MaxLength(LabelLength)] string Title, + [Required][MaxLength(TextLength)] string Requirements); + +public sealed record UpdatehangeRequest([Required] long ChangeRequestId, + [Required][MaxLength(LabelLength)] string Title, + [Required][MaxLength(TextLength)] string Justification, + [Required] decimal RevisedBudget, + [Required] ApprovalStatus Status); + +public sealed record CreateChangeRequest([Required] long ProjectId, + [Required][MaxLength(LabelLength)] string Title, + [Required][MaxLength(TextLength)] string Justification, + [Required] decimal RevisedBudget, + [Required] DateTime RevisionDate); + public sealed record UpdateProgrammeRequest([Required] long ProgrammeId, [Required][MaxLength(LabelLength)] string Name, [Required] Guid OwnerId, diff --git a/PostFundManagement.Domain/Models/AuditChanges.cs b/PostFundManagement.Domain/Models/AuditChanges.cs deleted file mode 100644 index f6acc5f..0000000 --- a/PostFundManagement.Domain/Models/AuditChanges.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace PostFundManagement.Domain.Models; - -public sealed class AuditChanges -{ - public string[]? Logs { get; set; } -} From f39eb9d8fe1cd1cc2ee76e1448c397f091ba650b Mon Sep 17 00:00:00 2001 From: Hermes Agent Date: Thu, 24 Sep 2026 16:00:06 +0200 Subject: [PATCH 2/2] Removed tools feature --- .../Endpoints/Tools/EncodeIdEndpoint.cs | 36 ------------------- .../Extensions/Services.cs | 2 -- .../Services/Tools/ToolCacheService.cs | 34 ------------------ .../Services/Tools/ToolService.cs | 10 ------ 4 files changed, 82 deletions(-) delete mode 100644 PostFundManagement.Api/Endpoints/Tools/EncodeIdEndpoint.cs delete mode 100644 PostFundManagement.Application/Services/Tools/ToolCacheService.cs delete mode 100644 PostFundManagement.Application/Services/Tools/ToolService.cs diff --git a/PostFundManagement.Api/Endpoints/Tools/EncodeIdEndpoint.cs b/PostFundManagement.Api/Endpoints/Tools/EncodeIdEndpoint.cs deleted file mode 100644 index 5a80c05..0000000 --- a/PostFundManagement.Api/Endpoints/Tools/EncodeIdEndpoint.cs +++ /dev/null @@ -1,36 +0,0 @@ -using PostFundManagement.Application.Abstractions.Services; -using PostFundManagement.Application.Extensions; -using PostFundManagement.Domain.Abstractions; -using PostFundManagement.Domain.Errors; -using PostFundManagement.Domain.Extensions; -using PostFundManagement.Domain.Security; -using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails; - -namespace PostFundManagement.Api.Endpoints.Tools; - -[ApiVersionTarget(1)] -public sealed class EncodeIdEndpoint : IEndpoint -{ - public void Map(IEndpointRouteBuilder builder) - { - builder.MapGet("api/tools/hashid/{id64:long}", async (long id64, IToolService service, - CancellationToken cancellationToken) => - await service.EncodeLong(id64, cancellationToken) switch - { - { IsSuccess: true, Value: var hash } => Results.Ok(hash), - var r when r.HasError() => Results.BadRequest(r.Errors.ToProblems()), - var r => Results.InternalServerError(r.Errors.ToProblems()) - }) - .WithDescription("Hashes an int64 identifier") - .RequireAuthorization(Constants.MainScope) - .WithName(typeof(EncodeIdEndpoint).ToEndpointName()) - .MapToApiVersion(new ApiVersion(1)) - .Produces(StatusCodes.Status200OK) - .Produces(StatusCodes.Status401Unauthorized) - .Produces(StatusCodes.Status403Forbidden) - .Produces(StatusCodes.Status400BadRequest) - .Produces(StatusCodes.Status500InternalServerError) - .WithTags(EndpointTags.Tools) - .CacheOutput(); - } -} diff --git a/PostFundManagement.Application/Extensions/Services.cs b/PostFundManagement.Application/Extensions/Services.cs index 7005798..300f634 100644 --- a/PostFundManagement.Application/Extensions/Services.cs +++ b/PostFundManagement.Application/Extensions/Services.cs @@ -8,7 +8,6 @@ using PostFundManagement.Application.Services.Governance; using PostFundManagement.Application.Services.Grants; using PostFundManagement.Application.Services.Projects; using PostFundManagement.Application.Services.Shared; -using PostFundManagement.Application.Services.Tools; using PostFundManagement.Application.Services.Users; using PostFundManagement.Domain.Abstractions; @@ -81,7 +80,6 @@ public static class Services services.AddLoggedAuditedCachedServiceScoped(); services.AddLoggedCachedServiceScoped(); - services.AddLoggedCachedServiceScoped(); services.AddStackExchangeRedisCache(options => { diff --git a/PostFundManagement.Application/Services/Tools/ToolCacheService.cs b/PostFundManagement.Application/Services/Tools/ToolCacheService.cs deleted file mode 100644 index 22a3b3f..0000000 --- a/PostFundManagement.Application/Services/Tools/ToolCacheService.cs +++ /dev/null @@ -1,34 +0,0 @@ -using PostFundManagement.Application.Abstractions; -using PostFundManagement.Application.Abstractions.Services; -using PostFundManagement.Application.Services.Shared; -using static PostFundManagement.Domain.Extensions.Constants; - -namespace PostFundManagement.Application.Services.Tools; - -public sealed class ToolCacheService(ToolService service, IDistributedCache cache, - IFeatureManager featureManager) : ServiceCacheBase, IToolService -{ - public async Task> EncodeLong(long id, CancellationToken cancellationToken) => - await Result.Try((Func>>)(async () => - { - if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) - return await service.EncodeLong(id, cancellationToken); - - var cacheKey = HashService.ToMd5Hash($"encodelonghash-{id}").Value; - - var cached = await cache.GetStringAsync(cacheKey, cancellationToken); - - if(string.IsNullOrWhiteSpace(cached)) - { - var result = (await service.EncodeLong(id).ConfigureAwait(false)); - - if (result.IsFailed) return result; - - await cache.SetStringAsync(cacheKey, result.Value, cacheOptions, cancellationToken); - - return result.Value; - } - - return Result.Ok(cached); - })); -} diff --git a/PostFundManagement.Application/Services/Tools/ToolService.cs b/PostFundManagement.Application/Services/Tools/ToolService.cs deleted file mode 100644 index b6a494f..0000000 --- a/PostFundManagement.Application/Services/Tools/ToolService.cs +++ /dev/null @@ -1,10 +0,0 @@ -using PostFundManagement.Application.Abstractions.Services; -using PostFundManagement.Application.Services.Shared; - -namespace PostFundManagement.Application.Services.Tools; - -public sealed class ToolService(HashService service) : IToolService -{ - public Task> EncodeLong(long id, CancellationToken cancellationToken = default) => - Task.FromResult(service.HashEncodeLongId(id)); -}